changeset 2758:652651eeb5f1

Automated merge with ssh://hg.atheme-project.org//hg//audacious-plugins
author Calin Crisan ccrisan@gmail.com
date Tue, 01 Jul 2008 21:13:48 +0300
parents 4ec0e13208de (current diff) d3d71539d675 (diff)
children 41a80a649b9d
files
diffstat 54 files changed, 20092 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Tue Jul 01 21:13:22 2008 +0300
+++ b/configure.ac	Tue Jul 01 21:13:48 2008 +0300
@@ -120,7 +120,7 @@
 
 dnl These plugins are always built.
 
-INPUT_PLUGINS="tonegen console sexypsf cue alac metronom vtx"
+INPUT_PLUGINS="tonegen console sexypsf psf2 cue alac metronom vtx"
 OUTPUT_PLUGINS="null"
 EFFECT_PLUGINS="audiocompress crystalizer ladspa voice_removal sndstretch stereo_plugin echo_plugin"
 GENERAL_PLUGINS="song_change alarm"
@@ -1689,6 +1689,7 @@
 echo "    -> distortion patched libSIDPlay2:    $xs_have_distortion"
 echo "  Game music (spc, nsf & gbs):            yes"
 echo "  PlayStation audio (sexypsf):            yes"
+echo "  PlayStation2 audio (psf2):              yes"
 echo "  AdLib synthesizer (adplug):             $have_adplug"
 echo "  Apple Lossless Audio Codec (alac):      yes"
 echo "  WavPack 4.31+ (wavpack):                $have_wavpack"
--- a/src/bluetooth/bluetooth.c	Tue Jul 01 21:13:22 2008 +0300
+++ b/src/bluetooth/bluetooth.c	Tue Jul 01 21:13:48 2008 +0300
@@ -49,7 +49,7 @@
 {
     printf("bluetooth: exit\n");
     if (config ==1 ){
-        destroy_scan_window();
+        close_window();
         config =0;
     }
     if(discover_finish == 2) {
@@ -91,14 +91,25 @@
 
 }
 
+void clean_devices_list(){
+    g_list_free(audio_devices);
+    dbus_g_connection_flush (bus);
+    dbus_g_connection_unref(bus);
+    audio_devices = NULL;
+    //g_list_free(current_device);
+}
 
 void refresh_call(void)
 {
     printf("refresh function called\n");
     disconnect_dbus_signals();
+    clean_devices_list();
     if(discover_finish == 0 ||discover_finish== 2){
         discover_finish = 0;
+
         discover_devices();
+        close_window();
+        show_scan();
     }
     else 
         printf("Scanning please wait!\n");
--- a/src/bluetooth/gui.c	Tue Jul 01 21:13:22 2008 +0300
+++ b/src/bluetooth/gui.c	Tue Jul 01 21:13:48 2008 +0300
@@ -173,6 +173,13 @@
     }
 }
 
+void refresh_resultsui(){
+  gtk_widget_destroy (window);
+  window = NULL;
+  refresh_call();
+}
+   
+
 void results_ui()
 {
     gchar *temp;
@@ -200,7 +207,7 @@
         gtk_container_add(GTK_CONTAINER(hbox_top),about_frame);
 
         refresh = gtk_button_new_with_mnemonic ("_Refresh");
-        g_signal_connect (refresh, "clicked",G_CALLBACK (refresh_call), NULL);
+        g_signal_connect (refresh, "clicked",G_CALLBACK (refresh_resultsui), NULL);
         gtk_container_add(GTK_CONTAINER(hbox_bottom),refresh);
 
         connect_button = gtk_button_new_with_mnemonic("_Connect");
--- a/src/bluetooth/scan_gui.c	Tue Jul 01 21:13:22 2008 +0300
+++ b/src/bluetooth/scan_gui.c	Tue Jul 01 21:13:48 2008 +0300
@@ -3,11 +3,13 @@
 static GtkWidget *window = NULL;
 static GtkWidget *winbox;
 static GtkWidget *scanbox;
+static GtkWidget *buttonsbox;
 static GtkWidget *progressbox;
 static GtkWidget *bluetooth_img;
 static GtkWidget *scan_label;
 static GtkWidget *progress_bar;
-
+static GtkWidget *rescan_buttton;
+static GtkWidget *close_button;
 gpointer progress() {
 
     for(;;){
@@ -33,6 +35,12 @@
 void destroy_scan_window(){
     gtk_widget_hide(window);
 }
+void close_window(void){
+    printf("scan_gui close callback \n");
+    gtk_widget_destroy (window);
+    window = NULL;
+}
+
 
 void show_scan()
 {
@@ -69,7 +77,19 @@
         /* I have to add a button for Rescan when there are
          * no devices found and not currently scanning
          */
-        gtk_window_set_default_size (GTK_WINDOW (window), 50, 40);
+        buttonsbox = gtk_hbox_new(FALSE,2);
+        gtk_container_set_border_width(GTK_CONTAINER(buttonsbox),2);
+        gtk_container_add(GTK_CONTAINER(progressbox),buttonsbox);
+        rescan_buttton = gtk_button_new_with_mnemonic("Rescan");
+        g_signal_connect(rescan_buttton,"clicked",G_CALLBACK (refresh_call),NULL);
+
+        close_button = gtk_button_new_with_mnemonic("Close");
+        gtk_container_add(GTK_CONTAINER(buttonsbox),rescan_buttton);
+        gtk_container_add(GTK_CONTAINER(buttonsbox),close_button);
+        g_signal_connect(close_button,"clicked",G_CALLBACK (close_window),NULL);
+
+        gtk_window_set_default_size (GTK_WINDOW (window), 60, 40);
+        gtk_window_set_resizable(GTK_WINDOW(window),FALSE);
         if (!GTK_WIDGET_VISIBLE (window))
             gtk_widget_show_all (window);
         else
--- a/src/bluetooth/scan_gui.h	Tue Jul 01 21:13:22 2008 +0300
+++ b/src/bluetooth/scan_gui.h	Tue Jul 01 21:13:48 2008 +0300
@@ -3,3 +3,4 @@
 void show_scan();
 void show_no_devices();
 void destroy_scan_window();
+void close_window();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/Makefile	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,22 @@
+PLUGIN = psf2${PLUGIN_SUFFIX}
+
+SRCS = corlett.c \
+       plugin.c \
+       psx.c \
+       psx_hw.c \
+       eng_psf.c \
+       eng_psf2.c \
+       eng_spu.c \
+       peops/spu.c \
+       peops2/dma.c \
+       peops2/registers.c \
+       peops2/spu.c \
+
+include ../../buildsys.mk
+include ../../extra.mk
+
+plugindir := ${plugindir}/${INPUT_PLUGIN_DIR}
+
+CFLAGS += ${PLUGIN_CFLAGS} -O0
+CPPFLAGS += ${PLUGIN_CPPFLAGS} ${MOWGLI_CFLAGS} ${GTK_CFLAGS} ${GLIB_CFLAGS}  -I../.. -Ispu/ -I.
+LIBS += ${GTK_LIBS} ${GLIB_LIBS}  ${MOWGLI_LIBS} -lz
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/README	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,21 @@
+psf2 plugin
+-----------
+
+What is this?
+
+It's a PSF2 plugin based on:
+
+ * UPSE
+ * Audio Overload
+ * Highly Experimental
+
+It's under the BSD license, mostly, but it uses code from PeOPS, so it's
+probably GPL.
+
+Does it work?
+
+Yes. But there's bugs. See http://audacious-media-player.org/?title=Audacious2/PSF2Plugin 
+for suggestions on things you can do.
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/ao.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,175 @@
+//
+// Audio Overload SDK
+//
+// Fake ao.h to set up the general Audio Overload style environment
+//
+
+#include "../../config.h"
+
+#ifndef __AO_H
+#define __AO_H
+
+#define AO_SUCCESS					1
+#define AO_FAIL						0
+#define AO_FAIL_DECOMPRESSION		-1
+
+#define MAX_DISP_INFO_LENGTH		256
+#define AUDIO_RATE					(44100)
+
+enum
+{
+	COMMAND_NONE = 0,
+	COMMAND_PREV,
+	COMMAND_NEXT,
+	COMMAND_RESTART,
+	COMMAND_HAS_PREV,
+	COMMAND_HAS_NEXT,
+	COMMAND_GET_MIN,
+	COMMAND_GET_MAX,
+	COMMAND_JUMP
+};
+
+/* Compiler defines for Xcode */
+#ifndef WORDS_BIGENDIAN
+	#define LSB_FIRST	1
+#endif
+
+typedef unsigned char ao_bool;
+
+#ifdef __GNUC__
+#include <stddef.h>	// get NULL
+#include <stdbool.h>
+
+#ifndef nil
+#define nil NULL
+#endif
+
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define xmalloc(a) malloc(a)
+
+#endif
+
+#ifdef _MSC_VER
+#include <stddef.h>	// get NULL
+#include <wchar.h> // for off_t
+
+#ifndef nil
+#define nil NULL
+#endif
+
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define true (1)
+#define false (0)
+
+#define xmalloc(a) malloc(a)
+
+#define strcasecmp _strcmpi
+
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX	2048
+#endif
+
+typedef unsigned char		uint8;
+typedef unsigned char		UINT8;
+typedef signed char			int8;
+typedef signed char			INT8;
+typedef unsigned short		uint16;
+typedef unsigned short		UINT16;
+typedef signed short		int16;
+typedef signed short		INT16;
+typedef signed int			int32;
+typedef unsigned int		uint32;
+#ifdef LONG_IS_64BIT
+typedef signed long             int64;
+typedef unsigned long           uint64;
+#else
+typedef signed long long	int64;
+typedef unsigned long long	uint64;
+#endif
+
+#ifdef WIN32
+#ifndef _BASETSD_H
+typedef signed int			INT32;
+typedef unsigned int		UINT32;
+typedef signed long long	INT64;
+typedef unsigned long long	UINT64;
+#endif
+#else
+typedef signed int			INT32;
+typedef unsigned int		UINT32;
+#ifdef LONG_IS_64BIT
+typedef signed long         INT64;
+typedef unsigned long       UINT64;
+#else
+typedef signed long long	INT64;
+typedef unsigned long long	UINT64;
+#endif
+#endif
+
+#ifndef INLINE
+#if defined(_MSC_VER)
+#define INLINE __forceinline
+#elif defined(__GNUC__)
+#define INLINE __inline__
+#elif defined(_MWERKS_)
+#define INLINE inline
+#elif defined(__powerc)
+#define INLINE inline
+#else
+#define INLINE
+#endif
+#endif
+
+#if LSB_FIRST
+#define LE16(x) (x)
+#define LE32(x) (x)
+
+#ifndef __ENDIAN__ /* Mac OS X Endian header has this function in it */
+static unsigned long INLINE Endian32_Swap(unsigned long addr)
+{
+	unsigned long res = (((addr&0xff000000)>>24) |
+		 ((addr&0x00ff0000)>>8) |
+		 ((addr&0x0000ff00)<<8) |
+		 ((addr&0x000000ff)<<24));
+
+	return res;
+}
+#endif
+
+#else
+
+static unsigned short INLINE LE16(unsigned short x)
+{
+	unsigned short res = (((x & 0xFF00) >> 8) | ((x & 0xFF) << 8));
+	return res;
+}
+
+static unsigned long INLINE LE32(unsigned long addr)
+{
+	unsigned long res = (((addr&0xff000000)>>24) |
+		 ((addr&0x00ff0000)>>8) |
+		 ((addr&0x0000ff00)<<8) |
+		 ((addr&0x000000ff)<<24));
+
+	return res;
+}
+
+#endif
+
+int ao_get_lib(char *filename, uint8 **buffer, uint64 *length);
+
+#endif // AO_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/corlett.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,381 @@
+/*
+
+	Audio Overload SDK
+
+	Copyright (c) 2007-2008, R. Belmont and Richard Bannister.
+
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+	* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// corlett.c
+
+// Decodes file format designed by Neill Corlett (PSF, QSF, ...)
+
+/*
+ - First 3 bytes: ASCII signature: "PSF" (case sensitive)
+
+- Next 1 byte: Version byte
+  The version byte is used to determine the type of PSF file.  It does NOT
+  affect the basic structure of the file in any way.
+
+  Currently accepted version bytes are:
+    0x01: Playstation (PSF1)
+    0x02: Playstation 2 (PSF2)
+    0x11: Saturn (SSF) [TENTATIVE]
+    0x12: Dreamcast (DSF) [TENTATIVE]
+    0x21: Nintendo 64 (USF) [RESERVED]
+    0x41: Capcom QSound (QSF)
+
+- Next 4 bytes: Size of reserved area (R), little-endian unsigned long
+
+- Next 4 bytes: Compressed program length (N), little-endian unsigned long
+  This is the length of the program data _after_ compression.
+
+- Next 4 bytes: Compressed program CRC-32, little-endian unsigned long
+  This is the CRC-32 of the program data _after_ compression.  Filling in
+  this value is mandatory, as a PSF file may be regarded as corrupt if it
+  does not match.
+
+- Next R bytes: Reserved area.
+  May be empty if R is 0 bytes.
+
+- Next N bytes: Compressed program, in zlib compress() format.
+  May be empty if N is 0 bytes.
+
+The following data is optional and may be omitted:
+
+- Next 5 bytes: ASCII signature: "[TAG]" (case sensitive)
+  If these 5 bytes do not match, then the remainder of the file may be
+  regarded as invalid and discarded.
+
+- Remainder of file: Uncompressed ASCII tag data.
+*/
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "ao.h"
+#include "corlett.h"
+
+#include <zlib.h>
+#include <stdlib.h>
+
+#define DECOMP_MAX_SIZE		((32 * 1024 * 1024) + 12)
+
+int corlett_decode(uint8 *input, uint32 input_len, uint8 **output, uint64 *size, corlett_t **c)
+{
+	uint32 *buf;
+	uint32 res_area, comp_crc,  actual_crc;
+	uint8 *decomp_dat, *tag_dec;
+	uLongf decomp_length, comp_length;
+	
+	// 32-bit pointer to data
+	buf = (uint32 *)input;
+	
+	// Check we have a PSF format file.
+	if ((input[0] != 'P') || (input[1] != 'S') || (input[2] != 'F'))
+	{
+		return AO_FAIL;
+	}
+	
+	// Get our values
+	res_area = LE32(buf[1]);
+	comp_length = LE32(buf[2]);
+	comp_crc = LE32(buf[3]);
+		
+	if (comp_length > 0)
+	{
+		// Check length
+		if (input_len < comp_length + 16)
+			return AO_FAIL;
+	
+		// Check CRC is correct
+		actual_crc = crc32(0, (unsigned char *)&buf[4+(res_area/4)], comp_length);
+		if (actual_crc != comp_crc)
+			return AO_FAIL;
+	
+		// Decompress data if any
+		decomp_dat = malloc(DECOMP_MAX_SIZE);
+		decomp_length = DECOMP_MAX_SIZE;
+		if (uncompress(decomp_dat, &decomp_length, (unsigned char *)&buf[4+(res_area/4)], comp_length) != Z_OK)
+		{
+			free(decomp_dat);
+			return AO_FAIL;
+		}
+	   	
+		// Resize memory buffer to what we actually need
+		decomp_dat = realloc(decomp_dat, (size_t)decomp_length + 1);
+	}
+	else
+	{
+		decomp_dat = NULL;
+		decomp_length =  0;
+	}
+
+	// Make structure
+	*c = malloc(sizeof(corlett_t));
+	if (!(*c))
+	{
+		free(decomp_dat);
+		return AO_FAIL;
+	}
+	memset(*c, 0, sizeof(corlett_t));
+	strcpy((*c)->inf_title, "n/a");
+	strcpy((*c)->inf_copy, "n/a");
+	strcpy((*c)->inf_artist, "n/a");
+	strcpy((*c)->inf_game, "n/a");
+	strcpy((*c)->inf_year, "n/a");
+	strcpy((*c)->inf_length, "n/a");
+	strcpy((*c)->inf_fade, "n/a");
+
+	// set reserved section pointer
+	(*c)->res_section = &buf[4];
+	(*c)->res_size = res_area;
+	
+	// Return it
+	if (output != NULL && size != NULL)
+	{
+		*output = decomp_dat;
+		*size = decomp_length;
+	}
+	else
+		free(decomp_dat);
+
+	// Next check for tags
+	input_len -= (comp_length + 16 + res_area);
+	if (input_len < 5)
+		return AO_SUCCESS;
+		
+//	printf("\n\nNew corlett: input len %d\n", input_len);
+	
+	tag_dec = input + (comp_length + res_area + 16);
+	if ((tag_dec[0] == '[') && (tag_dec[1] == 'T') && (tag_dec[2] == 'A') && (tag_dec[3] == 'G') && (tag_dec[4] == ']'))
+	{
+		int tag, l, num_tags, data;
+		
+		// Tags found!
+		tag_dec += 5;
+		input_len -= 5;
+
+		tag = 0;
+		data = false;
+		num_tags = 0;
+		l = 0;
+		while (input_len && (num_tags < MAX_UNKNOWN_TAGS))
+		{
+			if (data)
+			{
+				if ((*tag_dec == 0xA) || (*tag_dec == 0x00))
+				{
+					(*c)->tag_data[num_tags][l] = 0;
+					data = false;
+					num_tags++;
+					l = 0;
+				}
+				else
+				{
+					(*c)->tag_data[num_tags][l++] = *tag_dec;
+				}
+			}
+			else
+			{
+				if (*tag_dec == '=')
+				{
+					(*c)->tag_name[num_tags][l] = 0;
+					l = 0;
+					data = true;
+				}
+				else
+				{
+					(*c)->tag_name[num_tags][l++] = *tag_dec;
+				}
+			}
+			
+			tag_dec++;
+			input_len--;
+		}
+		
+		
+		// Now, process that tag array into what we expect
+		for (num_tags = 0; num_tags < MAX_UNKNOWN_TAGS; num_tags++)
+		{			
+			// See if tag belongs in one of the special fields we have
+			if (!strcasecmp((*c)->tag_name[num_tags], "_lib"))
+			{
+				strcpy((*c)->lib, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib2", 5))
+			{
+				strcpy((*c)->libaux[0], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib3", 5))
+			{
+				strcpy((*c)->libaux[1], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib4", 5))
+			{
+				strcpy((*c)->libaux[2], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib5", 5))
+			{
+				strcpy((*c)->libaux[3], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib6", 5))
+			{
+				strcpy((*c)->libaux[4], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib7", 5))
+			{
+				strcpy((*c)->libaux[5], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib8", 5))
+			{
+				strcpy((*c)->libaux[6], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_lib9", 5))
+			{
+				strcpy((*c)->libaux[7], (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "_refresh", 8))
+			{
+				strcpy((*c)->inf_refresh, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "title", 5))
+			{
+				strcpy((*c)->inf_title, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "copyright", 9))
+			{
+				strcpy((*c)->inf_copy, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "artist", 6))
+			{
+				strcpy((*c)->inf_artist, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "game", 4))
+			{
+				strcpy((*c)->inf_game, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "year", 4))
+			{
+				strcpy((*c)->inf_year, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "length", 6))
+			{
+				strcpy((*c)->inf_length, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+			else if (!strncmp((*c)->tag_name[num_tags], "fade", 4))
+			{
+				strcpy((*c)->inf_fade, (*c)->tag_data[num_tags]);
+				(*c)->tag_data[num_tags][0] = 0;
+				(*c)->tag_name[num_tags][0] = 0;
+			}
+		}
+	}
+	
+	// Bingo
+	return AO_SUCCESS;
+}
+
+uint32 psfTimeToMS(char *str)
+{
+	int x, c=0;
+	uint32 acc=0;
+	char s[100];
+
+	strncpy(s,str,100);
+	s[99]=0;
+
+	for (x=strlen(s); x>=0; x--)
+	{
+		if (s[x]=='.' || s[x]==',')
+		{
+			acc=atoi(s+x+1);
+			s[x]=0;
+		}
+		else if (s[x]==':')
+		{
+			if(c==0) 
+			{
+				acc+=atoi(s+x+1)*10;
+			}
+			else if(c==1) 
+			{
+				acc+=atoi(s+x+(x?1:0))*10*60;
+			}
+
+			c++;
+			s[x]=0;
+		}
+		else if (x==0)
+		{
+			if(c==0)
+			{ 
+				acc+=atoi(s+x)*10;
+			}
+			else if(c==1) 
+			{
+				acc+=atoi(s+x)*10*60;
+			}
+			else if(c==2) 
+			{
+				acc+=atoi(s+x)*10*60*60;
+			}
+		}
+	}
+
+	acc*=100;
+	return(acc);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/corlett.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,35 @@
+//
+// Audio Overload
+// Emulated music player
+//
+// (C) 2000-2008 Richard F. Bannister
+//
+
+// corlett.h
+
+#define MAX_UNKNOWN_TAGS			32
+
+typedef struct {
+	char lib[256];
+	char libaux[8][256];
+	
+	char inf_title[256];
+	char inf_copy[256];
+	char inf_artist[256];
+	char inf_game[256];
+	char inf_year[256];
+	char inf_length[256];
+	char inf_fade[256];
+
+	char inf_refresh[256];
+	
+	char tag_name[MAX_UNKNOWN_TAGS][256];
+	char tag_data[MAX_UNKNOWN_TAGS][256];
+
+	uint32 *res_section;
+	uint32 res_size;
+} corlett_t;
+
+int corlett_decode(uint8 *input, uint32 input_len, uint8 **output, uint64 *size, corlett_t **c);
+uint32 psfTimeToMS(char *str);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/cpuintrf.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,668 @@
+#ifndef CPUINTRF_H
+#define CPUINTRF_H
+
+#include "osd_cpu.h"
+
+/* The old system is obsolete and no longer supported by the core */
+#define NEW_INTERRUPT_SYSTEM    1
+
+#define MAX_IRQ_LINES   8       /* maximum number of IRQ lines per CPU */
+
+#define CLEAR_LINE		0		/* clear (a fired, held or pulsed) line */
+#define ASSERT_LINE     1       /* assert an interrupt immediately */
+#define HOLD_LINE       2       /* hold interrupt line until enable is true */
+#define PULSE_LINE		3		/* pulse interrupt line for one instruction */
+
+#define MAX_REGS		64		/* maximum number of register of any CPU */
+
+#define	IRQ_LINE_NMI	10
+/* Values passed to the cpu_info function of a core to retrieve information */
+enum {
+	CPU_INFO_REG,
+	CPU_INFO_FLAGS=MAX_REGS,
+	CPU_INFO_NAME,
+    CPU_INFO_FAMILY,
+    CPU_INFO_VERSION,
+    CPU_INFO_FILE,
+    CPU_INFO_CREDITS,
+    CPU_INFO_REG_LAYOUT,
+	CPU_INFO_WIN_LAYOUT
+};
+
+#define CPU_IS_LE		0	/* emulated CPU is little endian */
+#define CPU_IS_BE		1	/* emulated CPU is big endian */
+
+/*
+ * This value is passed to cpu_get_reg to retrieve the previous
+ * program counter value, ie. before a CPU emulation started
+ * to fetch opcodes and arguments for the current instrution.
+ */
+#define REG_PREVIOUSPC	-1
+
+/*
+ * This value is passed to cpu_get_reg/cpu_set_reg, instead of one of
+ * the names from the enum a CPU core defines for it's registers,
+ * to get or set the contents of the memory pointed to by a stack pointer.
+ * You can specify the n'th element on the stack by (REG_SP_CONTENTS-n),
+ * ie. lower negative values. The actual element size (UINT16 or UINT32)
+ * depends on the CPU core.
+ * This is also used to replace the cpu_geturnpc() function.
+ */
+#define REG_SP_CONTENTS -2
+
+/*
+ * These flags can be defined in the makefile (or project) to
+ * exclude (zero) or include (non zero) specific CPU cores
+ */
+#ifndef HAS_GENSYNC
+#define HAS_GENSYNC		0
+#endif
+#ifndef HAS_Z80
+#define HAS_Z80 		0
+#endif
+#ifndef HAS_Z80_VM
+#define HAS_Z80_VM		0
+#endif
+#ifndef HAS_8080
+#define HAS_8080		0
+#endif
+#ifndef HAS_8085A
+#define HAS_8085A		0
+#endif
+#ifndef HAS_M6502
+#define HAS_M6502		0
+#endif
+#ifndef HAS_M65C02
+#define HAS_M65C02		0
+#endif
+#ifndef HAS_M65SC02
+#define HAS_M65SC02		0
+#endif
+#ifndef HAS_M65CE02
+#define HAS_M65CE02		0
+#endif
+#ifndef HAS_M6509
+#define HAS_M6509		0
+#endif
+#ifndef HAS_M6510
+#define HAS_M6510		0
+#endif
+#ifndef HAS_N2A03
+#define HAS_N2A03		0
+#endif
+#ifndef HAS_H6280
+#define HAS_H6280		0
+#endif
+#ifndef HAS_I86
+#define HAS_I86 		0
+#endif
+#ifndef HAS_V20
+#define HAS_V20 		0
+#endif
+#ifndef HAS_V30
+#define HAS_V30 		0
+#endif
+#ifndef HAS_V33
+#define HAS_V33 		0
+#endif
+#ifndef HAS_I8035
+#define HAS_I8035		0
+#endif
+#ifndef HAS_I8039
+#define HAS_I8039		0
+#endif
+#ifndef HAS_I8048
+#define HAS_I8048		0
+#endif
+#ifndef HAS_N7751
+#define HAS_N7751		0
+#endif
+#ifndef HAS_M6800
+#define HAS_M6800		0
+#endif
+#ifndef HAS_M6801
+#define HAS_M6801		0
+#endif
+#ifndef HAS_M6802
+#define HAS_M6802		0
+#endif
+#ifndef HAS_M6803
+#define HAS_M6803		0
+#endif
+#ifndef HAS_M6808
+#define HAS_M6808		0
+#endif
+#ifndef HAS_HD63701
+#define HAS_HD63701 	0
+#endif
+#ifndef HAS_M6805
+#define HAS_M6805		0
+#endif
+#ifndef HAS_M68705
+#define HAS_M68705		0
+#endif
+#ifndef HAS_HD63705
+#define HAS_HD63705 	0
+#endif
+#ifndef HAS_HD6309
+#define HAS_HD6309		0
+#endif
+#ifndef HAS_M6809
+#define HAS_M6809		0
+#endif
+#ifndef HAS_KONAMI
+#define HAS_KONAMI		0
+#endif
+#ifndef HAS_M68000
+#define HAS_M68000		0
+#endif
+#ifndef HAS_M68010
+#define HAS_M68010		0
+#endif
+#ifndef HAS_M68020
+#define HAS_M68020		0
+#endif
+#ifndef HAS_T11
+#define HAS_T11 		0
+#endif
+#ifndef HAS_S2650
+#define HAS_S2650		0
+#endif
+#ifndef HAS_TMS34010
+#define HAS_TMS34010	0
+#endif
+#ifndef HAS_TMS9900
+#define HAS_TMS9900 	0
+#endif
+#ifndef HAS_TMS9940
+#define HAS_TMS9940 	0
+#endif
+#ifndef HAS_TMS9980
+#define HAS_TMS9980 	0
+#endif
+#ifndef HAS_TMS9985
+#define HAS_TMS9985 	0
+#endif
+#ifndef HAS_TMS9989
+#define HAS_TMS9989 	0
+#endif
+#ifndef HAS_TMS9995
+#define HAS_TMS9995 	0
+#endif
+#ifndef HAS_TMS99105A
+#define HAS_TMS99105A 	0
+#endif
+#ifndef HAS_TMS99110A
+#define HAS_TMS99110A 	0
+#endif
+#ifndef HAS_Z8000
+#define HAS_Z8000		0
+#endif
+#ifndef HAS_TMS320C10
+#define HAS_TMS320C10	0
+#endif
+#ifndef HAS_CCPU
+#define HAS_CCPU		0
+#endif
+#ifndef HAS_PDP1
+#define HAS_PDP1		0
+#endif
+#ifndef HAS_ADSP2100
+#define HAS_ADSP2100	0
+#endif
+
+/* ASG 971222 -- added this generic structure */
+struct cpu_interface
+{
+    unsigned cpu_num;
+    void (*reset)(void *param);
+    void (*exit)(void);
+    int (*execute)(int cycles);
+    void (*burn)(int cycles);
+    unsigned (*get_context)(void *reg);
+    void (*set_context)(void *reg);
+    unsigned (*get_pc)(void);
+    void (*set_pc)(unsigned val);
+    unsigned (*get_sp)(void);
+    void (*set_sp)(unsigned val);
+    unsigned (*get_reg)(int regnum);
+    void (*set_reg)(int regnum, unsigned val);
+    void (*set_nmi_line)(int linestate);
+    void (*set_irq_line)(int irqline, int linestate);
+    void (*set_irq_callback)(int(*callback)(int irqline));
+    void (*internal_interrupt)(int type);
+    void (*cpu_state_save)(void *file);
+    void (*cpu_state_load)(void *file);
+    const char* (*cpu_info)(void *context,int regnum);
+    unsigned (*cpu_dasm)(char *buffer,unsigned pc);
+	unsigned num_irqs;
+	int default_vector;
+    int *icount;
+    double overclock;
+    int no_int, irq_int, nmi_int;
+    int (*memory_read)(int offset);
+    void (*memory_write)(int offset, int data);
+    void (*set_op_base)(int pc);
+	int address_shift;
+	unsigned address_bits, endianess, align_unit, max_inst_len;
+    unsigned abits1, abits2, abitsmin;
+};
+
+extern struct cpu_interface cpuintf[];
+
+void cpu_init(void);
+void cpu_run(void);
+
+/* optional watchdog */
+void watchdog_reset_w(int offset,int data);
+int watchdog_reset_r(int offset);
+/* Use this function to reset the machine */
+void machine_reset(void);
+/* Use this function to reset a single CPU */
+void cpu_set_reset_line(int cpu,int state);
+/* Use this function to halt a single CPU */
+void cpu_set_halt_line(int cpu,int state);
+
+/* This function returns CPUNUM current status (running or halted) */
+int cpu_getstatus(int cpunum);
+int cpu_gettotalcpu(void);
+int cpu_getactivecpu(void);
+void cpu_setactivecpu(int cpunum);
+
+/* Returns the current program counter */
+unsigned cpu_get_pc(void);
+/* Set the current program counter */
+void cpu_set_pc(unsigned val);
+
+/* Returns the current stack pointer */
+unsigned cpu_get_sp(void);
+/* Set the current stack pointer */
+void cpu_set_sp(unsigned val);
+
+/* Get the active CPUs context and return it's size */
+unsigned cpu_get_context(void *context);
+/* Set the active CPUs context */
+void cpu_set_context(void *context);
+
+/* Returns a specific register value (mamedbg) */
+unsigned cpu_get_reg(int regnum);
+/* Sets a specific register value (mamedbg) */
+void cpu_set_reg(int regnum, unsigned val);
+
+/* Returns previous pc (start of opcode causing read/write) */
+/* int cpu_getpreviouspc(void); */
+#define cpu_getpreviouspc() cpu_get_reg(REG_PREVIOUSPC)
+
+/* Returns the return address from the top of the stack (Z80 only) */
+/* int cpu_getreturnpc(void); */
+/* This can now be handled with a generic function */
+#define cpu_geturnpc() cpu_get_reg(REG_SP_CONTENTS)
+
+int cycles_currently_ran(void);
+int cycles_left_to_run(void);
+
+/* Returns the number of CPU cycles which take place in one video frame */
+int cpu_gettotalcycles(void);
+/* Returns the number of CPU cycles before the next interrupt handler call */
+int cpu_geticount(void);
+/* Returns the number of CPU cycles before the end of the current video frame */
+int cpu_getfcount(void);
+/* Returns the number of CPU cycles in one video frame */
+int cpu_getfperiod(void);
+/* Scales a given value by the ratio of fcount / fperiod */
+int cpu_scalebyfcount(int value);
+/* Returns the current scanline number */
+int cpu_getscanline(void);
+/* Returns the amount of time until a given scanline */
+double cpu_getscanlinetime(int scanline);
+/* Returns the duration of a single scanline */
+double cpu_getscanlineperiod(void);
+/* Returns the duration of a single scanline in cycles */
+int cpu_getscanlinecycles(void);
+/* Returns the number of cycles since the beginning of this frame */
+int cpu_getcurrentcycles(void);
+/* Returns the current horizontal beam position in pixels */
+int cpu_gethorzbeampos(void);
+/*
+  Returns the number of times the interrupt handler will be called before
+  the end of the current video frame. This is can be useful to interrupt
+  handlers to synchronize their operation. If you call this from outside
+  an interrupt handler, add 1 to the result, i.e. if it returns 0, it means
+  that the interrupt handler will be called once.
+*/
+int cpu_getiloops(void);
+
+/* Returns the current VBLANK state */
+int cpu_getvblank(void);
+
+/* Returns the number of the video frame we are currently playing */
+int cpu_getcurrentframe(void);
+
+
+/* generate a trigger after a specific period of time */
+void cpu_triggertime (double duration, int trigger);
+/* generate a trigger now */
+void cpu_trigger (int trigger);
+
+/* burn CPU cycles until a timer trigger */
+void cpu_spinuntil_trigger (int trigger);
+/* burn CPU cycles until the next interrupt */
+void cpu_spinuntil_int (void);
+/* burn CPU cycles until our timeslice is up */
+void cpu_spin (void);
+/* burn CPU cycles for a specific period of time */
+void cpu_spinuntil_time (double duration);
+
+/* yield our timeslice for a specific period of time */
+void cpu_yielduntil_trigger (int trigger);
+/* yield our timeslice until the next interrupt */
+void cpu_yielduntil_int (void);
+/* yield our current timeslice */
+void cpu_yield (void);
+/* yield our timeslice for a specific period of time */
+void cpu_yielduntil_time (double duration);
+
+/* set the NMI line state for a CPU, normally use PULSE_LINE */
+void cpu_set_nmi_line(int cpunum, int state);
+/* set the IRQ line state for a specific irq line of a CPU */
+/* normally use state HOLD_LINE, irqline 0 for first IRQ type of a cpu */
+void cpu_set_irq_line(int cpunum, int irqline, int state);
+/* this is to be called by CPU cores only! */
+void cpu_generate_internal_interrupt(int cpunum, int type);
+/* set the vector to be returned during a CPU's interrupt acknowledge cycle */
+void cpu_irq_line_vector_w(int cpunum, int irqline, int vector);
+
+/* use these in your write memory/port handles to set an IRQ vector */
+/* offset corresponds to the irq line number here */
+void cpu_0_irq_line_vector_w(int offset, int data);
+void cpu_1_irq_line_vector_w(int offset, int data);
+void cpu_2_irq_line_vector_w(int offset, int data);
+void cpu_3_irq_line_vector_w(int offset, int data);
+void cpu_4_irq_line_vector_w(int offset, int data);
+void cpu_5_irq_line_vector_w(int offset, int data);
+void cpu_6_irq_line_vector_w(int offset, int data);
+void cpu_7_irq_line_vector_w(int offset, int data);
+
+/* Obsolete functions: avoid to use them in new drivers if possible. */
+
+/* cause an interrupt on a CPU */
+void cpu_cause_interrupt(int cpu,int type);
+void cpu_clear_pending_interrupts(int cpu);
+void interrupt_enable_w(int offset,int data);
+void interrupt_vector_w(int offset,int data);
+int interrupt(void);
+int nmi_interrupt(void);
+int m68_level1_irq(void);
+int m68_level2_irq(void);
+int m68_level3_irq(void);
+int m68_level4_irq(void);
+int m68_level5_irq(void);
+int m68_level6_irq(void);
+int m68_level7_irq(void);
+int ignore_interrupt(void);
+
+/* CPU context access */
+void* cpu_getcontext (int _activecpu);
+int cpu_is_saving_context(int _activecpu);
+
+/***************************************************************************
+ * Get information for the currently active CPU
+ * cputype is a value from the CPU enum in driver.h
+ ***************************************************************************/
+/* Return number of address bits */
+unsigned cpu_address_bits(void);
+/* Return address mask */
+unsigned cpu_address_mask(void);
+/* Return address shift factor (TMS34010 bit addressing mode) */
+int cpu_address_shift(void);
+/* Return endianess of the emulated CPU (CPU_IS_LE or CPU_IS_BE) */
+unsigned cpu_endianess(void);
+/* Return opcode align unit (1 byte, 2 word, 4 dword) */
+unsigned cpu_align_unit(void);
+/* Return maximum instruction length */
+unsigned cpu_max_inst_len(void);
+
+/* Return name of the active CPU */
+const char *cpu_name(void);
+/* Return family name of the active CPU */
+const char *cpu_core_family(void);
+/* Return core version of the active CPU */
+const char *cpu_core_version(void);
+/* Return core filename of the active CPU */
+const char *cpu_core_file(void);
+/* Return credits info for of the active CPU */
+const char *cpu_core_credits(void);
+/* Return register layout definition for the active CPU */
+const char *cpu_reg_layout(void);
+/* Return (debugger) window layout definition for the active CPU */
+const char *cpu_win_layout(void);
+
+/* Disassemble an instruction at PC into the given buffer */
+unsigned cpu_dasm(char *buffer, unsigned pc);
+/* Return a string describing the currently set flag (status) bits of the active CPU */
+const char *cpu_flags(void);
+/* Return a string with a register name and hex value for the active CPU */
+/* regnum is a value defined in the CPU cores header files */
+const char *cpu_dump_reg(int regnum);
+/* Return a string describing the active CPUs current state */
+const char *cpu_dump_state(void);
+
+/***************************************************************************
+ * Get information for a specific CPU type
+ * cputype is a value from the CPU enum in driver.h
+ ***************************************************************************/
+/* Return address shift factor */
+/* TMS320C10 -1: word addressing mode, TMS34010 3: bit addressing mode */
+int cputype_address_shift(int cputype);
+/* Return number of address bits */
+unsigned cputype_address_bits(int cputype);
+/* Return address mask */
+unsigned cputype_address_mask(int cputype);
+/* Return endianess of the emulated CPU (CPU_IS_LE or CPU_IS_BE) */
+unsigned cputype_endianess(int cputype);
+/* Return opcode align unit (1 byte, 2 word, 4 dword) */
+unsigned cputype_align_unit(int cputype);
+/* Return maximum instruction length */
+unsigned cputype_max_inst_len(int cputype);
+
+/* Return name of the CPU */
+const char *cputype_name(int cputype);
+/* Return family name of the CPU */
+const char *cputype_core_family(int cputype);
+/* Return core version number of the CPU */
+const char *cputype_core_version(int cputype);
+/* Return core filename of the CPU */
+const char *cputype_core_file(int cputype);
+/* Return credits for the CPU core */
+const char *cputype_core_credits(int cputype);
+/* Return register layout definition for the CPU core */
+const char *cputype_reg_layout(int cputype);
+/* Return (debugger) window layout definition for the CPU core */
+const char *cputype_win_layout(int cputype);
+
+/***************************************************************************
+ * Get (or set) information for a numbered CPU of the running machine
+ * cpunum is a value between 0 and cpu_gettotalcpu() - 1
+ ***************************************************************************/
+/* Return number of address bits */
+unsigned cpunum_address_bits(int cputype);
+/* Return address mask */
+unsigned cpunum_address_mask(int cputype);
+/* Return endianess of the emulated CPU (CPU_LSB_FIRST or CPU_MSB_FIRST) */
+unsigned cpunum_endianess(int cputype);
+/* Return opcode align unit (1 byte, 2 word, 4 dword) */
+unsigned cpunum_align_unit(int cputype);
+/* Return maximum instruction length */
+unsigned cpunum_max_inst_len(int cputype);
+
+/* Get a register value for the specified CPU number of the running machine */
+unsigned cpunum_get_reg(int cpunum, int regnum);
+/* Set a register value for the specified CPU number of the running machine */
+void cpunum_set_reg(int cpunum, int regnum, unsigned val);
+
+/* Return (debugger) register layout definition for the CPU core */
+const char *cpunum_reg_layout(int cpunum);
+/* Return (debugger) window layout definition for the CPU core */
+const char *cpunum_win_layout(int cpunum);
+
+unsigned cpunum_dasm(int cpunum,char *buffer,unsigned pc);
+/* Return a string describing the currently set flag (status) bits of the CPU */
+const char *cpunum_flags(int cpunum);
+/* Return a string with a register name and value */
+/* regnum is a value defined in the CPU cores header files */
+const char *cpunum_dump_reg(int cpunum, int regnum);
+/* Return a string describing the CPUs current state */
+const char *cpunum_dump_state(int cpunum);
+/* Return a name for the specified cpu number */
+const char *cpunum_name(int cpunum);
+/* Return a family name for the specified cpu number */
+const char *cpunum_core_family(int cpunum);
+/* Return a version for the specified cpu number */
+const char *cpunum_core_version(int cpunum);
+/* Return a the source filename for the specified cpu number */
+const char *cpunum_core_file(int cpunum);
+/* Return a the credits for the specified cpu number */
+const char *cpunum_core_credits(int cpunum);
+
+/* Dump all of the running machines CPUs state to stderr */
+void cpu_dump_states(void);
+
+/* daisy-chain link */
+typedef struct {
+    void (*reset)(int);             /* reset callback     */
+    int  (*interrupt_entry)(int);   /* entry callback     */
+    void (*interrupt_reti)(int);    /* reti callback      */
+    int irq_param;                  /* callback paramater */
+}	Z80_DaisyChain;
+
+#define Z80_MAXDAISY	4		/* maximum of daisy chan device */
+
+#define Z80_INT_REQ     0x01    /* interrupt request mask       */
+#define Z80_INT_IEO     0x02    /* interrupt disable mask(IEO)  */
+
+#define Z80_VECTOR(device,state) (((device)<<8)|(state))
+
+#ifndef INLINE
+#define INLINE 		inline
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define cpu_readmem16		memory_read
+#define cpu_readport16		memory_readport
+#define cpu_writeport16		memory_writeport
+#define cpu_writemem16		memory_write
+#define cpu_readop			memory_readop
+#define cpu_readop_arg		memory_read
+#define logerror(x, ...)
+#define change_pc16(x)
+#define CALL_MAME_DEBUG
+
+#define ADDRESS_SPACES			3						/* maximum number of address spaces */
+#define ADDRESS_SPACE_PROGRAM	0						/* program address space */
+#define ADDRESS_SPACE_DATA		1						/* data address space */
+#define ADDRESS_SPACE_IO		2						/* I/O address space */
+
+enum
+{
+	/* internal flags (not for use by drivers!) */
+	INTERNAL_CLEAR_LINE = 100 + CLEAR_LINE,
+	INTERNAL_ASSERT_LINE = 100 + ASSERT_LINE,
+
+	/* input lines */
+	MAX_INPUT_LINES = 32+3,
+	INPUT_LINE_IRQ0 = 0,
+	INPUT_LINE_IRQ1 = 1,
+	INPUT_LINE_IRQ2 = 2,
+	INPUT_LINE_IRQ3 = 3,
+	INPUT_LINE_IRQ4 = 4,
+	INPUT_LINE_IRQ5 = 5,
+	INPUT_LINE_IRQ6 = 6,
+	INPUT_LINE_IRQ7 = 7,
+	INPUT_LINE_IRQ8 = 8,
+	INPUT_LINE_IRQ9 = 9,
+	INPUT_LINE_NMI = MAX_INPUT_LINES - 3,
+
+	/* special input lines that are implemented in the core */
+	INPUT_LINE_RESET = MAX_INPUT_LINES - 2,
+	INPUT_LINE_HALT = MAX_INPUT_LINES - 1,
+
+	/* output lines */
+	MAX_OUTPUT_LINES = 32
+};
+
+enum
+{
+	/* --- the following bits of info are returned as 64-bit signed integers --- */
+	CPUINFO_INT_FIRST = 0x00000,
+
+	CPUINFO_INT_CONTEXT_SIZE = CPUINFO_INT_FIRST,		/* R/O: size of CPU context in bytes */
+	CPUINFO_INT_INPUT_LINES,							/* R/O: number of input lines */
+	CPUINFO_INT_OUTPUT_LINES,							/* R/O: number of output lines */
+	CPUINFO_INT_DEFAULT_IRQ_VECTOR,						/* R/O: default IRQ vector */
+	CPUINFO_INT_ENDIANNESS,								/* R/O: either CPU_IS_BE or CPU_IS_LE */
+	CPUINFO_INT_CLOCK_DIVIDER,							/* R/O: internal clock divider */
+	CPUINFO_INT_MIN_INSTRUCTION_BYTES,					/* R/O: minimum bytes per instruction */
+	CPUINFO_INT_MAX_INSTRUCTION_BYTES,					/* R/O: maximum bytes per instruction */
+	CPUINFO_INT_MIN_CYCLES,								/* R/O: minimum cycles for a single instruction */
+	CPUINFO_INT_MAX_CYCLES,								/* R/O: maximum cycles for a single instruction */
+
+	CPUINFO_INT_DATABUS_WIDTH,							/* R/O: data bus size for each address space (8,16,32,64) */
+	CPUINFO_INT_DATABUS_WIDTH_LAST = CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACES - 1,
+	CPUINFO_INT_ADDRBUS_WIDTH,							/* R/O: address bus size for each address space (12-32) */
+	CPUINFO_INT_ADDRBUS_WIDTH_LAST = CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACES - 1,
+	CPUINFO_INT_ADDRBUS_SHIFT,							/* R/O: shift applied to addresses each address space (+3 means >>3, -1 means <<1) */
+	CPUINFO_INT_ADDRBUS_SHIFT_LAST = CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACES - 1,
+
+	CPUINFO_INT_SP,										/* R/W: the current stack pointer value */
+	CPUINFO_INT_PC,										/* R/W: the current PC value */
+	CPUINFO_INT_PREVIOUSPC,								/* R/W: the previous PC value */
+	CPUINFO_INT_INPUT_STATE,							/* R/W: states for each input line */
+	CPUINFO_INT_INPUT_STATE_LAST = CPUINFO_INT_INPUT_STATE + MAX_INPUT_LINES - 1,
+	CPUINFO_INT_OUTPUT_STATE,							/* R/W: states for each output line */
+	CPUINFO_INT_OUTPUT_STATE_LAST = CPUINFO_INT_OUTPUT_STATE + MAX_OUTPUT_LINES - 1,
+	CPUINFO_INT_REGISTER,								/* R/W: values of up to MAX_REGs registers */
+	CPUINFO_INT_REGISTER_LAST = CPUINFO_INT_REGISTER + MAX_REGS - 1,
+
+	CPUINFO_INT_CPU_SPECIFIC = 0x08000,					/* R/W: CPU-specific values start here */
+
+	/* --- the following bits of info are returned as pointers to data or functions --- */
+	CPUINFO_PTR_FIRST = 0x10000,
+
+	CPUINFO_PTR_SET_INFO = CPUINFO_PTR_FIRST,			/* R/O: void (*set_info)(UINT32 state, INT64 data, void *ptr) */
+	CPUINFO_PTR_GET_CONTEXT,							/* R/O: void (*get_context)(void *buffer) */
+	CPUINFO_PTR_SET_CONTEXT,							/* R/O: void (*set_context)(void *buffer) */
+	CPUINFO_PTR_INIT,									/* R/O: void (*init)(void) */
+	CPUINFO_PTR_RESET,									/* R/O: void (*reset)(void *param) */
+	CPUINFO_PTR_EXIT,									/* R/O: void (*exit)(void) */
+	CPUINFO_PTR_EXECUTE,								/* R/O: int (*execute)(int cycles) */
+	CPUINFO_PTR_BURN,									/* R/O: void (*burn)(int cycles) */
+	CPUINFO_PTR_DISASSEMBLE,							/* R/O: void (*disassemble)(char *buffer, offs_t pc) */
+	CPUINFO_PTR_IRQ_CALLBACK,							/* R/W: int (*irqcallback)(int state) */
+	CPUINFO_PTR_INSTRUCTION_COUNTER,					/* R/O: int *icount */
+	CPUINFO_PTR_REGISTER_LAYOUT,						/* R/O: struct debug_register_layout *layout */
+	CPUINFO_PTR_WINDOW_LAYOUT,							/* R/O: struct debug_window_layout *layout */
+	CPUINFO_PTR_INTERNAL_MEMORY_MAP,					/* R/O: construct_map_t map */
+	CPUINFO_PTR_INTERNAL_MEMORY_MAP_LAST = CPUINFO_PTR_INTERNAL_MEMORY_MAP + ADDRESS_SPACES - 1,
+	CPUINFO_PTR_DEBUG_REGISTER_LIST,					/* R/O: int *list: list of registers for NEW_DEBUGGER */
+
+	CPUINFO_PTR_CPU_SPECIFIC = 0x18000,					/* R/W: CPU-specific values start here */
+
+	/* --- the following bits of info are returned as NULL-terminated strings --- */
+	CPUINFO_STR_FIRST = 0x20000,
+
+	CPUINFO_STR_NAME = CPUINFO_STR_FIRST,				/* R/O: name of the CPU */
+	CPUINFO_STR_CORE_FAMILY,							/* R/O: family of the CPU */
+	CPUINFO_STR_CORE_VERSION,							/* R/O: version of the CPU core */
+	CPUINFO_STR_CORE_FILE,								/* R/O: file containing the CPU core */
+	CPUINFO_STR_CORE_CREDITS,							/* R/O: credits for the CPU core */
+	CPUINFO_STR_FLAGS,									/* R/O: string representation of the main flags value */
+	CPUINFO_STR_REGISTER,								/* R/O: string representation of up to MAX_REGs registers */
+	CPUINFO_STR_REGISTER_LAST = CPUINFO_STR_REGISTER + MAX_REGS - 1,
+
+	CPUINFO_STR_CPU_SPECIFIC = 0x28000					/* R/W: CPU-specific values start here */
+};
+
+#endif	/* CPUINTRF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/eng_protos.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,19 @@
+//
+// Audio Overload
+// Emulated music player
+//
+// (C) 2000-2007 Richard F. Bannister
+//
+
+#include <audacious/plugin.h>
+
+//
+// eng_protos.h
+//
+
+int32 psf2_start(uint8 *, uint32 length);
+int32 psf2_execute(InputPlayback *playback);
+int32 psf2_stop(void);
+int32 psf2_command(int32, int32);
+int32 psf2_fill_info(Tuple *);
+int   psf2_seek(uint32);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/eng_psf.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,414 @@
+/*
+	Audio Overload SDK - PSF file format engine
+
+	Copyright (c) 2007 R. Belmont and Richard Bannister.
+
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+	* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "ao.h"
+#include "eng_protos.h"
+#include "cpuintrf.h"
+#include "psx.h"
+
+#include "peops/stdafx.h"
+#include "peops/externals.h"
+#include "peops/regs.h"
+#include "peops/registers.h"
+#include "peops/spu.h"
+
+
+#include "corlett.h"
+
+#define DEBUG_LOADER	(0)
+
+static corlett_t	*c = NULL;
+static char 		psfby[256];
+char			*spu_pOutput;
+int			psf_refresh  = -1;
+
+
+// main RAM
+extern uint32 psx_ram[((2*1024*1024)/4)+4];
+extern uint32 psx_scratch[0x400];
+extern uint32 initial_ram[((2*1024*1024)/4)+4];
+extern uint32 initial_scratch[0x400];
+static uint32 initialPC, initialGP, initialSP;
+
+extern void mips_init( void );
+extern void mips_reset( void *param );
+extern int mips_execute( int cycles );
+extern void mips_set_info(UINT32 state, union cpuinfo *info);
+extern void psx_hw_init(void);
+extern void psx_hw_slice(void);
+extern void psx_hw_frame(void);
+extern void setlength(int32 stop, int32 fade);
+
+int32 psf_start(uint8 *buffer, uint32 length)
+{
+	uint8 *file, *lib_decoded, *lib_raw_file, *alib_decoded;
+	uint32 offset, plength, PC, SP, GP, lengthMS, fadeMS;
+	uint64 file_len, lib_len, lib_raw_length, alib_len;
+	corlett_t *lib;
+	int i;
+	union cpuinfo mipsinfo;
+
+	// clear PSX work RAM before we start scribbling in it
+	memset(psx_ram, 0, 2*1024*1024);
+
+//	printf("Length = %d\n", length);
+
+	// Decode the current GSF
+	if (corlett_decode(buffer, length, &file, &file_len, &c) != AO_SUCCESS)
+	{
+		return AO_FAIL;
+	}
+
+//	printf("file_len %d reserve %d\n", file_len, c->res_size);
+
+	// check for PSX EXE signature
+	if (strncmp((char *)file, "PS-X EXE", 8))
+	{
+		return AO_FAIL;
+	}
+
+	#if DEBUG_LOADER
+	offset = file[0x18] | file[0x19]<<8 | file[0x1a]<<16 | file[0x1b]<<24;
+	printf("Text section start: %x\n", offset);
+	offset = file[0x1c] | file[0x1d]<<8 | file[0x1e]<<16 | file[0x1f]<<24;
+	printf("Text section size: %x\n", offset);
+	printf("Region: [%s]\n", &file[0x4c]);
+	printf("refresh: [%s]\n", c->inf_refresh);			
+	#endif
+
+	if (c->inf_refresh[0] == '5')
+	{
+		psf_refresh = 50;
+	}
+	if (c->inf_refresh[0] == '6')
+	{
+		psf_refresh = 60;
+	}
+
+	PC = file[0x10] | file[0x11]<<8 | file[0x12]<<16 | file[0x13]<<24;
+	GP = file[0x14] | file[0x15]<<8 | file[0x16]<<16 | file[0x17]<<24;
+	SP = file[0x30] | file[0x31]<<8 | file[0x32]<<16 | file[0x33]<<24;
+
+	#if DEBUG_LOADER
+	printf("Top level: PC %x GP %x SP %x\n", PC, GP, SP);
+	#endif
+
+	// Get the library file, if any
+	if (c->lib[0] != 0)
+	{
+		uint64 tmp_length;
+	
+		#if DEBUG_LOADER	
+		printf("Loading library: %s\n", c->lib);
+		#endif
+		if (ao_get_lib(c->lib, &lib_raw_file, &tmp_length) != AO_SUCCESS)
+		{
+			return AO_FAIL;
+		}
+		lib_raw_length = tmp_length;
+		
+		if (corlett_decode(lib_raw_file, lib_raw_length, &lib_decoded, &lib_len, &lib) != AO_SUCCESS)
+		{
+			free(lib_raw_file);
+			return AO_FAIL;
+		}
+				
+		// Free up raw file
+		free(lib_raw_file);
+
+		if (strncmp((char *)lib_decoded, "PS-X EXE", 8))
+		{
+			printf("Major error!  PSF was OK, but referenced library is not!\n");
+			free(lib);
+			return AO_FAIL;
+		}
+
+		#if DEBUG_LOADER	
+		offset = lib_decoded[0x18] | lib_decoded[0x19]<<8 | lib_decoded[0x1a]<<16 | lib_decoded[0x1b]<<24;
+		printf("Text section start: %x\n", offset);
+		offset = lib_decoded[0x1c] | lib_decoded[0x1d]<<8 | lib_decoded[0x1e]<<16 | lib_decoded[0x1f]<<24;
+		printf("Text section size: %x\n", offset);
+		printf("Region: [%s]\n", &lib_decoded[0x4c]);
+		printf("refresh: [%s]\n", lib->inf_refresh);			
+		#endif
+
+		// if the original file had no refresh tag, give the lib a shot
+		if (psf_refresh == -1)
+		{
+			if (lib->inf_refresh[0] == '5')
+			{
+				psf_refresh = 50;
+			}
+			if (lib->inf_refresh[0] == '6')
+			{
+				psf_refresh = 60;
+			}
+		}
+
+		PC = lib_decoded[0x10] | lib_decoded[0x11]<<8 | lib_decoded[0x12]<<16 | lib_decoded[0x13]<<24;
+		GP = lib_decoded[0x14] | lib_decoded[0x15]<<8 | lib_decoded[0x16]<<16 | lib_decoded[0x17]<<24;
+		SP = lib_decoded[0x30] | lib_decoded[0x31]<<8 | lib_decoded[0x32]<<16 | lib_decoded[0x33]<<24;
+
+		#if DEBUG_LOADER
+		printf("Library: PC %x GP %x SP %x\n", PC, GP, SP);
+		#endif
+
+		// now patch the file into RAM
+		offset = lib_decoded[0x18] | lib_decoded[0x19]<<8 | lib_decoded[0x1a]<<16 | lib_decoded[0x1b]<<24;
+		offset &= 0x3fffffff;	// kill any MIPS cache segment indicators
+		plength = lib_decoded[0x1c] | lib_decoded[0x1d]<<8 | lib_decoded[0x1e]<<16 | lib_decoded[0x1f]<<24;
+		#if DEBUG_LOADER
+		printf("library offset: %x plength: %d\n", offset, plength);
+		#endif
+		memcpy(&psx_ram[offset/4], lib_decoded+2048, plength);
+		
+		// Dispose the corlett structure for the lib - we don't use it
+		free(lib);
+	}
+
+	// now patch the main file into RAM OVER the libraries (but not the aux lib)
+	offset = file[0x18] | file[0x19]<<8 | file[0x1a]<<16 | file[0x1b]<<24;
+	offset &= 0x3fffffff;	// kill any MIPS cache segment indicators
+	plength = file[0x1c] | file[0x1d]<<8 | file[0x1e]<<16 | file[0x1f]<<24;
+
+	// Philosoma has an illegal "plength".  *sigh*
+	if (plength > (file_len-2048))
+	{
+		plength = file_len-2048;
+	}
+	memcpy(&psx_ram[offset/4], file+2048, plength);
+
+	// load any auxiliary libraries now
+	for (i = 0; i < 8; i++)
+	{
+		if (c->libaux[i][0] != 0)
+		{
+			uint64 tmp_length;
+		
+			#if DEBUG_LOADER	
+			printf("Loading aux library: %s\n", c->libaux[i]);
+			#endif
+
+			if (ao_get_lib(c->libaux[i], &lib_raw_file, &tmp_length) != AO_SUCCESS)
+			{
+				return AO_FAIL;
+			}
+			lib_raw_length = tmp_length;
+		
+			if (corlett_decode(lib_raw_file, lib_raw_length, &alib_decoded, &alib_len, &lib) != AO_SUCCESS)
+			{
+				free(lib_raw_file);
+				return AO_FAIL;
+			}
+				
+			// Free up raw file
+			free(lib_raw_file);
+
+			if (strncmp((char *)alib_decoded, "PS-X EXE", 8))
+			{
+				printf("Major error!  PSF was OK, but referenced library is not!\n");
+				free(lib);
+				return AO_FAIL;
+			}
+
+			#if DEBUG_LOADER	
+			offset = alib_decoded[0x18] | alib_decoded[0x19]<<8 | alib_decoded[0x1a]<<16 | alib_decoded[0x1b]<<24;
+			printf("Text section start: %x\n", offset);
+			offset = alib_decoded[0x1c] | alib_decoded[0x1d]<<8 | alib_decoded[0x1e]<<16 | alib_decoded[0x1f]<<24;
+			printf("Text section size: %x\n", offset);
+			printf("Region: [%s]\n", &alib_decoded[0x4c]);
+			#endif
+
+			// now patch the file into RAM
+			offset = alib_decoded[0x18] | alib_decoded[0x19]<<8 | alib_decoded[0x1a]<<16 | alib_decoded[0x1b]<<24;
+			offset &= 0x3fffffff;	// kill any MIPS cache segment indicators
+			plength = alib_decoded[0x1c] | alib_decoded[0x1d]<<8 | alib_decoded[0x1e]<<16 | alib_decoded[0x1f]<<24;
+			memcpy(&psx_ram[offset/4], alib_decoded+2048, plength);
+		
+			// Dispose the corlett structure for the lib - we don't use it
+			free(lib);
+		}
+	}
+
+	free(file);
+//	free(lib_decoded);
+	
+	// Finally, set psfby tag
+	strcpy(psfby, "n/a");
+	if (c)
+	{
+		int i;
+		for (i = 0; i < MAX_UNKNOWN_TAGS; i++)
+		{
+			if (!strcasecmp(c->tag_name[i], "psfby"))
+				strcpy(psfby, c->tag_data[i]);
+		}
+	}
+
+	mips_init();
+	mips_reset(NULL);
+
+	// set the initial PC, SP, GP
+	#if DEBUG_LOADER	
+	printf("Initial PC %x, GP %x, SP %x\n", PC, GP, SP);
+	printf("Refresh = %d\n", psf_refresh);
+	#endif
+	mipsinfo.i = PC;
+	mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+	// set some reasonable default for the stack
+	if (SP == 0)
+	{
+		SP = 0x801fff00;
+	}
+
+	mipsinfo.i = SP;
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
+
+	mipsinfo.i = GP;
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo);
+
+	#if DEBUG_LOADER && 1
+	{
+		FILE *f;
+
+		f = fopen("psxram.bin", "wb");
+		fwrite(psx_ram, 2*1024*1024, 1, f);
+		fclose(f);
+	}
+	#endif
+
+	psx_hw_init();
+	SPUinit();
+	SPUopen();
+
+	lengthMS = psfTimeToMS(c->inf_length);
+	fadeMS = psfTimeToMS(c->inf_fade);
+
+	#if DEBUG_LOADER
+	printf("length %d fade %d\n", lengthMS, fadeMS);
+	#endif
+
+	if (lengthMS == 0) 
+	{
+		lengthMS = ~0;
+	}
+
+	setlength(lengthMS, fadeMS);
+
+//	psx_ram[0x118b8/4] = LE32(0);	// crash 2 hack
+
+	// backup the initial state for restart
+	memcpy(initial_ram, psx_ram, 2*1024*1024);
+	memcpy(initial_scratch, psx_scratch, 0x400);
+	initialPC = PC;
+	initialGP = GP;
+	initialSP = SP;
+
+	mips_execute(5000);
+	
+	return AO_SUCCESS;
+}
+
+void spu_update(unsigned char* pSound,long lBytes)
+{
+	memcpy(spu_pOutput, pSound, lBytes);
+}
+
+int32 psf_gen(int16 *buffer, uint32 samples)
+{	
+	int i;
+
+	for (i = 0; i < samples; i++)
+	{
+		psx_hw_slice();
+		SPUasync(384);
+	}
+
+	spu_pOutput = (char *)buffer;
+	SPU_flushboot();
+
+	psx_hw_frame();
+
+	return AO_SUCCESS;
+}
+
+int32 psf_stop(void)
+{
+	SPUclose();
+	free(c);
+
+	return AO_SUCCESS;
+}
+
+int32 psf_command(int32 command, int32 parameter)
+{
+	union cpuinfo mipsinfo;
+	uint32 lengthMS, fadeMS;
+
+	switch (command)
+	{
+		case COMMAND_RESTART:
+			SPUclose();
+
+			memcpy(psx_ram, initial_ram, 2*1024*1024);
+			memcpy(psx_scratch, initial_scratch, 0x400);
+
+			mips_init();
+			mips_reset(NULL);
+			psx_hw_init();
+			SPUinit();
+			SPUopen();
+
+			lengthMS = psfTimeToMS(c->inf_length);
+			fadeMS = psfTimeToMS(c->inf_fade);
+
+			if (lengthMS == 0) 
+			{
+				lengthMS = ~0;
+			}
+			setlength(lengthMS, fadeMS);
+
+			mipsinfo.i = initialPC;
+			mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+			mipsinfo.i = initialSP;
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
+			mipsinfo.i = initialGP;
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo);
+
+			mips_execute(5000);
+
+			return AO_SUCCESS;
+		
+	}
+	return AO_FAIL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/eng_psf2.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,679 @@
+/*
+	Audio Overload SDK - PSF2 file format engine
+
+	Copyright (c) 2007-2008 R. Belmont and Richard Bannister.
+
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+	* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+//
+// Audio Overload
+// Emulated music player
+//
+// (C) 2000-2008 Richard F. Bannister
+//
+
+//
+// eng_psf2.c
+//
+// References:
+// psf_format.txt v1.6 by Neill Corlett (filesystem and decompression info)
+// Intel ELF format specs ELF.PS (general ELF parsing info)
+// http://ps2dev.org/kb.x?T=457 (IRX relocation and inter-module call info)
+// http://ps2dev.org/ (the whole site - lots of IOP info)
+// spu2regs.txt (comes with SexyPSF source: IOP hardware info)
+// 64-bit ELF Object File Specification: http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf (MIPS ELF relocation types)
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "ao.h"
+#include "eng_protos.h"
+#include "cpuintrf.h"
+#include "psx.h"
+
+#include "peops2/stdafx.h"
+#include "peops2/externals.h"
+#include "peops2/regs.h"
+#include "peops2/registers.h"
+#include "peops2/spu.h"
+
+#include "corlett.h"
+
+#define DEBUG_LOADER	(0)
+#define MAX_FS		(32)	// maximum # of filesystems (libs and subdirectories)
+
+// ELF relocation helpers
+#define ELF32_R_SYM(val)                ((val) >> 8)
+#define ELF32_R_TYPE(val)               ((val) & 0xff)
+
+static corlett_t	*c = NULL;
+static char 		psfby[256];
+static char		*spu_pOutput;
+
+// main RAM
+extern uint32 psx_ram[(2*1024*1024)/4];
+extern uint32 initial_ram[(2*1024*1024)/4];
+static uint32 initialPC, initialSP;
+static uint32 loadAddr, lengthMS, fadeMS;
+
+static uint8 *filesys[MAX_FS];
+static uint8 *lib_raw_file;
+static uint32 fssize[MAX_FS];
+static int num_fs;
+
+extern void mips_init( void );
+extern void mips_reset( void *param );
+extern int mips_execute( int cycles );
+extern void mips_set_info(UINT32 state, union cpuinfo *info);
+extern void psx_hw_init(void);
+extern void ps2_hw_slice(void);
+extern void ps2_hw_frame(void);
+extern void setlength2(int32 stop, int32 fade);
+
+static uint32 secname(uint8 *start, uint32 strndx, uint32 shoff, uint32 shentsize, uint32 name)
+{
+	uint32 offset, shent;
+
+	// get string table section
+	shent = shoff + (shentsize * strndx);
+
+	// find the offset to the section
+	offset = start[shent+16] | start[shent+17]<<8 | start[shent+18]<<16 | start[shent+19]<<24; 
+
+	offset += name;
+	
+	return offset;	
+}
+
+static void do_iopmod(uint8 *start, uint32 offset)
+{
+	uint32 nameoffs, saddr, heap, tsize, dsize, bsize, vers2;
+
+	nameoffs = start[offset] | start[offset+1]<<8 | start[offset+2]<<16 | start[offset+3]<<24;
+
+	saddr = start[offset+4] | start[offset+5]<<8 | start[offset+6]<<16 | start[offset+7]<<24;
+	heap = start[offset+8] | start[offset+9]<<8 | start[offset+10]<<16 | start[offset+11]<<24;
+	tsize = start[offset+12] | start[offset+13]<<8 | start[offset+14]<<16 | start[offset+15]<<24; 
+	dsize = start[offset+16] | start[offset+17]<<8 | start[offset+18]<<16 | start[offset+19]<<24; 
+	bsize = start[offset+20] | start[offset+21]<<8 | start[offset+22]<<16 | start[offset+23]<<24; 
+	vers2 = start[offset+24] | start[offset+25]<<8;
+
+//	printf("nameoffs %08x saddr %08x heap %08x tsize %08x dsize %08x bsize %08x\n", nameoffs, saddr, heap, tsize, dsize, bsize);
+	#if DEBUG_LOADER
+	printf("vers: %04x name [%s]\n", vers2, &start[offset+26]);
+	#endif
+}
+
+uint32 psf2_load_elf(uint8 *start, uint32 len)
+{
+	uint32 entry, phoff, shoff, phentsize, shentsize, phnum, shnum, shstrndx;
+	uint32 name, type, flags, addr, offset, size, shent;
+	uint32 totallen;
+	int i, rec;
+//	FILE *f;
+
+	if (loadAddr & 3)
+	{
+		loadAddr &= ~3;
+		loadAddr += 4;
+	}
+
+	#if DEBUG_LOADER
+	printf("psf2_load_elf: starting at %08x\n", loadAddr | 0x80000000);
+	#endif
+
+	if ((start[0] != 0x7f) || (start[1] != 'E') || (start[2] != 'L') || (start[3] != 'F'))
+	{
+		printf("Not an ELF file\n");
+		return 0xffffffff;
+	}
+
+	entry = start[24] | start[25]<<8 | start[26]<<16 | start[27]<<24;	// 0x18
+	phoff = start[28] | start[29]<<8 | start[30]<<16 | start[31]<<24; 	// 0x1c
+	shoff = start[32] | start[33]<<8 | start[34]<<16 | start[35]<<24; 	// 0x20
+		
+//	printf("Entry: %08x phoff %08x shoff %08x\n", entry, phoff, shoff);
+
+	phentsize = start[42] | start[43]<<8;			// 0x2a
+	phnum = start[44] | start[45]<<8;			// 0x2c
+	shentsize = start[46] | start[47]<<8;			// 0x2e
+	shnum = start[48] | start[49]<<8;			// 0x30
+	shstrndx = start[50] | start[51]<<8;			// 0x32
+
+//	printf("phentsize %08x phnum %d shentsize %08x shnum %d shstrndx %d\n", phentsize, phnum, shentsize, shnum, shstrndx);	
+
+	// process ELF sections
+	shent = shoff;
+	totallen = 0;
+	for (i = 0; i < shnum; i++)
+	{
+		name = start[shent] | start[shent+1]<<8 | start[shent+2]<<16 | start[shent+3]<<24;
+		type = start[shent+4] | start[shent+5]<<8 | start[shent+6]<<16 | start[shent+7]<<24;
+		flags = start[shent+8] | start[shent+9]<<8 | start[shent+10]<<16 | start[shent+11]<<24;
+		addr = start[shent+12] | start[shent+13]<<8 | start[shent+14]<<16 | start[shent+15]<<24;
+		offset = start[shent+16] | start[shent+17]<<8 | start[shent+18]<<16 | start[shent+19]<<24;
+		size = start[shent+20] | start[shent+21]<<8 | start[shent+22]<<16 | start[shent+23]<<24;
+
+//		printf("Section %02d: name %08x [%s] type %08x flags %08x addr %08x offset %08x size %08x\n", i, name, &start[secname(start, shstrndx, shoff, shentsize, name)], type, flags, addr, offset, size);
+
+		switch (type)
+		{
+			case 0:			// section table header - do nothing
+				break;
+
+			case 1:			// PROGBITS: copy data to destination
+				memcpy(&psx_ram[(loadAddr + addr)/4], &start[offset], size);
+				totallen += size;
+				break;
+
+			case 2:			// SYMTAB: ignore
+				break;
+
+			case 3:			// STRTAB: ignore
+				break;
+
+			case 8:			// NOBITS: BSS region, zero out destination
+				memset(&psx_ram[(loadAddr + addr)/4], 0, size);
+				totallen += size;
+				break;
+
+			case 9:			// REL: short relocation data
+		  		for (rec = 0; rec < (size/8); rec++)
+				{
+					uint32 offs, info, target, temp, val, vallo;
+					static uint32 hi16offs = 0, hi16target = 0;
+
+					offs = start[offset+(rec*8)] | start[offset+1+(rec*8)]<<8 | start[offset+2+(rec*8)]<<16 | start[offset+3+(rec*8)]<<24;
+					info = start[offset+4+(rec*8)] | start[offset+5+(rec*8)]<<8 | start[offset+6+(rec*8)]<<16 | start[offset+7+(rec*8)]<<24;
+					target = LE32(psx_ram[(loadAddr+offs)/4]);
+					
+//					printf("[%04d] offs %08x type %02x info %08x => %08x\n", rec, offs, ELF32_R_TYPE(info), ELF32_R_SYM(info), target);
+
+					switch (ELF32_R_TYPE(info))
+					{
+						case 2:	      	// R_MIPS_32
+							target += loadAddr;
+//							target |= 0x80000000;
+							break;
+
+						case 4:		// R_MIPS_26
+							temp = (target & 0x03ffffff);
+							target &= 0xfc000000;
+							temp += (loadAddr>>2);
+							target |= temp;
+							break;
+
+						case 5:		// R_MIPS_HI16
+							hi16offs = offs;
+							hi16target = target;
+							break;
+
+						case 6:		// R_MIPS_LO16
+							vallo = ((target & 0xffff) ^ 0x8000) - 0x8000;
+
+							val = ((hi16target & 0xffff) << 16) +	vallo;
+							val += loadAddr;
+//							val |= 0x80000000;
+
+							/* Account for the sign extension that will happen in the low bits.  */
+							val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
+
+							hi16target = (hi16target & ~0xffff) | val;
+
+							/* Ok, we're done with the HI16 relocs.  Now deal with the LO16.  */
+							val = loadAddr + vallo;
+							target = (target & ~0xffff) | (val & 0xffff);
+
+							psx_ram[(loadAddr+hi16offs)/4] = LE32(hi16target);
+							break;
+
+						default:
+							printf("FATAL: Unknown MIPS ELF relocation!\n");
+							return 0xffffffff;
+							break;
+					}
+
+					psx_ram[(loadAddr+offs)/4] = LE32(target);
+				}						
+				break;
+
+			case 0x70000080:	// .iopmod
+				do_iopmod(start, offset);
+				break;
+
+			default:
+				#if DEBUG_LOADER
+				printf("Unhandled ELF section type %d\n", type);
+				#endif
+				break;
+		}
+
+		shent += shentsize;
+	}	
+
+	entry += loadAddr;
+	entry |= 0x80000000;
+	loadAddr += totallen;
+
+	#if DEBUG_LOADER
+	printf("psf2_load_elf: entry PC %08x\n", entry);
+	#endif
+	return entry;
+}
+
+static uint32 load_file_ex(uint8 *top, uint8 *start, uint32 len, char *file, uint8 *buf, uint32 buflen)
+{
+	int32 numfiles, i, j;
+	uint8 *cptr;
+	uint32 offs, uncomp, bsize, cofs, uofs;
+	uint32 X;
+	uLongf dlength;
+	int uerr;
+	char matchname[512], *remainder;
+
+	// strip out to only the directory name
+	i = 0;
+	while ((file[i] != '/') && (file[i] != '\\') && (file[i] != '\0'))
+	{
+		matchname[i] = file[i];
+		i++;
+	}
+	matchname[i] = '\0';
+	remainder = &file[i+1];
+	
+	cptr = start + 4; 
+
+	numfiles = start[0] | start[1]<<8 | start[2]<<16 | start[3]<<24;
+
+	for (i = 0; i < numfiles; i++)
+	{
+		offs = cptr[36] | cptr[37]<<8 | cptr[38]<<16 | cptr[39]<<24;
+		uncomp = cptr[40] | cptr[41]<<8 | cptr[42]<<16 | cptr[43]<<24;
+		bsize = cptr[44] | cptr[45]<<8 | cptr[46]<<16 | cptr[47]<<24;
+
+		#if DEBUG_LOADER
+		printf("[%s vs %s]: ofs %08x uncomp %08x bsize %08x\n", cptr, matchname, offs, uncomp, bsize);
+		#endif
+		
+		if (!strcasecmp((char *)cptr, matchname))
+		{
+			if ((uncomp == 0) && (bsize == 0))
+			{
+				#if DEBUG_LOADER
+				printf("Drilling into subdirectory [%s] with [%s] at offset %x\n", matchname, remainder, offs);
+				#endif
+				return load_file_ex(top, &top[offs], len-offs, remainder, buf, buflen);
+			}
+
+			X = (uncomp + bsize - 1) / bsize;
+			
+			cofs = offs + (X*4);
+			uofs = 0;
+			for (j = 0; j < X; j++)
+			{
+				uint32 usize;
+
+				usize = top[offs+(j*4)] | top[offs+1+(j*4)]<<8 | top[offs+2+(j*4)]<<16 | top[offs+3+(j*4)]<<24;
+
+				dlength = buflen - uofs;
+		
+				uerr = uncompress(&buf[uofs], &dlength, &top[cofs], usize);
+				if (uerr != Z_OK)
+				{
+					printf("Decompress fail: %x %d!\n", dlength, uerr);
+					return 0xffffffff;
+				}
+
+				cofs += usize;
+				uofs += dlength;
+			}
+
+			return uncomp;
+		}
+		else
+		{
+			cptr += 48;
+		}
+	}
+
+	return 0xffffffff;
+}
+
+static uint32 load_file(int fs, char *file, uint8 *buf, uint32 buflen)
+{
+	return load_file_ex(filesys[fs], filesys[fs], fssize[fs], file, buf, buflen);
+}
+
+#if 0
+static dump_files(int fs, uint8 *buf, uint32 buflen)
+{
+	int32 numfiles, i, j;
+	uint8 *cptr;
+	uint32 offs, uncomp, bsize, cofs, uofs;
+	uint32 X;
+	uLongf dlength;
+	int uerr;
+	uint8 *start;
+	uint32 len;
+	FILE *f;
+	char tfn[128];
+
+	printf("Dumping FS %d\n", fs);
+		
+	start = filesys[fs];
+	len = fssize[fs];
+
+	cptr = start + 4; 
+
+	numfiles = start[0] | start[1]<<8 | start[2]<<16 | start[3]<<24;
+
+	for (i = 0; i < numfiles; i++)
+	{
+		offs = cptr[36] | cptr[37]<<8 | cptr[38]<<16 | cptr[39]<<24;
+		uncomp = cptr[40] | cptr[41]<<8 | cptr[42]<<16 | cptr[43]<<24;
+		bsize = cptr[44] | cptr[45]<<8 | cptr[46]<<16 | cptr[47]<<24;
+
+		if (bsize > 0)		
+		{
+			X = (uncomp + bsize - 1) / bsize;
+
+			printf("[dump %s]: ofs %08x uncomp %08x bsize %08x\n", cptr, offs, uncomp, bsize);
+
+			cofs = offs + (X*4);
+			uofs = 0;
+			for (j = 0; j < X; j++)
+			{
+				uint32 usize;
+
+				usize = start[offs+(j*4)] | start[offs+1+(j*4)]<<8 | start[offs+2+(j*4)]<<16 | start[offs+3+(j*4)]<<24;
+
+				dlength = buflen - uofs;
+		
+				uerr = uncompress(&buf[uofs], &dlength, &start[cofs], usize);
+				if (uerr != Z_OK)
+				{
+					printf("Decompress fail: %x %d!\n", dlength, uerr);
+					return 0xffffffff;
+				}
+
+				cofs += usize;
+				uofs += dlength;
+			}
+
+			sprintf(tfn, "iopfiles/%s", cptr);
+			f = fopen(tfn, "wb");
+			fwrite(buf, uncomp, 1, f);
+			fclose(f);
+		}
+		else
+		{
+			printf("[subdir %s]: ofs %08x uncomp %08x bsize %08x\n", cptr, offs, uncomp, bsize);
+		}
+
+		cptr += 48;
+	}
+
+	return 0xffffffff;
+}
+#endif
+
+// find a file on our filesystems
+uint32 psf2_load_file(char *file, uint8 *buf, uint32 buflen)
+{
+	int i;
+	uint32 flen;
+
+	for (i = 0; i < num_fs; i++)
+	{
+		flen = load_file(i, file, buf, buflen);
+		if (flen != 0xffffffff)
+		{
+			return flen;
+		}
+	}
+
+	return 0xffffffff;
+}
+
+int32 psf2_start(uint8 *buffer, uint32 length)
+{
+	uint8 *file, *lib_decoded;
+	uint32 irx_len;
+	uint64 file_len, lib_raw_length, lib_len;
+	uint8 *buf;
+	union cpuinfo mipsinfo;
+	corlett_t *lib;
+
+	loadAddr = 0x23f00;	// this value makes allocations work out similarly to how they would 
+				// in Highly Experimental (as per Shadow Hearts' hard-coded assumptions)
+
+	// clear IOP work RAM before we start scribbling in it
+	memset(psx_ram, 0, 2*1024*1024);
+
+	// Decode the current PSF2
+	if (corlett_decode(buffer, length, &file, &file_len, &c) != AO_SUCCESS)
+	{
+		return AO_FAIL;
+	}
+
+	if (file_len > 0) printf("ERROR: PSF2 can't have a program section!  ps %08x\n", file_len);
+
+	#if DEBUG_LOADER
+	printf("FS section: size %x\n", c->res_size);
+	#endif
+
+	num_fs = 1;
+	filesys[0] = (uint8 *)c->res_section;
+	fssize[0] = c->res_size;
+
+	// Get the library file, if any
+	if (c->lib[0] != 0)
+	{
+		uint64 tmp_length;
+	
+		#if DEBUG_LOADER	
+		printf("Loading library: %s\n", c->lib);
+		#endif
+		if (ao_get_lib(c->lib, &lib_raw_file, &tmp_length) != AO_SUCCESS)
+		{
+			return AO_FAIL;
+		}
+		lib_raw_length = tmp_length;
+
+		if (corlett_decode(lib_raw_file, lib_raw_length, &lib_decoded, &lib_len, &lib) != AO_SUCCESS)
+		{
+			free(lib_raw_file);
+			return AO_FAIL;
+		}
+				
+		#if DEBUG_LOADER
+		printf("Lib FS section: size %x bytes\n", lib->res_size);
+		#endif
+
+		num_fs++;
+		filesys[1] = (uint8 *)lib->res_section;
+ 		fssize[1] = lib->res_size;
+	}
+
+	// dump all files
+	#if 0
+	buf = (uint8 *)malloc(16*1024*1024);
+	dump_files(0, buf, 16*1024*1024);
+	if (c->lib[0] != 0)
+		dump_files(1, buf, 16*1024*1024);
+	free(buf);
+	#endif
+
+	// load psf2.irx, which kicks everything off
+	buf = (uint8 *)malloc(512*1024);
+	irx_len = psf2_load_file("psf2.irx", buf, 512*1024);
+
+	if (irx_len != 0xffffffff)
+	{
+		initialPC = psf2_load_elf(buf, irx_len);
+		initialSP = 0x801ffff0;
+	}
+	free(buf);
+
+	if (initialPC == 0xffffffff)
+	{
+		return AO_FAIL;
+	}
+
+	lengthMS = psfTimeToMS(c->inf_length);
+	fadeMS = psfTimeToMS(c->inf_fade);
+	if (lengthMS == 0) 
+	{
+		lengthMS = ~0;
+	}
+	setlength2(lengthMS, fadeMS);
+
+	mips_init();
+	mips_reset(NULL);
+
+	mipsinfo.i = initialPC;
+	mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+	mipsinfo.i = initialSP;
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
+
+	// set RA
+	mipsinfo.i = 0x80000000;
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+
+	// set A0 & A1 to point to "aofile:/"
+	mipsinfo.i = 2;	// argc
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+
+	mipsinfo.i = 0x80000004;	// argv
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
+	psx_ram[1] = LE32(0x80000008);
+	
+	buf = (uint8 *)&psx_ram[2];
+	strcpy((char *)buf, "aofile:/");		
+
+	psx_ram[0] = LE32(FUNCT_HLECALL);
+
+	// back up initial RAM image to quickly restart songs
+	memcpy(initial_ram, psx_ram, 2*1024*1024);
+
+	psx_hw_init();
+	SPU2init();
+	SPU2open(NULL);
+
+	return AO_SUCCESS;
+}
+
+int32 psf2_execute(InputPlayback *playback)
+{	
+	int i;
+
+	while (playback->playing && !playback->eof)
+	{
+		for (i = 0; i < 44100 / 60; i++)
+		{
+			SPU2async(1, playback);
+			ps2_hw_slice();
+		}
+
+		ps2_hw_frame();
+	}
+
+	return AO_SUCCESS;
+}
+
+int32 psf2_stop(void)
+{
+	SPU2close();
+	if (c->lib[0] != 0)
+	{
+		free(lib_raw_file);
+	}
+	free(c);
+
+	return AO_SUCCESS;
+}
+
+int32 psf2_command(int32 command, int32 parameter)
+{
+	union cpuinfo mipsinfo;
+	uint32 lengthMS, fadeMS;
+
+	switch (command)
+	{
+		case COMMAND_RESTART:
+			SPU2close();
+
+			memcpy(psx_ram, initial_ram, 2*1024*1024);
+
+			mips_init();
+			mips_reset(NULL);
+			psx_hw_init();
+			SPU2init();
+			SPU2open(NULL);
+
+			mipsinfo.i = initialPC;
+			mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+			mipsinfo.i = initialSP;
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
+
+			// set RA
+			mipsinfo.i = 0x80000000;
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+
+			// set A0 & A1 to point to "aofile:/"
+			mipsinfo.i = 2;	// argc
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+
+			mipsinfo.i = 0x80000004;	// argv
+			mips_set_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
+
+			psx_hw_init();
+
+			lengthMS = psfTimeToMS(c->inf_length);
+			fadeMS = psfTimeToMS(c->inf_fade);
+			if (lengthMS == 0) 
+			{
+				lengthMS = ~0;
+			}
+			setlength2(lengthMS, fadeMS);
+
+			return AO_SUCCESS;
+		
+	}
+	return AO_FAIL;
+}
+
+uint32 psf2_get_loadaddr(void)
+{
+	return loadAddr;
+}
+
+void psf2_set_loadaddr(uint32 new)
+{
+	loadAddr = new;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/eng_spu.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,307 @@
+/*
+	Audio Overload SDK - SPU file format engine
+
+	Copyright (c) 2007 R. Belmont and Richard Bannister.
+
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+	* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+//
+// eng_spu.c
+//
+// Note: support for old-format files is not tested and may not work.  All the rips I could find
+//       are in the newer format.  Also, CDDA and XA commands do not work - I've not found a rip using them.
+//
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "ao.h"
+#include "eng_protos.h"
+#include "cpuintrf.h"
+#include "psx.h"
+
+extern int SPUinit(void);
+extern int SPUopen(void);
+extern int SPUclose(void);
+extern void SPUinjectRAMImage(unsigned short *source);
+
+static uint8 *start_of_file, *song_ptr;
+static uint32 cur_tick, cur_event, num_events, next_tick, end_tick;
+static int old_fmt;
+static char name[128], song[128], company[128];
+
+int32 spu_start(uint8 *buffer, uint32 length)
+{
+	int i;
+	uint16 reg;
+
+	if (strncmp((char *)buffer, "SPU", 3))
+	{
+		return AO_FAIL;
+	}
+
+	start_of_file = buffer;
+
+	SPUinit();
+	SPUopen();
+	setlength(~0, 0);
+
+	// upload the SPU RAM image
+	SPUinjectRAMImage((unsigned short *)&buffer[0]);
+
+	// apply the register image	
+	for (i = 0; i < 512; i += 2)
+	{
+		reg = buffer[0x80000+i] | buffer[0x80000+i+1]<<8;
+
+		SPUwriteRegister((i/2)+0x1f801c00, reg);
+	}
+
+	old_fmt = 1;
+
+	if ((buffer[0x80200] != 0x44) || (buffer[0x80201] != 0xac) || (buffer[0x80202] != 0x00) || (buffer[0x80203] != 0x00))
+	{
+		old_fmt = 0;
+	}
+
+	if (old_fmt)
+	{
+		num_events = buffer[0x80204] | buffer[0x80205]<<8 | buffer[0x80206]<<16 | buffer[0x80207]<<24;
+
+		if (((num_events * 12) + 0x80208) > length)
+		{
+			old_fmt = 0;
+		}
+		else
+		{
+			cur_tick = 0;
+		}
+	}
+
+	if (!old_fmt)
+	{
+		end_tick = buffer[0x80200] | buffer[0x80201]<<8 | buffer[0x80202]<<16 | buffer[0x80203]<<24; 
+		cur_tick = buffer[0x80204] | buffer[0x80205]<<8 | buffer[0x80206]<<16 | buffer[0x80207]<<24; 
+		next_tick = cur_tick;
+	}
+
+	song_ptr = &buffer[0x80208];
+	cur_event = 0;
+
+	strncpy((char *)&buffer[4], name, 128);
+	strncpy((char *)&buffer[0x44], song, 128);
+	strncpy((char *)&buffer[0x84], company, 128);
+
+	return AO_SUCCESS;
+}
+
+extern int SPUasync(uint32 cycles);
+extern void SPU_flushboot(void);
+
+extern char *spu_pOutput;	// this is a bit lame, but we'll deal
+
+static void spu_tick(void)
+{
+	uint32 time, reg, size;
+	uint16 rdata;
+	uint8 opcode;
+
+	if (old_fmt)
+	{
+		time = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24;
+
+		while ((time == cur_tick) && (cur_event < num_events))
+		{
+			reg = song_ptr[4] | song_ptr[5]<<8 | song_ptr[6]<<16 | song_ptr[7]<<24;
+			rdata = song_ptr[8] | song_ptr[9]<<8;
+
+			SPUwriteRegister(reg, rdata);
+
+			cur_event++;
+			song_ptr += 12;
+
+			time = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24;
+		}
+	}
+	else
+	{
+		if (cur_tick < end_tick)
+		{
+			while (cur_tick == next_tick)
+			{
+				opcode = song_ptr[0];
+				song_ptr++;
+
+				switch (opcode)
+				{
+					case 0:	// write register
+						reg = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24;
+						rdata = song_ptr[4] | song_ptr[5]<<8;
+
+						SPUwriteRegister(reg, rdata);
+
+						next_tick = song_ptr[6] | song_ptr[7]<<8 | song_ptr[8]<<16 | song_ptr[9]<<24; 
+						song_ptr += 10;
+						break;
+
+					case 1:	// read register
+				 		reg = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24;
+						SPUreadRegister(reg);
+						next_tick = song_ptr[4] | song_ptr[5]<<8 | song_ptr[6]<<16 | song_ptr[7]<<24; 
+						song_ptr += 8;
+						break;
+
+					case 2: // dma write
+						size = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24; 
+						song_ptr += (4 + size);
+						next_tick = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24; 
+						song_ptr += 4;
+						break;
+
+					case 3: // dma read
+						next_tick = song_ptr[4] | song_ptr[5]<<8 | song_ptr[6]<<16 | song_ptr[7]<<24; 
+						song_ptr += 8;
+						break;
+
+					case 4: // xa play
+						song_ptr += (32 + 16384);
+						next_tick = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24; 
+						song_ptr += 4;
+						break;
+
+					case 5: // cdda play
+						size = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24; 
+						song_ptr += (4 + size);
+						next_tick = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24; 
+						song_ptr += 4;
+						break;
+
+					default:
+						printf("Unknown opcode %d\n", opcode);
+						exit(-1);
+						break;
+				}
+			}
+		}
+		else
+		{
+//			ao_song_done = 1;
+		}
+	}
+
+	cur_tick++;
+}
+
+int32 spu_gen(int16 *buffer, uint32 samples)
+{
+	int i, run = 1;
+
+	if (old_fmt)
+	{
+		if (cur_event >= num_events)
+		{
+			run = 0;
+		}
+	}
+	else
+	{
+		if (cur_tick >= end_tick)
+		{
+			run = 0;
+		}
+	}
+
+	if (run)
+	{
+		for (i = 0; i < samples; i++)
+		{
+		  	spu_tick();
+			SPUasync(384);
+		}
+
+		spu_pOutput = (char *)buffer;
+		SPU_flushboot();
+	}
+	else
+	{
+		memset(buffer, 0, samples*2*sizeof(int16));
+	}
+
+	return AO_SUCCESS;
+}
+
+int32 spu_stop(void)
+{
+	return AO_SUCCESS;
+}
+
+int32 spu_command(int32 command, int32 parameter)
+{
+	switch (command)
+	{
+		case COMMAND_GET_MIN:
+		case COMMAND_GET_MAX:
+		{
+			return 0;
+		}
+		break;
+		
+		case COMMAND_HAS_PREV:
+		case COMMAND_HAS_NEXT:
+		case COMMAND_PREV:
+		case COMMAND_NEXT:
+		case COMMAND_JUMP:
+		{
+			return AO_FAIL;
+		}
+		break;
+		
+		case COMMAND_RESTART:
+		{
+			song_ptr = &start_of_file[0x80200];
+
+			if (old_fmt)
+			{
+				num_events = song_ptr[4] | song_ptr[5]<<8 | song_ptr[6]<<16 | song_ptr[7]<<24;
+			}
+			else
+			{
+				end_tick = song_ptr[0] | song_ptr[1]<<8 | song_ptr[2]<<16 | song_ptr[3]<<24; 
+				cur_tick = song_ptr[4] | song_ptr[5]<<8 | song_ptr[6]<<16 | song_ptr[7]<<24; 
+			}
+
+			song_ptr += 8;
+			cur_event = 0;
+			return AO_SUCCESS;
+		}
+		break;
+		
+#if VERBOSE
+		default:
+			printf("Unknown command executed!\n");
+			break;
+#endif		
+	}
+	
+	return AO_FAIL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/license.txt	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,236 @@
+Here is a list of the licenses each file in this package are under, and their text.
+
+The upshot is this: if you create anything from this PSFSDK and include any MAME licensed components
+(which are an integral part of each engine supplied) you must distribute the source code to your
+derivative program.
+
+
+=====================================================================================
+
+BSD license:
+
+Copyright (c) 2007-2008, R. Belmont and Richard Bannister.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=====================================================================================
+
+MAME license:
+
+Copyright (c) 1997-2008, Nicola Salmoria and the MAME team
+All rights reserved.
+
+Redistribution and use of this code or any derivative works are permitted provided that the following conditions are met:
+
+    * Redistributions may not be sold, nor may they be used in a commercial product or activity.
+    * Redistributions that are modified from the original source must include the complete source code, including the source code for all components used by a binary built from the modified sources. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+    * Redistributions must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=====================================================================================
+
+GPL license:
+
+Too long to include here.
+
+See eng_psf/peops/License.txt or eng_psf/peops2/License.txt for the complete text.
+
+While legally combining GPL with BSD and MAME licensed software is unclear, Pete Bernert 
+will probably be OK with anything you do as long as you distribute the source for your
+AOSDK based creations.
+										  
+=====================================================================================
+
+ZLIB license:
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.2, October 3rd, 2004
+
+  Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly jloup@gzip.org
+  Mark Adler madler@alumni.caltech.edu
+
+*/
+
+=====================================================================================
+
+List of files and their licenses:
+
+ao.h			BSD
+corlett.c		BSD 
+corlett.h		BSD 
+cpuintrf.h		BSD 
+dsnd.c			BSD 
+eng_protos.h		BSD 
+main.c			BSD 
+Makefile		BSD 
+mem.h			BSD 
+osd_cpu.h		MAME
+oss.c			BSD 
+oss.h			BSD 
+
+eng_dsf:
+
+aica.c			MAME
+aica.h			MAME
+aicadsp.c		MAME
+aicadsp.h		MAME
+aicalfo.c		MAME
+arm7.c			MAME
+arm7.h			MAME
+arm7i.c			MAME
+arm7i.h			MAME
+arm7memil.c		MAME
+eng_dsf.c		BSD
+dc_hw.c			BSD
+dc_hw.h			BSD
+
+eng_qsf:
+
+eng_qsf.c		BSD
+kabuki.c		MAME
+qsound.c		MAME
+qsound.h		MAME
+z80.c			MAME
+z80dasm.c		MAME
+z80dasm.h		MAME
+z80.h			MAME
+
+eng_ssf:
+
+eng_ssf.c		BSD
+m68kconf.h		MAME
+m68kcpu.c		MAME
+m68kcpu.h		MAME
+m68k.h			MAME
+m68k_in.c		MAME
+m68kmake.c		MAME
+m68kmame.h		MAME
+m68kopac.c		MAME
+m68kopdm.c		MAME
+m68kopnz.c		MAME
+m68kops.c		MAME
+m68kops.h		MAME
+sat_hw.c		BSD
+sat_hw.h		BSD
+scsp.c			MAME
+scspdsp.c		MAME
+scspdsp.h		MAME
+scsp.h			MAME
+scsplfo.c		MAME
+
+eng_psf:
+
+eng_psf2.c		BSD
+eng_psf.c		BSD
+eng_spu.c		BSD
+psx_hw.c		BSD
+psx.c			MAME
+psx.h			MAME
+cpuintrf.h		MAME
+mamemem.h		MAME
+
+eng_psf/peops:
+
+adsr.c			GPL
+adsr.h			GPL
+dma.c			GPL
+dma.h			GPL
+externals.h		GPL
+gauss_i.h		GPL
+registers.c		GPL
+registers.h		GPL
+regs.h			GPL
+reverb.c		GPL
+spu.c			GPL
+spu.h			GPL
+stdafx.h		GPL
+
+
+eng_psf/peops2:
+
+adsr.c			GPL
+adsr.h			GPL
+dma.c			GPL
+dma.h			GPL
+externals.h		GPL
+gauss_i.h		GPL
+psemuxa.h		GPL
+registers.c		GPL
+registers.h		GPL
+regs.h			GPL
+reverb.c		GPL
+reverb.h		GPL
+spu.c			GPL
+spu.h			GPL
+stdafx.h		GPL
+xa.c			GPL
+
+
+
+zlib:
+
+adler32.c		ZLIB
+compress.c		ZLIB
+crc32.c			ZLIB
+crc32.h			ZLIB
+crypt.h			ZLIB
+deflate.c		ZLIB
+deflate.h		ZLIB
+gzio.c			ZLIB
+infback.c		ZLIB
+inffast.c		ZLIB
+inffast.h		ZLIB
+inffixed.h		ZLIB
+inflate.c		ZLIB
+inflate.h		ZLIB
+inftrees.c		ZLIB
+inftrees.h		ZLIB
+ioapi.c			ZLIB
+ioapi.h			ZLIB
+trees.c			ZLIB
+trees.h			ZLIB
+uncompr.c		ZLIB
+unzip.c			ZLIB
+unzip.h			ZLIB
+zconf.h			ZLIB
+zlib.h			ZLIB
+zutil.c			ZLIB
+zutil.h			ZLIB
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/mamemem.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,662 @@
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __GNU__
+#define UNUSEDARG __attribute__((__unused__))
+#else
+#define UNUSEDARG
+#endif
+
+#define MAX_BANKS		20
+
+
+/* obsolete, to be removed */
+#define READ_WORD(a)		  (*(UINT16 *)(a))
+#define WRITE_WORD(a,d) 	  (*(UINT16 *)(a) = (d))
+#define COMBINE_WORD(w,d)	  (((w) & ((d) >> 16)) | ((d) & 0xffff))
+#define COMBINE_WORD_MEM(a,d) (WRITE_WORD((a), (READ_WORD(a) & ((d) >> 16)) | (d)))
+
+#define ASSERT_LINE (1)
+#define CLEAR_LINE  (0)
+#define TIME_NEVER (0)
+
+#define TIME_IN_HZ(hz)        (1.0 / (double)(hz))
+#define TIME_IN_MSEC(ms)      ((double)(ms) * (1.0 / 1000.0))
+#define TIME_IN_USEC(us)      ((double)(us) * (1.0 / 1000000.0))
+
+/***************************************************************************
+
+Note that the memory hooks are not passed the actual memory address where
+the operation takes place, but the offset from the beginning of the block
+they are assigned to. This makes handling of mirror addresses easier, and
+makes the handlers a bit more "object oriented". If you handler needs to
+read/write the main memory area, provide a "base" pointer: it will be
+initialized by the main engine to point to the beginning of the memory block
+assigned to the handler. You may also provided a pointer to "size": it
+will be set to the length of the memory area processed by the handler.
+
+***************************************************************************/
+
+#define MEMORY_WIDTH_MASK		0x00000003
+#define MEMORY_WIDTH_8			0x00000001
+#define MEMORY_WIDTH_16 		0x00000002
+#define MEMORY_WIDTH_32 		0x00000003
+
+#define MEMORY_TYPE_MASK		0x30000000
+#define MEMORY_TYPE_MEM 		0x10000000
+#define MEMORY_TYPE_IO			0x20000000
+
+#define MEMORY_DIRECTION_MASK   0xc0000000
+#define MEMORY_DIRECTION_READ   0x40000000
+#define MEMORY_DIRECTION_WRITE  0x80000000
+
+typedef unsigned int offs_t;
+typedef offs_t (*opbase_handler)(UNUSEDARG offs_t address);
+
+/***************************************************************************
+	8-BIT	Core memory read/write/opbase handler types
+***************************************************************************/
+
+typedef unsigned char data8_t;
+typedef unsigned short data16_t;
+
+typedef data8_t (*mem_read_handler)(UNUSEDARG offs_t offset);
+typedef void (*mem_write_handler)(UNUSEDARG offs_t offset, UNUSEDARG data8_t data);
+
+#define READ_HANDLER(name) data8_t name(UNUSEDARG offs_t offset)
+#define WRITE_HANDLER(name) void name(UNUSEDARG offs_t offset, UNUSEDARG data8_t data)
+#define OPBASE_HANDLER(name) offs_t name(UNUSEDARG offs_t address)
+
+#define READ16_HANDLER(name) data16_t name(UNUSEDARG offs_t offset, UNUSEDARG UINT32 mem_mask)
+#define WRITE16_HANDLER(name) void name(UNUSEDARG offs_t offset, UNUSEDARG data16_t data, UNUSEDARG UINT32 mem_mask)
+#define OPBASE16_HANDLER(name) offs_t name(UNUSEDARG offs_t address)
+
+#define MRA_NOP 0                           /* don't care, return 0 */
+#define MWA_NOP 0							/* do nothing */
+#define MRA_RAM ((mem_read_handler)-1)		/* plain RAM location (return its contents) */
+#define MWA_RAM ((mem_write_handler)-1) 	/* plain RAM location (store the value) */
+#define MRA_ROM ((mem_read_handler)-2)		/* plain ROM location (return its contents) */
+#define MWA_ROM ((mem_write_handler)-2) 	/* plain ROM location (do nothing) */
+/**************************************************************************
+ * If the CPU opcodes are encrypted, they are fetched from a different
+ * memory space. In such a case, if the program dynamically creates code
+ * in RAM and executes it, it won't work unless you use MWA_RAMROM
+ * to affect both memory spaces.
+ **************************************************************************/
+#define MWA_RAMROM ((mem_write_handler)-3)
+
+/* bank memory */
+#define MRA_BANK1 ((mem_read_handler)-10)
+#define MWA_BANK1 ((mem_write_handler)-10)
+#define MRA_BANK2 ((mem_read_handler)-11)
+#define MWA_BANK2 ((mem_write_handler)-11)
+#define MRA_BANK3 ((mem_read_handler)-12)
+#define MWA_BANK3 ((mem_write_handler)-12)
+#define MRA_BANK4 ((mem_read_handler)-13)
+#define MWA_BANK4 ((mem_write_handler)-13)
+#define MRA_BANK5 ((mem_read_handler)-14)
+#define MWA_BANK5 ((mem_write_handler)-14)
+#define MRA_BANK6 ((mem_read_handler)-15)
+#define MWA_BANK6 ((mem_write_handler)-15)
+#define MRA_BANK7 ((mem_read_handler)-16)
+#define MWA_BANK7 ((mem_write_handler)-16)
+#define MRA_BANK8 ((mem_read_handler)-17)
+#define MWA_BANK8 ((mem_write_handler)-17)
+#define MRA_BANK9 ((mem_read_handler)-18)
+#define MWA_BANK9 ((mem_write_handler)-18)
+#define MRA_BANK10 ((mem_read_handler)-19)
+#define MWA_BANK10 ((mem_write_handler)-19)
+#define MRA_BANK11 ((mem_read_handler)-20)
+#define MWA_BANK11 ((mem_write_handler)-20)
+#define MRA_BANK12 ((mem_read_handler)-21)
+#define MWA_BANK12 ((mem_write_handler)-21)
+#define MRA_BANK13 ((mem_read_handler)-22)
+#define MWA_BANK13 ((mem_write_handler)-22)
+#define MRA_BANK14 ((mem_read_handler)-23)
+#define MWA_BANK14 ((mem_write_handler)-23)
+#define MRA_BANK15 ((mem_read_handler)-24)
+#define MWA_BANK15 ((mem_write_handler)-24)
+#define MRA_BANK16 ((mem_read_handler)-25)
+#define MWA_BANK16 ((mem_write_handler)-25)
+#define MRA_BANK17 ((mem_read_handler)-26)
+#define MWA_BANK17 ((mem_write_handler)-26)
+#define MRA_BANK18 ((mem_read_handler)-27)
+#define MWA_BANK18 ((mem_write_handler)-27)
+#define MRA_BANK19 ((mem_read_handler)-28)
+#define MWA_BANK19 ((mem_write_handler)-28)
+#define MRA_BANK20 ((mem_read_handler)-29)
+#define MWA_BANK20 ((mem_write_handler)-29)
+
+struct Memory_ReadAddress
+{
+	offs_t start, end;
+	mem_read_handler handler;				/* see special values above */
+};
+
+struct Memory_WriteAddress
+{
+    offs_t start, end;
+	mem_write_handler handler;				/* see special values above */
+	data8_t **base;							/* optional (see explanation above) */
+    size_t *size;                           /* optional (see explanation above) */
+};
+
+#define MEMORY_MARKER ((offs_t)~0)
+
+#define MEMORY_END { MEMORY_MARKER, 0 } };
+
+#define IS_MEMORY_MARKER( ma ) ((ma)->start == MEMORY_MARKER && (ma)->end < MEMORY_MARKER)
+#define IS_MEMORY_END( ma ) ((ma)->start == MEMORY_MARKER && (ma)->end == 0)
+
+#define MEMORY_READ_START(name) const struct Memory_ReadAddress name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_READ | MEMORY_TYPE_MEM | MEMORY_WIDTH_8 },
+#define MEMORY_WRITE_START(name) const struct Memory_WriteAddress name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_WRITE | MEMORY_TYPE_MEM | MEMORY_WIDTH_8 },
+
+/***************************************************************************
+	16-BIT	Core memory read/write/opbase handler types
+***************************************************************************/
+
+typedef data16_t (*mem_read16_handler)(UNUSEDARG offs_t offset);
+typedef void (*mem_write16_handler)(UNUSEDARG offs_t offset, UNUSEDARG data16_t data, UNUSEDARG UINT32 mem_mask);
+
+#define READ16_HANDLER(name) data16_t name(UNUSEDARG offs_t offset, UNUSEDARG UINT32 mem_mask)
+#define WRITE16_HANDLER(name) void name(UNUSEDARG offs_t offset, UNUSEDARG data16_t data, UNUSEDARG UINT32 mem_mask)
+#define OPBASE16_HANDLER(name) offs_t name(UNUSEDARG offs_t address)
+
+#define MRA16_NOP 0 						/* don't care, return 0 */
+#define MWA16_NOP 0 						/* do nothing */
+#define MRA16_RAM ((mem_read16_handler)-1)  /* plain RAM location (return its contents) */
+#define MWA16_RAM ((mem_write16_handler)-1) /* plqain RAM location (store the value) */
+#define MRA16_ROM ((mem_read16_handler)-2)	/* plain ROM location (return its contents) */
+#define MWA16_ROM ((mem_write16_handler)-2) /* plain ROM location (do nothing) */
+/**************************************************************************
+ * If the CPU opcodes are encrypted, they are fetched from a different
+ * memory space. In such a case, if the program dynamically creates code
+ * in RAM and executes it, it won't work unless you use MWA_RAMROM
+ * to affect both memory spaces.
+ **************************************************************************/
+#define MWA16_RAMROM ((mem_write16_handler)-3)
+
+/* bank memory */
+#define MRA16_BANK1 ((mem_read16_handler)-10)
+#define MWA16_BANK1 ((mem_write16_handler)-10)
+#define MRA16_BANK2 ((mem_read16_handler)-11)
+#define MWA16_BANK2 ((mem_write16_handler)-11)
+#define MRA16_BANK3 ((mem_read16_handler)-12)
+#define MWA16_BANK3 ((mem_write16_handler)-12)
+#define MRA16_BANK4 ((mem_read16_handler)-13)
+#define MWA16_BANK4 ((mem_write16_handler)-13)
+#define MRA16_BANK5 ((mem_read16_handler)-14)
+#define MWA16_BANK5 ((mem_write16_handler)-14)
+#define MRA16_BANK6 ((mem_read16_handler)-15)
+#define MWA16_BANK6 ((mem_write16_handler)-15)
+#define MRA16_BANK7 ((mem_read16_handler)-16)
+#define MWA16_BANK7 ((mem_write16_handler)-16)
+#define MRA16_BANK8 ((mem_read16_handler)-17)
+#define MWA16_BANK8 ((mem_write16_handler)-17)
+#define MRA16_BANK9 ((mem_read16_handler)-18)
+#define MWA16_BANK9 ((mem_write16_handler)-18)
+#define MRA16_BANK10 ((mem_read16_handler)-19)
+#define MWA16_BANK10 ((mem_write16_handler)-19)
+#define MRA16_BANK11 ((mem_read16_handler)-20)
+#define MWA16_BANK11 ((mem_write16_handler)-20)
+#define MRA16_BANK12 ((mem_read16_handler)-21)
+#define MWA16_BANK12 ((mem_write16_handler)-21)
+#define MRA16_BANK13 ((mem_read16_handler)-22)
+#define MWA16_BANK13 ((mem_write16_handler)-22)
+#define MRA16_BANK14 ((mem_read16_handler)-23)
+#define MWA16_BANK14 ((mem_write16_handler)-23)
+#define MRA16_BANK15 ((mem_read16_handler)-24)
+#define MWA16_BANK15 ((mem_write16_handler)-24)
+#define MRA16_BANK16 ((mem_read16_handler)-25)
+#define MWA16_BANK16 ((mem_write16_handler)-25)
+#define MRA16_BANK17 ((mem_read16_handler)-26)
+#define MWA16_BANK17 ((mem_write16_handler)-26)
+#define MRA16_BANK18 ((mem_read16_handler)-27)
+#define MWA16_BANK18 ((mem_write16_handler)-27)
+#define MRA16_BANK19 ((mem_read16_handler)-28)
+#define MWA16_BANK19 ((mem_write16_handler)-28)
+#define MRA16_BANK20 ((mem_read16_handler)-29)
+#define MWA16_BANK20 ((mem_write16_handler)-29)
+
+struct Memory_ReadAddress16
+{
+	offs_t start, end;
+	mem_read16_handler handler; 			/* see special values above */
+};
+
+struct Memory_WriteAddress16
+{
+	offs_t start, end;
+	mem_write16_handler handler;			/* see special values above */
+	data16_t **base;						/* optional (see explanation above) */
+    size_t *size;                           /* optional (see explanation above) */
+};
+
+#define MEMORY_READ16_START(name) const struct Memory_ReadAddress16 name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_READ | MEMORY_TYPE_MEM | MEMORY_WIDTH_16 },
+#define MEMORY_WRITE16_START(name) const struct Memory_WriteAddress16 name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_WRITE | MEMORY_TYPE_MEM | MEMORY_WIDTH_16 },
+
+/***************************************************************************
+	32-BIT	Core memory read/write/opbase handler types
+***************************************************************************/
+
+typedef UINT32 data32_t;
+
+typedef data32_t (*mem_read32_handler)(UNUSEDARG offs_t offset);
+typedef void (*mem_write32_handler)(UNUSEDARG offs_t offset, UNUSEDARG data32_t data, UNUSEDARG UINT32 mem_mask);
+
+#define READ32_HANDLER(name) data32_t name(UNUSEDARG offs_t offset, UNUSEDARG UINT32 mem_mask)
+#define WRITE32_HANDLER(name) void name(UNUSEDARG offs_t offset, UNUSEDARG data32_t data, UNUSEDARG UINT32 mem_mask)
+#define OPBASE32_HANDLER(name) offs_t name(UNUSEDARG offs_t address)
+
+#define MRA32_NOP 0 						/* don't care, return 0 */
+#define MWA32_NOP 0 						/* do nothing */
+#define MRA32_RAM ((mem_read32_handler)-1)  /* plain RAM location (return its contents) */
+#define MWA32_RAM ((mem_write32_handler)-1) /* plain RAM location (store the value) */
+#define MRA32_ROM ((mem_read32_handler)-2)	/* plain ROM location (return its contents) */
+#define MWA32_ROM ((mem_write32_handler)-2) /* plain ROM location (do nothing) */
+/**************************************************************************
+ * If the CPU opcodes are encrypted, they are fetched from a different
+ * memory space. In such a case, if the program dynamically creates code
+ * in RAM and executes it, it won't work unless you use MWA_RAMROM
+ * to affect both memory spaces.
+ **************************************************************************/
+#define MWA32_RAMROM ((mem_write32_handler)-3)
+
+/* bank memory */
+#define MRA32_BANK1 ((mem_read32_handler)-10)
+#define MWA32_BANK1 ((mem_write32_handler)-10)
+#define MRA32_BANK2 ((mem_read32_handler)-11)
+#define MWA32_BANK2 ((mem_write32_handler)-11)
+#define MRA32_BANK3 ((mem_read32_handler)-12)
+#define MWA32_BANK3 ((mem_write32_handler)-12)
+#define MRA32_BANK4 ((mem_read32_handler)-13)
+#define MWA32_BANK4 ((mem_write32_handler)-13)
+#define MRA32_BANK5 ((mem_read32_handler)-14)
+#define MWA32_BANK5 ((mem_write32_handler)-14)
+#define MRA32_BANK6 ((mem_read32_handler)-15)
+#define MWA32_BANK6 ((mem_write32_handler)-15)
+#define MRA32_BANK7 ((mem_read32_handler)-16)
+#define MWA32_BANK7 ((mem_write32_handler)-16)
+#define MRA32_BANK8 ((mem_read32_handler)-17)
+#define MWA32_BANK8 ((mem_write32_handler)-17)
+#define MRA32_BANK9 ((mem_read32_handler)-18)
+#define MWA32_BANK9 ((mem_write32_handler)-18)
+#define MRA32_BANK10 ((mem_read32_handler)-19)
+#define MWA32_BANK10 ((mem_write32_handler)-19)
+#define MRA32_BANK11 ((mem_read32_handler)-20)
+#define MWA32_BANK11 ((mem_write32_handler)-20)
+#define MRA32_BANK12 ((mem_read32_handler)-21)
+#define MWA32_BANK12 ((mem_write32_handler)-21)
+#define MRA32_BANK13 ((mem_read32_handler)-22)
+#define MWA32_BANK13 ((mem_write32_handler)-22)
+#define MRA32_BANK14 ((mem_read32_handler)-23)
+#define MWA32_BANK14 ((mem_write32_handler)-23)
+#define MRA32_BANK15 ((mem_read32_handler)-24)
+#define MWA32_BANK15 ((mem_write32_handler)-24)
+#define MRA32_BANK32 ((mem_read32_handler)-25)
+#define MWA32_BANK32 ((mem_write32_handler)-25)
+#define MRA32_BANK17 ((mem_read32_handler)-26)
+#define MWA32_BANK17 ((mem_write32_handler)-26)
+#define MRA32_BANK18 ((mem_read32_handler)-27)
+#define MWA32_BANK18 ((mem_write32_handler)-27)
+#define MRA32_BANK19 ((mem_read32_handler)-28)
+#define MWA32_BANK19 ((mem_write32_handler)-28)
+#define MRA32_BANK20 ((mem_read32_handler)-29)
+#define MWA32_BANK20 ((mem_write32_handler)-29)
+
+struct Memory_ReadAddress32
+{
+	offs_t start, end;
+	mem_read32_handler handler; 			/* see special values above */
+};
+
+struct Memory_WriteAddress32
+{
+	offs_t start, end;
+	mem_write32_handler handler;			/* see special values above */
+	data32_t **base;						/* optional (see explanation above) */
+	size_t *size;							/* optional (see explanation above) */
+};
+
+#define MEMORY_READ32_START(name) const struct Memory_ReadAddress32 name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_READ | MEMORY_TYPE_MEM | MEMORY_WIDTH_32 },
+#define MEMORY_WRITE32_START(name) const struct Memory_WriteAddress32 name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_WRITE | MEMORY_TYPE_MEM | MEMORY_WIDTH_32 },
+
+/***************************************************************************
+
+IN and OUT ports are handled like memory accesses, the hook template is the
+same so you can interchange them. Of course there is no 'base' pointer for
+IO ports.
+
+***************************************************************************/
+
+struct IO_ReadPort
+{
+	offs_t start,end;
+	mem_read_handler handler;				/* see special values below */
+};
+
+#define IORP_NOP 0	/* don't care, return 0 */
+
+
+struct IO_WritePort
+{
+	offs_t start,end;
+	mem_write_handler handler;				/* see special values below */
+};
+
+#define IOWP_NOP 0	/* do nothing */
+
+#define PORT_READ_START(name) const struct IO_ReadPort name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_READ | MEMORY_TYPE_IO | MEMORY_WIDTH_8 },
+#define PORT_WRITE_START(name) const struct IO_WritePort name[] = { \
+	{ MEMORY_MARKER, MEMORY_DIRECTION_WRITE | MEMORY_TYPE_IO | MEMORY_WIDTH_8 },
+#define PORT_END	MEMORY_END
+
+/***************************************************************************
+
+If a memory region contains areas that are outside of the ROM region for
+an address space, the memory system will allocate an array of structures
+to track the external areas.
+
+***************************************************************************/
+
+#define MAX_EXT_MEMORY 64
+
+struct ExtMemory
+{
+	offs_t start,end,region;
+    UINT8 *data;
+};
+
+extern struct ExtMemory ext_memory[MAX_EXT_MEMORY];
+
+
+
+/***************************************************************************
+
+For a given number of address bits, we need to determine how many elements
+there are in the first and second-order lookup tables. We also need to know
+how many low-order bits to ignore. The ABITS* values represent these
+constants for each address space type we support.
+
+***************************************************************************/
+
+/* memory element block size */
+#define MH_SBITS		8			/* sub element bank size */
+#define MH_PBITS		8			/* port current element size */
+#define MH_ELEMAX		64			/* sub elements limit */
+#define MH_HARDMAX		64			/* hardware functions limit */
+
+/* 16 bits address */
+#define ABITS1_16		12
+#define ABITS2_16		4
+#define ABITS_MIN_16	0			/* minimum memory block is 1 byte */
+/* 16 bits address (word access) */
+#define ABITS1_16W	12
+#define ABITS2_16W	3
+#define ABITS_MIN_16W 1			/* minimum memory block is 2 bytes */
+/* 20 bits address */
+#define ABITS1_20		12
+#define ABITS2_20		8
+#define ABITS_MIN_20	0			/* minimum memory block is 1 byte */
+/* 21 bits address */
+#define ABITS1_21		13
+#define ABITS2_21		8
+#define ABITS_MIN_21	0			/* minimum memory block is 1 byte */
+/* 24 bits address */
+#define ABITS1_24		16
+#define ABITS2_24		8
+#define ABITS_MIN_24	0			/* minimum memory block is 1 byte */
+/* 24 bits address (word access) */
+#define ABITS1_24W	15
+#define ABITS2_24W	8
+#define ABITS_MIN_24W 1			/* minimum memory block is 2 bytes */
+/* 24 bits address (dword access) */
+#define ABITS1_24DW	14
+#define ABITS2_24DW	8
+#define ABITS_MIN_24DW 2			/* minimum memory block is 4 bytes */
+/* 26 bits address (dword access) */
+#define ABITS1_26DW	16
+#define ABITS2_26DW	8
+#define ABITS_MIN_26DW 2			/* minimum memory block is 4 bytes */
+/* 29 bits address (word access) */
+#define ABITS1_29W	20
+#define ABITS2_29W	8
+#define ABITS_MIN_29W	1			/* minimum memory block is 2 bytes */
+/* 32 bits address (word access) */
+#define ABITS1_32W	23
+#define ABITS2_32W	8
+#define ABITS_MIN_32W	1			/* minimum memory block is 2 bytes */
+/* 32 bits address (dword access) */
+#define ABITS1_32DW	22
+#define ABITS2_32DW	8
+#define ABITS_MIN_32DW	2			/* minimum memory block is 4 bytes */
+/* mask bits */
+#define MHMASK(abits)	 (0xffffffff >> (32 - abits))
+
+
+/***************************************************************************
+
+	Global variables
+
+***************************************************************************/
+
+typedef unsigned char MHELE;
+
+extern MHELE ophw;				/* opcode handler */
+extern MHELE *cur_mrhard;		/* current set of read handlers */
+extern MHELE *cur_mwhard;		/* current set of write handlers */
+
+extern UINT8 *OP_RAM;			/* opcode RAM base */
+extern UINT8 *OP_ROM;			/* opcode ROM base */
+extern UINT8 *cpu_bankbase[];	/* array of bank bases */
+
+
+/* global memory access width and mask (16-bit and 32-bit under-size accesses) */
+//extern UINT32 mem_width;
+//extern UINT32 mem_mask;
+//extern UINT32 mem_offs;
+
+/***************************************************************************
+
+	Macros
+
+***************************************************************************/
+
+/* ----- 16-bit memory accessing ----- */
+#define COMBINE_DATA(varptr)	(*(varptr) = (*(varptr) & mem_mask) | (data & ~mem_mask))
+#define ACCESSING_LSB ((mem_mask & 0x00ff) == 0)
+#define ACCESSING_MSB ((mem_mask & 0xff00) == 0)
+
+//extern unsigned char prgrom[128*1024];
+
+/* ----- opcode reading ----- */
+#define cpu_readop cpu_readmem16
+//#define cpu_readop16(A) 	READ_WORD(&prgrom[A&0x3fff])
+//#define cpu_readop32(A) 	READ_DWORD(&prgrom[A&0x3fff])
+
+/* ----- opcode argument reading ----- */
+#define cpu_readop_arg cpu_readmem16
+//#define cpu_readop_arg16(A) READ_WORD(&prgrom[A&0x3fff])
+//#define cpu_readop_arg32(A) READ_DWORD(&prgrom[A&0x3fff])
+
+/* ----- bank switching for CPU cores ----- */
+#define change_pc_generic(pc,abits2,abitsmin,shift,setop)	\
+{															\
+	if (cur_mrhard[(pc)>>(abits2+abitsmin+shift)] != ophw)	\
+		setop(pc);											\
+}
+#define change_pc16(pc) 	 
+//change_pc_generic(pc, ABITS2_16, ABITS_MIN_16, 0, cpu_setOPbase16)
+#define change_pc20(pc) 	 change_pc_generic(pc, ABITS2_20, ABITS_MIN_20, 0, cpu_setOPbase20)
+#define change_pc21(pc) 	 change_pc_generic(pc, ABITS2_21, ABITS_MIN_21, 0, cpu_setOPbase21)
+#define change_pc24(pc) 	 change_pc_generic(pc, ABITS2_24, ABITS_MIN_24, 0, cpu_setOPbase24)
+#define change_pc16bew(pc)	 change_pc_generic(pc, ABITS2_16W,   ABITS_MIN_16W,   0, cpu_setOPbase16bew)
+#define change_pc16lew(pc)	 change_pc_generic(pc, ABITS2_16W,   ABITS_MIN_16W,   0, cpu_setOPbase16lew)
+#define change_pc24bew(pc)	 change_pc_generic(pc, ABITS2_24W,   ABITS_MIN_24W,   0, cpu_setOPbase24bew)
+#define change_pc29lew(pc)   change_pc_generic(pc, ABITS2_29W,   ABITS_MIN_29W,   3, cpu_setOPbase29lew)
+#define change_pc32bew(pc) 	 change_pc_generic(pc, ABITS2_32W,   ABITS_MIN_32W,   0, cpu_setOPbase32bew)
+#define change_pc32lew(pc)	 change_pc_generic(pc, ABITS2_32W,   ABITS_MIN_32W,   0, cpu_setOPbase32lew)
+#define change_pc24bedw(pc)	 change_pc_generic(pc, ABITS2_24DW,  ABITS_MIN_24DW,  0, cpu_setOPbase24bedw)
+#define change_pc26ledw(pc)	 change_pc_generic(pc, ABITS2_26DW,  ABITS_MIN_26DW,  0, cpu_setOPbase26ledw)
+#define change_pc32bedw(pc)  change_pc_generic(pc, ABITS2_32DW,  ABITS_MIN_32DW,  0, cpu_setOPbase32bedw)
+
+/* backward compatibility */
+#define change_pc(pc)
+// change_pc16(pc)
+
+/* ----- for use OPbaseOverride driver, request override callback to next cpu_setOPbase ----- */
+#define catch_nextBranch()	(ophw = 0xff)
+
+/* -----  bank switching macro ----- */
+#define cpu_setbank(bank, base) 						\
+{														\
+	if (bank >= 1 && bank <= MAX_BANKS) 				\
+	{													\
+		cpu_bankbase[bank] = (UINT8 *)(base);			\
+		if (ophw == bank)								\
+		{												\
+			ophw = 0xff;								\
+			cpu_set_op_base(cpu_get_pc());				\
+		}												\
+	}													\
+}
+
+
+/***************************************************************************
+
+	Function prototypes
+
+***************************************************************************/
+
+/* ----- memory setup function ----- */
+
+/* ----- memory read functions ----- */
+data8_t cpu_readmem16(offs_t address);
+data8_t cpu_readmem20(offs_t address);
+data8_t cpu_readmem21(offs_t address);
+data8_t cpu_readmem24(offs_t address);
+
+data16_t cpu_readmem16bew(offs_t address);
+data16_t cpu_readmem16bew_word(offs_t address);
+data16_t cpu_readmem16lew(offs_t address);
+data16_t cpu_readmem16lew_word(offs_t address);
+data16_t cpu_readmem24bew(offs_t address);
+data16_t cpu_readmem24bew_word(offs_t address);
+data16_t cpu_readmem29lew(offs_t address);
+data16_t cpu_readmem29lew_word(offs_t address);
+data16_t cpu_readmem32bew(offs_t address);
+data16_t cpu_readmem32bew_word(offs_t address);
+data16_t cpu_readmem32lew(offs_t address);
+data16_t cpu_readmem32lew_word(offs_t address);
+
+data32_t cpu_readmem24bedw(offs_t address);
+data32_t cpu_readmem24bedw_word(offs_t address);
+data32_t cpu_readmem24bedw_dword(offs_t address);
+data32_t cpu_readmem26ledw(offs_t address);
+data32_t cpu_readmem26ledw_word(offs_t address);
+data32_t cpu_readmem26ledw_dword(offs_t address);
+data32_t cpu_readmem27bedw(offs_t address);
+data32_t cpu_readmem27bedw_word(offs_t address);
+data32_t cpu_readmem27bedw_dword(offs_t address);
+data32_t cpu_readmem32bedw(offs_t address);
+data32_t cpu_readmem32bedw_word(offs_t address);
+data32_t cpu_readmem32bedw_dword(offs_t address);
+
+/* ----- memory write functions ----- */
+void cpu_writemem16(offs_t address,data8_t data);
+void cpu_writemem20(offs_t address,data8_t data);
+void cpu_writemem21(offs_t address,data8_t data);
+void cpu_writemem24(offs_t address,data8_t data);
+
+void cpu_writemem16bew(offs_t address,data16_t data);
+void cpu_writemem16bew_word(offs_t address,data16_t data);
+void cpu_writemem16lew(offs_t address,data16_t data);
+void cpu_writemem16lew_word(offs_t address,data16_t data);
+void cpu_writemem24bew(offs_t address,data16_t data);
+void cpu_writemem24bew_word(offs_t address,data16_t data);
+void cpu_writemem29lew(offs_t address,data16_t data);
+void cpu_writemem29lew_word(offs_t address,data16_t data);
+void cpu_writemem32bew(offs_t address,data16_t data);
+void cpu_writemem32bew_word(offs_t address,data16_t data);
+void cpu_writemem32lew(offs_t address,data16_t data);
+void cpu_writemem32lew_word(offs_t address,data16_t data);
+
+void cpu_writemem24bedw(offs_t address,data32_t data);
+void cpu_writemem24bedw_word(offs_t address,data32_t data);
+void cpu_writemem24bedw_dword(offs_t address,data32_t data);
+void cpu_writemem26ledw(offs_t address,data32_t data);
+void cpu_writemem26ledw_word(offs_t address,data32_t data);
+void cpu_writemem26ledw_dword(offs_t address,data32_t data);
+void cpu_writemem27bedw(offs_t address,data32_t data);
+void cpu_writemem27bedw_word(offs_t address,data32_t data);
+void cpu_writemem27bedw_dword(offs_t address,data32_t data);
+void cpu_writemem32bedw(offs_t address,data32_t data);
+void cpu_writemem32bedw_word(offs_t address,data32_t data);
+void cpu_writemem32bedw_dword(offs_t address,data32_t data);
+
+/* ----- port I/O functions ----- */
+int cpu_readport(int port);
+void cpu_writeport(int port, int value);
+
+/* ----- dynamic memory/port mapping ----- */
+void *install_mem_read_handler(int cpu, int start, int end, mem_read_handler handler);
+void *install_mem_read16_handler(int cpu, int start, int end, mem_read16_handler handler);
+void *install_mem_read32_handler(int cpu, int start, int end, mem_read32_handler handler);
+void *install_mem_write_handler(int cpu, int start, int end, mem_write_handler handler);
+void *install_mem_write16_handler(int cpu, int start, int end, mem_write16_handler handler);
+void *install_mem_write32_handler(int cpu, int start, int end, mem_write32_handler handler);
+void *install_port_read_handler(int cpu, int start, int end, mem_read_handler handler);
+void *install_port_write_handler(int cpu, int start, int end, mem_write_handler handler);
+
+/* ----- dynamic bank handlers ----- */
+void cpu_setbankhandler_r(int bank, mem_read_handler handler);
+void cpu_setbankhandler_w(int bank, mem_write_handler handler);
+
+/* ----- opcode base control ---- */
+void cpu_setOPbase16(offs_t pc);
+void cpu_setOPbase20(offs_t pc);
+void cpu_setOPbase21(offs_t pc);
+void cpu_setOPbase24(offs_t pc);
+void cpu_setOPbase16bew(offs_t pc);
+void cpu_setOPbase16lew(offs_t pc);
+void cpu_setOPbase24bew(offs_t pc);
+void cpu_setOPbase24bedw(offs_t pc);
+void cpu_setOPbase26ledw(offs_t pc);
+void cpu_setOPbase29lew(offs_t pc);
+void cpu_setOPbase32bew(offs_t pc);
+void cpu_setOPbase32lew(offs_t pc);
+void cpu_setOPbaseoverride(int cpu, opbase_handler function);
+
+/* ----- harder-to-explain functions ---- */
+
+/* use this to set the a different opcode base address when using a CPU with
+   opcodes and data encrypted separately */
+void memory_set_opcode_base(int cpu, void *base);
+
+/* look up a chunk of memory and get its start/end addresses, and its base.
+Pass in the cpu number and the offset. It will find the chunk containing
+that offset and return the start and end addresses, along with a pointer to
+the base of the memory.
+This can be used (carefully!) by drivers that wish to access memory directly
+without going through the readmem/writemem accessors (e.g., blitters). */
+void *findmemorychunk(int cpu, int offset, int *chunkstart, int *chunkend);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* !_MEMORY_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/osd_cpu.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,70 @@
+/*******************************************************************************
+*																			   *
+*	Define size independent data types and operations.						   *
+*																			   *
+*   The following types must be supported by all platforms:					   *
+*																			   *
+*	UINT8  - Unsigned 8-bit Integer		INT8  - Signed 8-bit integer           *
+*	UINT16 - Unsigned 16-bit Integer	INT16 - Signed 16-bit integer          *
+*	UINT32 - Unsigned 32-bit Integer	INT32 - Signed 32-bit integer          *
+*	UINT64 - Unsigned 64-bit Integer	INT64 - Signed 64-bit integer          *
+*																			   *
+*																			   *
+*   The macro names for the artithmatic operations are composed as follows:    *
+*																			   *
+*   XXX_R_A_B, where XXX - 3 letter operation code (ADD, SUB, etc.)			   *
+*					 R   - The type	of the result							   *
+*					 A   - The type of operand 1							   *
+*			         B   - The type of operand 2 (if binary operation)		   *
+*																			   *
+*				     Each type is one of: U8,8,U16,16,U32,32,U64,64			   *
+*																			   *
+*******************************************************************************/
+
+
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+
+#include "ao.h"
+
+/* Combine two 32-bit integers into a 64-bit integer */
+#define COMBINE_64_32_32(A,B)     ((((UINT64)(A))<<32) | (UINT32)(B))
+#define COMBINE_U64_U32_U32(A,B)  COMBINE_64_32_32(A,B)
+
+/* Return upper 32 bits of a 64-bit integer */
+#define HI32_32_64(A)		  (((UINT64)(A)) >> 32)
+#define HI32_U32_U64(A)		  HI32_32_64(A)
+
+/* Return lower 32 bits of a 64-bit integer */
+#define LO32_32_64(A)		  ((A) & 0xffffffff)
+#define LO32_U32_U64(A)		  LO32_32_64(A)
+
+#define DIV_64_64_32(A,B)	  ((A)/(B))
+#define DIV_U64_U64_U32(A,B)  ((A)/(UINT32)(B))
+
+#define MOD_32_64_32(A,B)	  ((A)%(B))
+#define MOD_U32_U64_U32(A,B)  ((A)%(UINT32)(B))
+
+#define MUL_64_32_32(A,B)	  ((A)*(INT64)(B))
+#define MUL_U64_U32_U32(A,B)  ((A)*(UINT64)(UINT32)(B))
+
+
+/******************************************************************************
+ * Union of UINT8, UINT16 and UINT32 in native endianess of the target
+ * This is used to access bytes and words in a machine independent manner.
+ * The upper bytes h2 and h3 normally contain zero (16 bit CPU cores)
+ * thus PAIR.d can be used to pass arguments to the memory system
+ * which expects 'int' really.
+ ******************************************************************************/
+typedef union {
+#if LSB_FIRST
+	struct { UINT8 l,h,h2,h3; } b;
+	struct { UINT16 l,h; } w;
+#else
+	struct { UINT8 h3,h2,h,l; } b;
+	struct { UINT16 h,l; } w;
+#endif
+	UINT32 d;
+}	PAIR;
+
+#endif	/* defined OSD_CPU_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/License.txt	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,282 @@
+#########################################################################
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/adsr.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,618 @@
+/***************************************************************************
+                          adsr.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2003/01/06 - Pete
+// - added Neill's ADSR timings
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#define _IN_ADSR
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// ADSR func
+////////////////////////////////////////////////////////////////////////
+
+static u32 RateTable[160];
+
+static void InitADSR(void)                                    // INIT ADSR
+{
+ u32 r,rs,rd;int i;
+
+ memset(RateTable,0,sizeof(u32)*160);        // build the rate table according to Neill's rules (see at bottom of file)
+
+ r=3;rs=1;rd=0;
+
+ for(i=32;i<160;i++)                                   // we start at pos 32 with the real values... everything before is 0
+  {
+   if(r<0x3FFFFFFF)
+    {
+     r+=rs;
+     rd++;if(rd==5) {rd=1;rs*=2;}
+    }
+   if(r>0x3FFFFFFF) r=0x3FFFFFFF;
+
+   RateTable[i]=r;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+static INLINE void StartADSR(int ch)                          // MIX ADSR
+{
+ s_chan[ch].ADSRX.lVolume=1;                           // and init some adsr vars
+ s_chan[ch].ADSRX.State=0;
+ s_chan[ch].ADSRX.EnvelopeVol=0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+static INLINE int MixADSR(int ch)                             // MIX ADSR
+{    
+ static const int sexytable[8]=
+	{0,4,6,8,9,10,11,12};
+
+ if(s_chan[ch].bStop)                                  // should be stopped:
+  {                                                    // do release
+   if(s_chan[ch].ADSRX.ReleaseModeExp)
+    {
+     s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18+32+sexytable[(s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7]];
+    }
+   else
+    {
+     s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x0C + 32];
+    }
+
+   if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+    {
+     s_chan[ch].ADSRX.EnvelopeVol=0;
+     s_chan[ch].bOn=0;
+     s_chan[ch].bNoise=0;
+    }
+
+   s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+   return s_chan[ch].ADSRX.lVolume;
+  }
+ else                                                  // not stopped yet?
+  {
+   if(s_chan[ch].ADSRX.State==0)                       // -> attack
+    {
+     if(s_chan[ch].ADSRX.AttackModeExp)
+      {
+       if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) 
+        s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32];
+       else
+        s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x18 + 32];
+      }
+     else
+      {
+       s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32];
+      }
+
+     if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+      {
+       s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF;
+       s_chan[ch].ADSRX.State=1;
+      }
+
+     s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+     return s_chan[ch].ADSRX.lVolume;
+    }
+   //--------------------------------------------------//
+   if(s_chan[ch].ADSRX.State==1)                       // -> decay
+    {
+     s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+32+sexytable[(s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7]];
+
+     if(s_chan[ch].ADSRX.EnvelopeVol<0) s_chan[ch].ADSRX.EnvelopeVol=0;
+     if(((s_chan[ch].ADSRX.EnvelopeVol>>27)&0xF) <= s_chan[ch].ADSRX.SustainLevel)
+      {
+       s_chan[ch].ADSRX.State=2;
+      }
+
+     s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+     return s_chan[ch].ADSRX.lVolume;
+    }
+   //--------------------------------------------------//
+   if(s_chan[ch].ADSRX.State==2)                       // -> sustain
+    {
+     if(s_chan[ch].ADSRX.SustainIncrease)
+      {
+       if(s_chan[ch].ADSRX.SustainModeExp)
+        {
+         if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) 
+          s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32];
+         else
+          s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x18 + 32];
+        }
+       else
+        {
+         s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32];
+        }
+
+       if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+        {
+         s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF;
+        }
+      }
+     else
+      {
+       if(s_chan[ch].ADSRX.SustainModeExp)
+        s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B+32+sexytable[(s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7]];
+       else
+        s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x0F + 32];
+
+       if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+        {
+         s_chan[ch].ADSRX.EnvelopeVol=0;
+        }
+      }
+     s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+     return s_chan[ch].ADSRX.lVolume;
+    }
+  }
+ return 0;
+}
+
+#endif
+
+/*
+James Higgs ADSR investigations:
+
+PSX SPU Envelope Timings
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+First, here is an extract from doomed's SPU doc, which explains the basics
+of the SPU "volume envelope": 
+
+*** doomed doc extract start ***
+
+--------------------------------------------------------------------------
+Voices.
+--------------------------------------------------------------------------
+The SPU has 24 hardware voices. These voices can be used to reproduce sample
+data, noise or can be used as frequency modulator on the next voice.
+Each voice has it's own programmable ADSR envelope filter. The main volume
+can be programmed independently for left and right output.
+
+The ADSR envelope filter works as follows:
+Ar = Attack rate, which specifies the speed at which the volume increases
+     from zero to it's maximum value, as soon as the note on is given. The
+     slope can be set to lineair or exponential.
+Dr = Decay rate specifies the speed at which the volume decreases to the
+     sustain level. Decay is always decreasing exponentially.
+Sl = Sustain level, base level from which sustain starts.
+Sr = Sustain rate is the rate at which the volume of the sustained note
+     increases or decreases. This can be either lineair or exponential.
+Rr = Release rate is the rate at which the volume of the note decreases
+     as soon as the note off is given.
+
+     lvl |
+       ^ |     /\Dr     __
+     Sl _| _  / _ \__---  \
+         |   /       ---__ \ Rr
+         |  /Ar       Sr  \ \
+         | /                \\
+         |/___________________\________
+                                  ->time
+
+The overal volume can also be set to sweep up or down lineairly or
+exponentially from it's current value. This can be done seperately
+for left and right.
+
+Relevant SPU registers:
+-------------------------------------------------------------
+$1f801xx8         Attack/Decay/Sustain level
+bit  |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|
+desc.|Am|         Ar         |Dr         |Sl         |
+
+Am       0        Attack mode Linear
+         1                    Exponential
+
+Ar       0-7f     attack rate
+Dr       0-f      decay rate
+Sl       0-f      sustain level
+-------------------------------------------------------------
+$1f801xxa         Sustain rate, Release Rate.
+bit  |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|
+desc.|Sm|Sd| 0|   Sr               |Rm|Rr            |
+
+Sm       0        sustain rate mode linear
+         1                          exponential
+Sd       0        sustain rate mode increase
+         1                          decrease
+Sr       0-7f     Sustain Rate
+Rm       0        Linear decrease
+         1        Exponential decrease
+Rr       0-1f     Release Rate
+
+Note: decay mode is always Expontial decrease, and thus cannot
+be set.
+-------------------------------------------------------------
+$1f801xxc         Current ADSR volume
+bit  |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|
+desc.|ADSRvol                                        |
+
+ADSRvol           Returns the current envelope volume when
+                  read.
+-- James' Note: return range: 0 -> 32767
+
+*** doomed doc extract end *** 
+
+By using a small PSX proggie to visualise the envelope as it was played,
+the following results for envelope timing were obtained:
+
+1. Attack rate value (linear mode)
+
+   Attack value range: 0 -> 127
+
+   Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |    | 80 |
+   -----------------------------------------------------------------
+   Frames | 11 | 21 | 42 | 84 | 169| 338| 676|    |2890|
+
+   Note: frames is no. of PAL frames to reach full volume (100%
+   amplitude)
+
+   Hmm, noticing that the time taken to reach full volume doubles
+   every time we add 4 to our attack value, we know the equation is
+   of form:
+             frames = k * 2 ^ (value / 4)
+
+   (You may ponder about envelope generator hardware at this point,
+   or maybe not... :)
+
+   By substituting some stuff and running some checks, we get:
+
+       k = 0.00257              (close enuf)
+
+   therefore,
+             frames = 0.00257 * 2 ^ (value / 4)
+   If you just happen to be writing an emulator, then you can probably
+   use an equation like:
+
+       %volume_increase_per_tick = 1 / frames
+
+
+   ------------------------------------
+   Pete:
+   ms=((1<<(value>>2))*514)/10000
+   ------------------------------------
+
+2. Decay rate value (only has log mode)
+
+   Decay value range: 0 -> 15
+
+   Value  |  8 |  9 | 10 | 11 | 12 | 13 | 14 | 15 |
+   ------------------------------------------------
+   frames |    |    |    |    |  6 | 12 | 24 | 47 |
+
+   Note: frames here is no. of PAL frames to decay to 50% volume.
+
+   formula: frames = k * 2 ^ (value)
+
+   Substituting, we get: k = 0.00146
+
+   Further info on logarithmic nature:
+   frames to decay to sustain level 3  =  3 * frames to decay to 
+   sustain level 9
+
+   Also no. of frames to 25% volume = roughly 1.85 * no. of frames to
+   50% volume.
+
+   Frag it - just use linear approx.
+
+   ------------------------------------
+   Pete:
+   ms=((1<<value)*292)/10000
+   ------------------------------------
+
+
+3. Sustain rate value (linear mode)
+
+   Sustain rate range: 0 -> 127
+
+   Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |
+   -------------------------------------------
+   frames |  9 | 19 | 37 | 74 | 147| 293| 587|
+
+   Here, frames = no. of PAL frames for volume amplitude to go from 100%
+   to 0% (or vice-versa).
+
+   Same formula as for attack value, just a different value for k:
+
+   k = 0.00225
+
+   ie: frames = 0.00225 * 2 ^ (value / 4)
+
+   For emulation purposes:
+
+   %volume_increase_or_decrease_per_tick = 1 / frames
+
+   ------------------------------------
+   Pete:
+   ms=((1<<(value>>2))*450)/10000
+   ------------------------------------
+
+
+4. Release rate (linear mode)
+
+   Release rate range: 0 -> 31
+
+   Value  | 13 | 14 | 15 | 16 | 17 |
+   ---------------------------------------------------------------
+   frames | 18 | 36 | 73 | 146| 292|
+
+   Here, frames = no. of PAL frames to decay from 100% vol to 0% vol
+   after "note-off" is triggered.
+
+   Formula: frames = k * 2 ^ (value)
+
+   And so: k = 0.00223
+
+   ------------------------------------
+   Pete:
+   ms=((1<<value)*446)/10000
+   ------------------------------------
+
+
+Other notes:   
+
+Log stuff not figured out. You may get some clues from the "Decay rate"
+stuff above. For emu purposes it may not be important - use linear
+approx.
+
+To get timings in millisecs, multiply frames by 20.
+
+
+
+- James Higgs 17/6/2000
+james7780@yahoo.com
+
+//---------------------------------------------------------------
+
+OLD adsr mixing according to james' rules... has to be called
+every one millisecond
+
+
+ i32 v,v2,lT,l1,l2,l3;
+
+ if(s_chan[ch].bStop)                                  // psx wants to stop? -> release phase
+  {
+   if(s_chan[ch].ADSR.ReleaseVal!=0)                   // -> release not 0: do release (if 0: stop right now)
+    {
+     if(!s_chan[ch].ADSR.ReleaseVol)                   // --> release just started? set up the release stuff
+      {
+       s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;
+       s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;
+       s_chan[ch].ADSR.ReleaseTime =                   // --> calc how long does it take to reach the wanted sus level
+         (s_chan[ch].ADSR.ReleaseTime*
+          s_chan[ch].ADSR.ReleaseVol)/1024;
+      }
+                                                       // -> NO release exp mode used (yet)
+     v=s_chan[ch].ADSR.ReleaseVol;                     // -> get last volume
+     lT=s_chan[ch].ADSR.lTime-                         // -> how much time is past?
+        s_chan[ch].ADSR.ReleaseStartTime;
+     l1=s_chan[ch].ADSR.ReleaseTime;
+                                                       
+     if(lT<l1)                                         // -> we still have to release
+      {
+       v=v-((v*lT)/l1);                                // --> calc new volume
+      }
+     else                                              // -> release is over: now really stop that sample
+      {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
+    }
+   else                                                // -> release IS 0: release at once
+    {
+     v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;
+    }
+  }
+ else                                               
+  {//--------------------------------------------------// not in release phase:
+   v=1024;
+   lT=s_chan[ch].ADSR.lTime;
+   l1=s_chan[ch].ADSR.AttackTime;
+                                                       
+   if(lT<l1)                                           // attack
+    {                                                  // no exp mode used (yet)
+//     if(s_chan[ch].ADSR.AttackModeExp)
+//      {
+//       v=(v*lT)/l1;
+//      }
+//     else
+      {
+       v=(v*lT)/l1;
+      }
+     if(v==0) v=1;
+    }
+   else                                                // decay
+    {                                                  // should be exp, but who cares? ;)
+     l2=s_chan[ch].ADSR.DecayTime;
+     v2=s_chan[ch].ADSR.SustainLevel;
+
+     lT-=l1;
+     if(lT<l2)
+      {
+       v-=(((v-v2)*lT)/l2);
+      }
+     else                                              // sustain
+      {                                                // no exp mode used (yet)
+       l3=s_chan[ch].ADSR.SustainTime;
+       lT-=l2;
+       if(s_chan[ch].ADSR.SustainModeDec>0)
+        {
+         if(l3!=0) v2+=((v-v2)*lT)/l3;
+         else      v2=v;
+        }
+       else
+        {
+         if(l3!=0) v2-=(v2*lT)/l3;
+         else      v2=v;
+        }
+
+       if(v2>v)  v2=v;
+       if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
+
+       v=v2;
+      }
+    }
+  }
+
+ //----------------------------------------------------// 
+ // ok, done for this channel, so increase time
+
+ s_chan[ch].ADSR.lTime+=1;                             // 1 = 1.020408f ms;      
+
+ if(v>1024)     v=1024;                                // adjust volume
+ if(v<0)        v=0;                                  
+ s_chan[ch].ADSR.lVolume=v;                            // store act volume
+
+ return v;                                             // return the volume factor
+*/
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+
+/*
+-----------------------------------------------------------------------------
+Neill Corlett
+Playstation SPU envelope timing notes
+-----------------------------------------------------------------------------
+
+This is preliminary.  This may be wrong.  But the model described herein fits
+all of my experimental data, and it's just simple enough to sound right.
+
+ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.
+The value returned by channel reg 0xC is (envelope_level>>16).
+
+Each sample, an increment or decrement value will be added to or
+subtracted from this envelope level.
+
+Create the rate log table.  The values double every 4 entries.
+   entry #0 = 4
+
+    4, 5, 6, 7,
+    8,10,12,14,
+   16,20,24,28, ...
+
+   entry #40 = 4096...
+   entry #44 = 8192...
+   entry #48 = 16384...
+   entry #52 = 32768...
+   entry #56 = 65536...
+
+increments and decrements are in terms of ratelogtable[n]
+n may exceed the table bounds (plan on n being between -32 and 127).
+table values are all clipped between 0x00000000 and 0x3FFFFFFF
+
+when you "voice on", the envelope is always fully reset.
+(yes, it may click. the real thing does this too.)
+
+envelope level begins at zero.
+
+each state happens for at least 1 cycle
+(transitions are not instantaneous)
+this may result in some oddness: if the decay rate is uberfast, it will cut
+the envelope from full down to half in one sample, potentially skipping over
+the sustain level
+
+ATTACK
+------
+- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and
+  proceed to DECAY.
+
+Linear attack mode:
+- line extends upward to 0x7FFFFFFF
+- increment per sample is ratelogtable[(Ar^0x7F)-0x10]
+
+Logarithmic attack mode:
+if envelope_level < 0x60000000:
+  - line extends upward to 0x60000000
+  - increment per sample is ratelogtable[(Ar^0x7F)-0x10]
+else:
+  - line extends upward to 0x7FFFFFFF
+  - increment per sample is ratelogtable[(Ar^0x7F)-0x18]
+
+DECAY
+-----
+- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.
+  Do not clip to the sustain level.
+- current line ends at (envelope_level & 0x07FFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+  0: ratelogtable[(4*(Dr^0x1F))-0x18+0]
+  1: ratelogtable[(4*(Dr^0x1F))-0x18+4]
+  2: ratelogtable[(4*(Dr^0x1F))-0x18+6]
+  3: ratelogtable[(4*(Dr^0x1F))-0x18+8]
+  4: ratelogtable[(4*(Dr^0x1F))-0x18+9]
+  5: ratelogtable[(4*(Dr^0x1F))-0x18+10]
+  6: ratelogtable[(4*(Dr^0x1F))-0x18+11]
+  7: ratelogtable[(4*(Dr^0x1F))-0x18+12]
+  (note that this is the same as the release rate formula, except that
+   decay rates 10-1F aren't possible... those would be slower in theory)
+
+SUSTAIN
+-------
+- no terminating condition except for voice off
+- Sd=0 (increase) behavior is identical to ATTACK for both log and linear.
+- Sd=1 (decrease) behavior:
+Linear sustain decrease:
+- line extends to 0x00000000
+- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]
+Logarithmic sustain decrease:
+- current line ends at (envelope_level & 0x07FFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+  0: ratelogtable[(Sr^0x7F)-0x1B+0]
+  1: ratelogtable[(Sr^0x7F)-0x1B+4]
+  2: ratelogtable[(Sr^0x7F)-0x1B+6]
+  3: ratelogtable[(Sr^0x7F)-0x1B+8]
+  4: ratelogtable[(Sr^0x7F)-0x1B+9]
+  5: ratelogtable[(Sr^0x7F)-0x1B+10]
+  6: ratelogtable[(Sr^0x7F)-0x1B+11]
+  7: ratelogtable[(Sr^0x7F)-0x1B+12]
+
+RELEASE
+-------
+- if the envelope level has overflowed to negative, clip to 0 and QUIT.
+
+Linear release mode:
+- line extends to 0x00000000
+- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]
+
+Logarithmic release mode:
+- line extends to (envelope_level & 0x0FFFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+  0: ratelogtable[(4*(Rr^0x1F))-0x18+0]
+  1: ratelogtable[(4*(Rr^0x1F))-0x18+4]
+  2: ratelogtable[(4*(Rr^0x1F))-0x18+6]
+  3: ratelogtable[(4*(Rr^0x1F))-0x18+8]
+  4: ratelogtable[(4*(Rr^0x1F))-0x18+9]
+  5: ratelogtable[(4*(Rr^0x1F))-0x18+10]
+  6: ratelogtable[(4*(Rr^0x1F))-0x18+11]
+  7: ratelogtable[(4*(Rr^0x1F))-0x18+12]
+
+-----------------------------------------------------------------------------
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/adsr.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,28 @@
+/***************************************************************************
+                           adsr.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+static INLINE void StartADSR(int ch);
+static INLINE int  MixADSR(int ch);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/dma.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,80 @@
+/***************************************************************************
+                            dma.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "../peops/stdafx.h"
+
+#define _IN_DMA
+
+extern uint32 psx_ram[(2*1024*1024)/4];
+
+//#include "externals.h"
+////////////////////////////////////////////////////////////////////////
+// READ DMA (many values)
+////////////////////////////////////////////////////////////////////////
+
+void SPUreadDMAMem(u32 usPSXMem,int iSize)
+{
+ int i;
+ u16 *ram16 = (u16 *)&psx_ram[0];
+
+ for(i=0;i<iSize;i++)
+  {
+   ram16[usPSXMem>>1]=spuMem[spuAddr>>1];		// spu addr got by writeregister
+   usPSXMem+=2;
+   spuAddr+=2;                                         // inc spu addr
+   if(spuAddr>0x7ffff) spuAddr=0;                      // wrap
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+// to investigate: do sound data updates by writedma affect spu
+// irqs? Will an irq be triggered, if new data is written to
+// the memory irq address?
+
+////////////////////////////////////////////////////////////////////////
+// WRITE DMA (many values)
+////////////////////////////////////////////////////////////////////////
+
+void SPUwriteDMAMem(u32 usPSXMem,int iSize)
+{
+ int i;
+ u16 *ram16 = (u16 *)&psx_ram[0];
+
+ for(i=0;i<iSize;i++)
+  {
+//  printf("main RAM %x => SPU %x\n", usPSXMem, spuAddr);
+   spuMem[spuAddr>>1] = ram16[usPSXMem>>1];
+   usPSXMem+=2;                  			// spu addr got by writeregister
+   spuAddr+=2;                                         // inc spu addr
+   if(spuAddr>0x7ffff) spuAddr=0;                      // wrap
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/dma.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,31 @@
+/***************************************************************************
+                            dma.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+
+u16 CALLBACK SPUreadDMA(void);
+void CALLBACK SPUreadDMAMem(u16 * pusPSXMem,int iSize);
+void CALLBACK SPUwriteDMA(u16 val);
+void CALLBACK SPUwriteDMAMem(u16 * pusPSXMem,int iSize);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/externals.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,203 @@
+/***************************************************************************
+                         externals.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "ao.h"
+
+#ifndef PEOPS_EXTERNALS
+#define PEOPS_EXTERNALS
+
+typedef int8 s8;
+typedef int16 s16;
+typedef int32 s32;
+typedef int64 s64;
+
+typedef uint8 u8;
+typedef uint16 u16;
+typedef uint32 u32;
+typedef uint64 u64;
+
+#if LSB_FIRST
+static INLINE u16 BFLIP16(u16 x)
+{
+ return x;
+}
+#else
+static INLINE u16 BFLIP16(u16 x)
+{
+ return( ((x>>8)&0xFF)| ((x&0xFF)<<8) );
+}
+#endif
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+
+////////////////////////////////////////////////////////////////////////
+// spu defines
+////////////////////////////////////////////////////////////////////////
+
+// num of channels
+#define MAXCHAN     24
+
+///////////////////////////////////////////////////////////
+// struct defines
+///////////////////////////////////////////////////////////
+
+// ADSR INFOS PER CHANNEL
+typedef struct
+{
+ int            AttackModeExp;
+ s32           AttackTime;
+ s32           DecayTime;
+ s32           SustainLevel;
+ int            SustainModeExp;
+ s32           SustainModeDec;
+ s32           SustainTime;
+ int            ReleaseModeExp;
+ u32  ReleaseVal;
+ s32           ReleaseTime;
+ s32           ReleaseStartTime; 
+ s32           ReleaseVol; 
+ s32           lTime;
+ s32           lVolume;
+} ADSRInfo;
+
+typedef struct
+{
+ int            State;
+ int            AttackModeExp;
+ int            AttackRate;
+ int            DecayRate;
+ int            SustainLevel;
+ int            SustainModeExp;
+ int            SustainIncrease;
+ int            SustainRate;
+ int            ReleaseModeExp;
+ int            ReleaseRate;
+ int            EnvelopeVol;
+ s32           lVolume;
+ s32           lDummy1;
+ s32           lDummy2;
+} ADSRInfoEx;
+              
+///////////////////////////////////////////////////////////
+
+// Tmp Flags
+
+// used for debug channel muting
+#define FLAG_MUTE  1
+
+///////////////////////////////////////////////////////////
+
+// MAIN CHANNEL STRUCT
+typedef struct
+{
+ int               bNew;                               // start flag
+
+ int               iSBPos;                             // mixing stuff
+ int               spos;
+ int               sinc;
+ int               SB[32+1];
+ int               sval;
+
+ u8 *   pStart;                             // start ptr into sound mem
+ u8 *   pCurr;                              // current pos in sound mem
+ u8 *   pLoop;                              // loop ptr in sound mem
+
+ int               bOn;                                // is channel active (sample playing?)
+ int               bStop;                              // is channel stopped (sample _can_ still be playing, ADSR Release phase)
+ int               iActFreq;                           // current psx pitch
+ int               iUsedFreq;                          // current pc pitch
+ int               iLeftVolume;                        // left volume
+ int               iLeftVolRaw;                        // left psx volume value
+ int               bIgnoreLoop;                        // ignore loop bit, if an external loop address is used
+ int               iRightVolume;                       // right volume
+ int               iRightVolRaw;                       // right psx volume value
+ int               iRawPitch;                          // raw pitch (0...3fff)
+ int               iIrqDone;                           // debug irq done flag
+ int               s_1;                                // last decoding infos
+ int               s_2;
+ int               bRVBActive;                         // reverb active flag
+ int               iRVBOffset;                         // reverb offset
+ int               iRVBRepeat;                         // reverb repeat
+ int               bNoise;                             // noise active flag
+ int               bFMod;                              // freq mod (0=off, 1=sound channel, 2=freq channel)
+ int               iOldNoise;                          // old noise val for this channel   
+ ADSRInfo          ADSR;                               // active ADSR settings
+ ADSRInfoEx        ADSRX;                              // next ADSR settings (will be moved to active on sample start)
+
+} SPUCHAN;
+
+///////////////////////////////////////////////////////////
+
+typedef struct
+{
+ int StartAddr;      // reverb area start addr in samples
+ int CurrAddr;       // reverb area curr addr in samples
+
+ int Enabled;
+ int VolLeft;
+ int VolRight;
+ int iLastRVBLeft;
+ int iLastRVBRight;
+ int iRVBLeft;
+ int iRVBRight;
+
+
+ int FB_SRC_A;       // (offset)
+ int FB_SRC_B;       // (offset)
+ int IIR_ALPHA;      // (coef.)
+ int ACC_COEF_A;     // (coef.)
+ int ACC_COEF_B;     // (coef.)
+ int ACC_COEF_C;     // (coef.)
+ int ACC_COEF_D;     // (coef.)
+ int IIR_COEF;       // (coef.)
+ int FB_ALPHA;       // (coef.)
+ int FB_X;           // (coef.)
+ int IIR_DEST_A0;    // (offset)
+ int IIR_DEST_A1;    // (offset)
+ int ACC_SRC_A0;     // (offset)
+ int ACC_SRC_A1;     // (offset)
+ int ACC_SRC_B0;     // (offset)
+ int ACC_SRC_B1;     // (offset)
+ int IIR_SRC_A0;     // (offset)
+ int IIR_SRC_A1;     // (offset)
+ int IIR_DEST_B0;    // (offset)
+ int IIR_DEST_B1;    // (offset)
+ int ACC_SRC_C0;     // (offset)
+ int ACC_SRC_C1;     // (offset)
+ int ACC_SRC_D0;     // (offset)
+ int ACC_SRC_D1;     // (offset)
+ int IIR_SRC_B1;     // (offset)
+ int IIR_SRC_B0;     // (offset)
+ int MIX_DEST_A0;    // (offset)
+ int MIX_DEST_A1;    // (offset)
+ int MIX_DEST_B0;    // (offset)
+ int MIX_DEST_B1;    // (offset)
+ int IN_COEF_L;      // (coef.)
+ int IN_COEF_R;      // (coef.)
+} REVERBInfo;
+
+#endif // PEOPS_EXTERNALS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/gauss_i.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,163 @@
+/***************************************************************************
+                          gauss_i.h  -  description
+                           -----------------------
+    begin                : Sun Feb 08 2003
+    copyright            : (C) 2003 by Chris Moeller, eh, whatever
+    email                : chris@kode54.tk
+ ***************************************************************************/
+                       
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+                           
+//*************************************************************************//
+// History of changes:
+//
+// 2003/02/08 - kode54
+// - generated by interleaving table from gauss.h from the libopenspc
+//   project; a gaussian bell curve table logged from the SPC-700,
+//   though Neill says he logged the same curve from a PSX SPU. Also
+//   says that interleaving the coefficients together runs faster. Meh.
+//
+//*************************************************************************//
+
+#ifndef GAUSS_H
+#define GAUSS_H
+
+// 1024 entries
+const int gauss[]={
+	0x172, 0x519, 0x176, 0x000, 0x16E, 0x519, 0x17A, 0x000, 
+	0x16A, 0x518, 0x17D, 0x000, 0x166, 0x518, 0x181, 0x000, 
+	0x162, 0x518, 0x185, 0x000, 0x15F, 0x518, 0x189, 0x000, 
+	0x15B, 0x518, 0x18D, 0x000, 0x157, 0x517, 0x191, 0x000, 
+	0x153, 0x517, 0x195, 0x000, 0x150, 0x517, 0x19A, 0x000, 
+	0x14C, 0x516, 0x19E, 0x000, 0x148, 0x516, 0x1A2, 0x000, 
+	0x145, 0x515, 0x1A6, 0x000, 0x141, 0x514, 0x1AA, 0x000, 
+	0x13E, 0x514, 0x1AE, 0x000, 0x13A, 0x513, 0x1B2, 0x000, 
+	0x137, 0x512, 0x1B7, 0x001, 0x133, 0x511, 0x1BB, 0x001, 
+	0x130, 0x511, 0x1BF, 0x001, 0x12C, 0x510, 0x1C3, 0x001, 
+	0x129, 0x50F, 0x1C8, 0x001, 0x125, 0x50E, 0x1CC, 0x001, 
+	0x122, 0x50D, 0x1D0, 0x001, 0x11E, 0x50C, 0x1D5, 0x001, 
+	0x11B, 0x50B, 0x1D9, 0x001, 0x118, 0x50A, 0x1DD, 0x001, 
+	0x114, 0x508, 0x1E2, 0x001, 0x111, 0x507, 0x1E6, 0x002, 
+	0x10E, 0x506, 0x1EB, 0x002, 0x10B, 0x504, 0x1EF, 0x002, 
+	0x107, 0x503, 0x1F3, 0x002, 0x104, 0x502, 0x1F8, 0x002, 
+	0x101, 0x500, 0x1FC, 0x002, 0x0FE, 0x4FF, 0x201, 0x002, 
+	0x0FB, 0x4FD, 0x205, 0x003, 0x0F8, 0x4FB, 0x20A, 0x003, 
+	0x0F5, 0x4FA, 0x20F, 0x003, 0x0F2, 0x4F8, 0x213, 0x003, 
+	0x0EF, 0x4F6, 0x218, 0x003, 0x0EC, 0x4F5, 0x21C, 0x004, 
+	0x0E9, 0x4F3, 0x221, 0x004, 0x0E6, 0x4F1, 0x226, 0x004, 
+	0x0E3, 0x4EF, 0x22A, 0x004, 0x0E0, 0x4ED, 0x22F, 0x004, 
+	0x0DD, 0x4EB, 0x233, 0x005, 0x0DA, 0x4E9, 0x238, 0x005, 
+	0x0D7, 0x4E7, 0x23D, 0x005, 0x0D4, 0x4E5, 0x241, 0x005, 
+	0x0D2, 0x4E3, 0x246, 0x006, 0x0CF, 0x4E0, 0x24B, 0x006, 
+	0x0CC, 0x4DE, 0x250, 0x006, 0x0C9, 0x4DC, 0x254, 0x006, 
+	0x0C7, 0x4D9, 0x259, 0x007, 0x0C4, 0x4D7, 0x25E, 0x007, 
+	0x0C1, 0x4D5, 0x263, 0x007, 0x0BF, 0x4D2, 0x267, 0x008, 
+	0x0BC, 0x4D0, 0x26C, 0x008, 0x0BA, 0x4CD, 0x271, 0x008, 
+	0x0B7, 0x4CB, 0x276, 0x009, 0x0B4, 0x4C8, 0x27B, 0x009, 
+	0x0B2, 0x4C5, 0x280, 0x009, 0x0AF, 0x4C3, 0x284, 0x00A, 
+	0x0AD, 0x4C0, 0x289, 0x00A, 0x0AB, 0x4BD, 0x28E, 0x00A, 
+	0x0A8, 0x4BA, 0x293, 0x00B, 0x0A6, 0x4B7, 0x298, 0x00B, 
+	0x0A3, 0x4B5, 0x29D, 0x00B, 0x0A1, 0x4B2, 0x2A2, 0x00C, 
+	0x09F, 0x4AF, 0x2A6, 0x00C, 0x09C, 0x4AC, 0x2AB, 0x00D, 
+	0x09A, 0x4A9, 0x2B0, 0x00D, 0x098, 0x4A6, 0x2B5, 0x00E, 
+	0x096, 0x4A2, 0x2BA, 0x00E, 0x093, 0x49F, 0x2BF, 0x00F, 
+	0x091, 0x49C, 0x2C4, 0x00F, 0x08F, 0x499, 0x2C9, 0x00F, 
+	0x08D, 0x496, 0x2CE, 0x010, 0x08B, 0x492, 0x2D3, 0x010, 
+	0x089, 0x48F, 0x2D8, 0x011, 0x086, 0x48C, 0x2DC, 0x011, 
+	0x084, 0x488, 0x2E1, 0x012, 0x082, 0x485, 0x2E6, 0x013, 
+	0x080, 0x481, 0x2EB, 0x013, 0x07E, 0x47E, 0x2F0, 0x014, 
+	0x07C, 0x47A, 0x2F5, 0x014, 0x07A, 0x477, 0x2FA, 0x015, 
+	0x078, 0x473, 0x2FF, 0x015, 0x076, 0x470, 0x304, 0x016, 
+	0x075, 0x46C, 0x309, 0x017, 0x073, 0x468, 0x30E, 0x017, 
+	0x071, 0x465, 0x313, 0x018, 0x06F, 0x461, 0x318, 0x018, 
+	0x06D, 0x45D, 0x31D, 0x019, 0x06B, 0x459, 0x322, 0x01A, 
+	0x06A, 0x455, 0x326, 0x01B, 0x068, 0x452, 0x32B, 0x01B, 
+	0x066, 0x44E, 0x330, 0x01C, 0x064, 0x44A, 0x335, 0x01D, 
+	0x063, 0x446, 0x33A, 0x01D, 0x061, 0x442, 0x33F, 0x01E, 
+	0x05F, 0x43E, 0x344, 0x01F, 0x05E, 0x43A, 0x349, 0x020, 
+	0x05C, 0x436, 0x34E, 0x020, 0x05A, 0x432, 0x353, 0x021, 
+	0x059, 0x42E, 0x357, 0x022, 0x057, 0x42A, 0x35C, 0x023, 
+	0x056, 0x425, 0x361, 0x024, 0x054, 0x421, 0x366, 0x024, 
+	0x053, 0x41D, 0x36B, 0x025, 0x051, 0x419, 0x370, 0x026, 
+	0x050, 0x415, 0x374, 0x027, 0x04E, 0x410, 0x379, 0x028, 
+	0x04D, 0x40C, 0x37E, 0x029, 0x04C, 0x408, 0x383, 0x02A, 
+	0x04A, 0x403, 0x388, 0x02B, 0x049, 0x3FF, 0x38C, 0x02C, 
+	0x047, 0x3FB, 0x391, 0x02D, 0x046, 0x3F6, 0x396, 0x02E, 
+	0x045, 0x3F2, 0x39B, 0x02F, 0x043, 0x3ED, 0x39F, 0x030, 
+	0x042, 0x3E9, 0x3A4, 0x031, 0x041, 0x3E5, 0x3A9, 0x032, 
+	0x040, 0x3E0, 0x3AD, 0x033, 0x03E, 0x3DC, 0x3B2, 0x034, 
+	0x03D, 0x3D7, 0x3B7, 0x035, 0x03C, 0x3D2, 0x3BB, 0x036, 
+	0x03B, 0x3CE, 0x3C0, 0x037, 0x03A, 0x3C9, 0x3C5, 0x038, 
+	0x038, 0x3C5, 0x3C9, 0x03A, 0x037, 0x3C0, 0x3CE, 0x03B, 
+	0x036, 0x3BB, 0x3D2, 0x03C, 0x035, 0x3B7, 0x3D7, 0x03D, 
+	0x034, 0x3B2, 0x3DC, 0x03E, 0x033, 0x3AD, 0x3E0, 0x040, 
+	0x032, 0x3A9, 0x3E5, 0x041, 0x031, 0x3A4, 0x3E9, 0x042, 
+	0x030, 0x39F, 0x3ED, 0x043, 0x02F, 0x39B, 0x3F2, 0x045, 
+	0x02E, 0x396, 0x3F6, 0x046, 0x02D, 0x391, 0x3FB, 0x047, 
+	0x02C, 0x38C, 0x3FF, 0x049, 0x02B, 0x388, 0x403, 0x04A, 
+	0x02A, 0x383, 0x408, 0x04C, 0x029, 0x37E, 0x40C, 0x04D, 
+	0x028, 0x379, 0x410, 0x04E, 0x027, 0x374, 0x415, 0x050, 
+	0x026, 0x370, 0x419, 0x051, 0x025, 0x36B, 0x41D, 0x053, 
+	0x024, 0x366, 0x421, 0x054, 0x024, 0x361, 0x425, 0x056, 
+	0x023, 0x35C, 0x42A, 0x057, 0x022, 0x357, 0x42E, 0x059, 
+	0x021, 0x353, 0x432, 0x05A, 0x020, 0x34E, 0x436, 0x05C, 
+	0x020, 0x349, 0x43A, 0x05E, 0x01F, 0x344, 0x43E, 0x05F, 
+	0x01E, 0x33F, 0x442, 0x061, 0x01D, 0x33A, 0x446, 0x063, 
+	0x01D, 0x335, 0x44A, 0x064, 0x01C, 0x330, 0x44E, 0x066, 
+	0x01B, 0x32B, 0x452, 0x068, 0x01B, 0x326, 0x455, 0x06A, 
+	0x01A, 0x322, 0x459, 0x06B, 0x019, 0x31D, 0x45D, 0x06D, 
+	0x018, 0x318, 0x461, 0x06F, 0x018, 0x313, 0x465, 0x071, 
+	0x017, 0x30E, 0x468, 0x073, 0x017, 0x309, 0x46C, 0x075, 
+	0x016, 0x304, 0x470, 0x076, 0x015, 0x2FF, 0x473, 0x078, 
+	0x015, 0x2FA, 0x477, 0x07A, 0x014, 0x2F5, 0x47A, 0x07C, 
+	0x014, 0x2F0, 0x47E, 0x07E, 0x013, 0x2EB, 0x481, 0x080, 
+	0x013, 0x2E6, 0x485, 0x082, 0x012, 0x2E1, 0x488, 0x084, 
+	0x011, 0x2DC, 0x48C, 0x086, 0x011, 0x2D8, 0x48F, 0x089, 
+	0x010, 0x2D3, 0x492, 0x08B, 0x010, 0x2CE, 0x496, 0x08D, 
+	0x00F, 0x2C9, 0x499, 0x08F, 0x00F, 0x2C4, 0x49C, 0x091, 
+	0x00F, 0x2BF, 0x49F, 0x093, 0x00E, 0x2BA, 0x4A2, 0x096, 
+	0x00E, 0x2B5, 0x4A6, 0x098, 0x00D, 0x2B0, 0x4A9, 0x09A, 
+	0x00D, 0x2AB, 0x4AC, 0x09C, 0x00C, 0x2A6, 0x4AF, 0x09F, 
+	0x00C, 0x2A2, 0x4B2, 0x0A1, 0x00B, 0x29D, 0x4B5, 0x0A3, 
+	0x00B, 0x298, 0x4B7, 0x0A6, 0x00B, 0x293, 0x4BA, 0x0A8, 
+	0x00A, 0x28E, 0x4BD, 0x0AB, 0x00A, 0x289, 0x4C0, 0x0AD, 
+	0x00A, 0x284, 0x4C3, 0x0AF, 0x009, 0x280, 0x4C5, 0x0B2, 
+	0x009, 0x27B, 0x4C8, 0x0B4, 0x009, 0x276, 0x4CB, 0x0B7, 
+	0x008, 0x271, 0x4CD, 0x0BA, 0x008, 0x26C, 0x4D0, 0x0BC, 
+	0x008, 0x267, 0x4D2, 0x0BF, 0x007, 0x263, 0x4D5, 0x0C1, 
+	0x007, 0x25E, 0x4D7, 0x0C4, 0x007, 0x259, 0x4D9, 0x0C7, 
+	0x006, 0x254, 0x4DC, 0x0C9, 0x006, 0x250, 0x4DE, 0x0CC, 
+	0x006, 0x24B, 0x4E0, 0x0CF, 0x006, 0x246, 0x4E3, 0x0D2, 
+	0x005, 0x241, 0x4E5, 0x0D4, 0x005, 0x23D, 0x4E7, 0x0D7, 
+	0x005, 0x238, 0x4E9, 0x0DA, 0x005, 0x233, 0x4EB, 0x0DD, 
+	0x004, 0x22F, 0x4ED, 0x0E0, 0x004, 0x22A, 0x4EF, 0x0E3, 
+	0x004, 0x226, 0x4F1, 0x0E6, 0x004, 0x221, 0x4F3, 0x0E9, 
+	0x004, 0x21C, 0x4F5, 0x0EC, 0x003, 0x218, 0x4F6, 0x0EF, 
+	0x003, 0x213, 0x4F8, 0x0F2, 0x003, 0x20F, 0x4FA, 0x0F5, 
+	0x003, 0x20A, 0x4FB, 0x0F8, 0x003, 0x205, 0x4FD, 0x0FB, 
+	0x002, 0x201, 0x4FF, 0x0FE, 0x002, 0x1FC, 0x500, 0x101, 
+	0x002, 0x1F8, 0x502, 0x104, 0x002, 0x1F3, 0x503, 0x107, 
+	0x002, 0x1EF, 0x504, 0x10B, 0x002, 0x1EB, 0x506, 0x10E, 
+	0x002, 0x1E6, 0x507, 0x111, 0x001, 0x1E2, 0x508, 0x114, 
+	0x001, 0x1DD, 0x50A, 0x118, 0x001, 0x1D9, 0x50B, 0x11B, 
+	0x001, 0x1D5, 0x50C, 0x11E, 0x001, 0x1D0, 0x50D, 0x122, 
+	0x001, 0x1CC, 0x50E, 0x125, 0x001, 0x1C8, 0x50F, 0x129, 
+	0x001, 0x1C3, 0x510, 0x12C, 0x001, 0x1BF, 0x511, 0x130, 
+	0x001, 0x1BB, 0x511, 0x133, 0x001, 0x1B7, 0x512, 0x137, 
+	0x000, 0x1B2, 0x513, 0x13A, 0x000, 0x1AE, 0x514, 0x13E, 
+	0x000, 0x1AA, 0x514, 0x141, 0x000, 0x1A6, 0x515, 0x145, 
+	0x000, 0x1A2, 0x516, 0x148, 0x000, 0x19E, 0x516, 0x14C, 
+	0x000, 0x19A, 0x517, 0x150, 0x000, 0x195, 0x517, 0x153, 
+	0x000, 0x191, 0x517, 0x157, 0x000, 0x18D, 0x518, 0x15B, 
+	0x000, 0x189, 0x518, 0x15F, 0x000, 0x185, 0x518, 0x162, 
+	0x000, 0x181, 0x518, 0x166, 0x000, 0x17D, 0x518, 0x16A, 
+	0x000, 0x17A, 0x519, 0x16E, 0x000, 0x176, 0x519, 0x172};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/registers.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,492 @@
+/***************************************************************************
+                         registers.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+/* ChangeLog 
+
+ February 8, 2004	-	xodnizel
+ - Fixed setting of reverb volume.  Just typecast val("u16") to s16.
+   Also adjusted the normal channel volume to be one less than what it was before when the 
+   "phase invert" bit is set.  I'm assuming it's just in two's complement.
+
+ 2003/02/09 - kode54
+ - removed &0x3fff from reverb volume registers, fixes a few games,
+   hopefully won't be breaking anything
+
+ 2003/01/19 - Pete
+ - added Neill's reverb
+
+ 2003/01/06 - Pete
+ - added Neill's ADSR timings
+
+ 2002/05/15 - Pete
+ - generic cleanup for the Peops release
+
+*/
+
+#include "stdafx.h"
+
+#define _IN_REGISTERS
+
+#include "../peops/externals.h"
+#include "../peops/registers.h"
+#include "../peops/regs.h"
+
+////////////////////////////////////////////////////////////////////////
+// WRITE REGISTERS: called by main emu
+////////////////////////////////////////////////////////////////////////
+
+void SPUwriteRegister(u32 reg, u16 val)
+{
+ const u32 r=reg&0xfff;
+ regArea[(r-0xc00)>>1] = val;
+
+// printf("SPUwrite: r %x val %x\n", r, val);
+
+ if(r>=0x0c00 && r<0x0d80)                             // some channel info?
+  {
+   int ch=(r>>4)-0xc0;                                 // calc channel
+
+   //if(ch==20) printf("%08x: %04x\n",reg,val);
+
+   switch(r&0x0f)
+    {
+     //------------------------------------------------// r volume
+     case 0:                                           
+       SetVolumeLR(0,(u8)ch,val);
+       break;
+     //------------------------------------------------// l volume
+     case 2:                                           
+       SetVolumeLR(1,(u8)ch,val);
+       break;
+     //------------------------------------------------// pitch
+     case 4:                                           
+       SetPitch(ch,val);
+       break;
+     //------------------------------------------------// start
+     case 6:      
+       s_chan[ch].pStart=spuMemC+((u32) val<<3);
+       break;
+     //------------------------------------------------// level with pre-calcs
+     case 8:
+       {
+        const u32 lval=val; // DEBUG CHECK
+        //---------------------------------------------//
+        s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; 
+        s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;
+        s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;
+        s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;
+        //---------------------------------------------//
+      }
+      break;
+     //------------------------------------------------// adsr times with pre-calcs
+     case 10:
+      {
+       const u32 lval=val; // DEBUG CHECK
+
+       //----------------------------------------------//
+       s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;
+       s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;
+       s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;
+       s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;
+       s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;
+       //----------------------------------------------//
+      }
+     break;
+     //------------------------------------------------// adsr volume... mmm have to investigate this
+     //case 0xC:
+     //  break;
+     //------------------------------------------------//
+     case 0xE:                                          // loop?
+       s_chan[ch].pLoop=spuMemC+((u32) val<<3);
+       s_chan[ch].bIgnoreLoop=1;
+       break;
+     //------------------------------------------------//
+    }
+   return;
+  }
+
+ switch(r)
+   {
+    //-------------------------------------------------//
+    case H_SPUaddr:
+      spuAddr = (u32) val<<3;
+      break;
+    //-------------------------------------------------//
+    case H_SPUdata:
+      spuMem[spuAddr>>1] = BFLIP16(val);
+      spuAddr+=2;
+      if(spuAddr>0x7ffff) spuAddr=0;
+      break;
+    //-------------------------------------------------//
+    case H_SPUctrl:
+      spuCtrl=val;
+      break;
+    //-------------------------------------------------//
+    case H_SPUstat:
+      spuStat=val & 0xf800;
+      break;
+    //-------------------------------------------------//
+    case H_SPUReverbAddr:
+      if(val==0xFFFF || val<=0x200)
+       {rvb.StartAddr=rvb.CurrAddr=0;}
+      else
+       {
+        const s32 iv=(u32)val<<2;
+        if(rvb.StartAddr!=iv)
+         {
+          rvb.StartAddr=(u32)val<<2;
+          rvb.CurrAddr=rvb.StartAddr;
+         }
+       }
+      break;
+    //-------------------------------------------------//
+    case H_SPUirqAddr:
+      spuIrq = val;
+      pSpuIrq=spuMemC+((u32) val<<3);
+      break;
+    //-------------------------------------------------//
+    /* Volume settings appear to be at least 15-bit unsigned in this case.  
+       Definitely NOT 15-bit signed.  Probably 16-bit signed, so s16 type cast.
+       Check out "Chrono Cross:  Shadow's End Forest"
+    */
+    case H_SPUrvolL:
+      rvb.VolLeft=(s16)val;
+      //printf("%d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUrvolR:
+      rvb.VolRight=(s16)val;
+      //printf("%d\n",val);
+      break;
+    //-------------------------------------------------//
+
+/*
+    case H_ExtLeft:
+     //auxprintf("EL %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_ExtRight:
+     //auxprintf("ER %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUmvolL:
+     //auxprintf("ML %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUmvolR:
+     //auxprintf("MR %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUMute1:
+     //printf("M0 %04x\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUMute2:
+    // printf("M1 %04x\n",val);
+      break;
+*/
+    //-------------------------------------------------//
+    case H_SPUon1:
+      SoundOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+     case H_SPUon2:
+	// printf("Boop: %08x: %04x\n",reg,val);
+      SoundOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUoff1:
+      SoundOff(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUoff2:
+      SoundOff(16,24,val);
+	// printf("Boop: %08x: %04x\n",reg,val);
+      break;
+    //-------------------------------------------------//
+    case H_FMod1:
+      FModOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case H_FMod2:
+      FModOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case H_Noise1:
+      NoiseOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case H_Noise2:
+      NoiseOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case H_RVBon1:
+      rvb.Enabled&=~0xFFFF;
+      rvb.Enabled|=val;
+      break;
+
+    //-------------------------------------------------//
+    case H_RVBon2:
+      rvb.Enabled&=0xFFFF;
+      rvb.Enabled|=val<<16;
+      break;
+
+    //-------------------------------------------------//
+    case H_Reverb+0:
+      rvb.FB_SRC_A=val;
+      break;
+
+    case H_Reverb+2   : rvb.FB_SRC_B=(s16)val;       break;
+    case H_Reverb+4   : rvb.IIR_ALPHA=(s16)val;      break;
+    case H_Reverb+6   : rvb.ACC_COEF_A=(s16)val;     break;
+    case H_Reverb+8   : rvb.ACC_COEF_B=(s16)val;     break;
+    case H_Reverb+10  : rvb.ACC_COEF_C=(s16)val;     break;
+    case H_Reverb+12  : rvb.ACC_COEF_D=(s16)val;     break;
+    case H_Reverb+14  : rvb.IIR_COEF=(s16)val;       break;
+    case H_Reverb+16  : rvb.FB_ALPHA=(s16)val;       break;
+    case H_Reverb+18  : rvb.FB_X=(s16)val;           break;
+    case H_Reverb+20  : rvb.IIR_DEST_A0=(s16)val;    break;
+    case H_Reverb+22  : rvb.IIR_DEST_A1=(s16)val;    break;
+    case H_Reverb+24  : rvb.ACC_SRC_A0=(s16)val;     break;
+    case H_Reverb+26  : rvb.ACC_SRC_A1=(s16)val;     break;
+    case H_Reverb+28  : rvb.ACC_SRC_B0=(s16)val;     break;
+    case H_Reverb+30  : rvb.ACC_SRC_B1=(s16)val;     break;
+    case H_Reverb+32  : rvb.IIR_SRC_A0=(s16)val;     break;
+    case H_Reverb+34  : rvb.IIR_SRC_A1=(s16)val;     break;
+    case H_Reverb+36  : rvb.IIR_DEST_B0=(s16)val;    break;
+    case H_Reverb+38  : rvb.IIR_DEST_B1=(s16)val;    break;
+    case H_Reverb+40  : rvb.ACC_SRC_C0=(s16)val;     break;
+    case H_Reverb+42  : rvb.ACC_SRC_C1=(s16)val;     break;
+    case H_Reverb+44  : rvb.ACC_SRC_D0=(s16)val;     break;
+    case H_Reverb+46  : rvb.ACC_SRC_D1=(s16)val;     break;
+    case H_Reverb+48  : rvb.IIR_SRC_B1=(s16)val;     break;
+    case H_Reverb+50  : rvb.IIR_SRC_B0=(s16)val;     break;
+    case H_Reverb+52  : rvb.MIX_DEST_A0=(s16)val;    break;
+    case H_Reverb+54  : rvb.MIX_DEST_A1=(s16)val;    break;
+    case H_Reverb+56  : rvb.MIX_DEST_B0=(s16)val;    break;
+    case H_Reverb+58  : rvb.MIX_DEST_B1=(s16)val;    break;
+    case H_Reverb+60  : rvb.IN_COEF_L=(s16)val;      break;
+    case H_Reverb+62  : rvb.IN_COEF_R=(s16)val;      break;
+   }
+
+}
+
+////////////////////////////////////////////////////////////////////////
+// READ REGISTER: called by main emu
+////////////////////////////////////////////////////////////////////////
+
+u16 SPUreadRegister(u32 reg)
+{
+ const u32 r=reg&0xfff;
+
+ if(r>=0x0c00 && r<0x0d80)
+  {
+   switch(r&0x0f)
+    {
+     case 0xC:                                          // get adsr vol
+      {
+       const int ch=(r>>4)-0xc0;
+       if(s_chan[ch].bNew) return 1;                   // we are started, but not processed? return 1
+       if(s_chan[ch].ADSRX.lVolume &&                  // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
+          !s_chan[ch].ADSRX.EnvelopeVol)                   
+        return 1;
+       return (u16)(s_chan[ch].ADSRX.EnvelopeVol>>16);
+      }
+
+     case 0xE:                                          // get loop address
+      {
+       const int ch=(r>>4)-0xc0;
+       if(s_chan[ch].pLoop==NULL) return 0;
+       return (u16)((s_chan[ch].pLoop-spuMemC)>>3);
+      }
+    }
+  }
+
+ switch(r)
+  {
+    case H_SPUctrl:
+     return spuCtrl;
+
+    case H_SPUstat:
+     return spuStat;
+        
+    case H_SPUaddr:
+     return (u16)(spuAddr>>3);
+
+    case H_SPUdata:
+     {
+      u16 s=BFLIP16(spuMem[spuAddr>>1]);
+      spuAddr+=2;
+      if(spuAddr>0x7ffff) spuAddr=0;
+      return s;
+     }
+
+    case H_SPUirqAddr:
+     return spuIrq;
+
+    //case H_SPUIsOn1:
+    // return IsSoundOn(0,16);
+
+    //case H_SPUIsOn2:
+    // return IsSoundOn(16,24);
+ 
+  }
+
+ return regArea[(r-0xc00)>>1];
+}
+ 
+////////////////////////////////////////////////////////////////////////
+// SOUND ON register write
+////////////////////////////////////////////////////////////////////////
+
+static void SoundOn(int start,int end,u16 val)     // SOUND ON PSX COMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if((val&1) && s_chan[ch].pStart)                    // mmm... start has to be set before key on !?!
+    {
+     s_chan[ch].bIgnoreLoop=0;
+     s_chan[ch].bNew=1;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// SOUND OFF register write
+////////////////////////////////////////////////////////////////////////
+
+static void SoundOff(int start,int end,u16 val)    // SOUND OFF PSX COMMAND
+{
+ int ch;
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // && s_chan[i].bOn)  mmm...
+    {
+     s_chan[ch].bStop=1;
+    }                                                  
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// FMOD register write
+////////////////////////////////////////////////////////////////////////
+
+static void FModOn(int start,int end,u16 val)      // FMOD ON PSX COMMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // -> fmod on/off
+    {
+     if(ch>0) 
+      {
+       s_chan[ch].bFMod=1;                             // --> sound channel
+       s_chan[ch-1].bFMod=2;                           // --> freq channel
+      }
+    }
+   else
+    {
+     s_chan[ch].bFMod=0;                               // --> turn off fmod
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// NOISE register write
+////////////////////////////////////////////////////////////////////////
+
+static void NoiseOn(int start,int end,u16 val)     // NOISE ON PSX COMMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // -> noise on/off
+    {
+     s_chan[ch].bNoise=1;
+    }
+   else 
+    {
+     s_chan[ch].bNoise=0;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// LEFT VOLUME register write
+////////////////////////////////////////////////////////////////////////
+
+// please note: sweep is wrong.
+
+static void SetVolumeLR(int right, u8 ch,s16 vol)            // LEFT VOLUME
+{
+ //if(vol&0xc000)
+ //printf("%d %08x\n",right,vol);
+ if(right)
+  s_chan[ch].iRightVolRaw=vol;
+ else
+  s_chan[ch].iLeftVolRaw=vol;
+
+ if(vol&0x8000)                                        // sweep?
+  {
+   s16 sInc=1;                                       // -> sweep up?
+   if(vol&0x2000) sInc=-1;                             // -> or down?
+   if(vol&0x1000) vol^=0xffff;                         // -> mmm... phase inverted? have to investigate this
+   vol=((vol&0x7f)+1)/2;                               // -> sweep: 0..127 -> 0..64
+   vol+=vol/(2*sInc);                                  // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
+   vol*=128;
+   vol&=0x3fff;
+   //puts("Sweep");
+  }
+ else                                                  // no sweep:
+  {
+   if(vol&0x4000)
+    vol=(vol&0x3FFF)-0x4000;
+   else
+    vol&=0x3FFF;
+
+   //if(vol&0x4000)                                      // -> mmm... phase inverted? have to investigate this
+   // vol=0-(0x3fff-(vol&0x3fff));
+   //else
+   // vol&=0x3fff;
+  }
+ if(right)
+  s_chan[ch].iRightVolume=vol;
+ else
+  s_chan[ch].iLeftVolume=vol;                           // store volume
+}
+
+////////////////////////////////////////////////////////////////////////
+// PITCH register write
+////////////////////////////////////////////////////////////////////////
+
+static void SetPitch(int ch,u16 val)               // SET PITCH
+{
+ int NP;
+ if(val>0x3fff) NP=0x3fff;                             // get pitch val
+ else           NP=val;
+
+ s_chan[ch].iRawPitch=NP;
+
+ NP=(44100L*NP)/4096L;                                 // calc frequency
+ if(NP<1) NP=1;                                        // some security
+ s_chan[ch].iActFreq=NP;                               // store frequency
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/registers.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,153 @@
+/***************************************************************************
+                         registers.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#define H_SPUReverbAddr  0x0da2
+#define H_SPUirqAddr     0x0da4
+#define H_SPUaddr        0x0da6
+#define H_SPUdata        0x0da8
+#define H_SPUctrl        0x0daa
+#define H_SPUstat        0x0dae
+#define H_SPUmvolL       0x0d80
+#define H_SPUmvolR       0x0d82
+#define H_SPUrvolL       0x0d84
+#define H_SPUrvolR       0x0d86
+#define H_SPUon1         0x0d88
+#define H_SPUon2         0x0d8a
+#define H_SPUoff1        0x0d8c
+#define H_SPUoff2        0x0d8e
+#define H_FMod1          0x0d90
+#define H_FMod2          0x0d92
+#define H_Noise1         0x0d94
+#define H_Noise2         0x0d96
+#define H_RVBon1         0x0d98
+#define H_RVBon2         0x0d9a
+#define H_SPUMute1       0x0d9c
+#define H_SPUMute2       0x0d9e
+#define H_CDLeft         0x0db0
+#define H_CDRight        0x0db2
+#define H_ExtLeft        0x0db4
+#define H_ExtRight       0x0db6
+#define H_Reverb         0x0dc0
+#define H_SPUPitch0      0x0c04
+#define H_SPUPitch1      0x0c14
+#define H_SPUPitch2      0x0c24
+#define H_SPUPitch3      0x0c34
+#define H_SPUPitch4      0x0c44
+#define H_SPUPitch5      0x0c54
+#define H_SPUPitch6      0x0c64
+#define H_SPUPitch7      0x0c74
+#define H_SPUPitch8      0x0c84
+#define H_SPUPitch9      0x0c94
+#define H_SPUPitch10     0x0ca4
+#define H_SPUPitch11     0x0cb4
+#define H_SPUPitch12     0x0cc4
+#define H_SPUPitch13     0x0cd4
+#define H_SPUPitch14     0x0ce4
+#define H_SPUPitch15     0x0cf4
+#define H_SPUPitch16     0x0d04
+#define H_SPUPitch17     0x0d14
+#define H_SPUPitch18     0x0d24
+#define H_SPUPitch19     0x0d34
+#define H_SPUPitch20     0x0d44
+#define H_SPUPitch21     0x0d54
+#define H_SPUPitch22     0x0d64
+#define H_SPUPitch23     0x0d74
+
+#define H_SPUStartAdr0   0x0c06
+#define H_SPUStartAdr1   0x0c16
+#define H_SPUStartAdr2   0x0c26
+#define H_SPUStartAdr3   0x0c36
+#define H_SPUStartAdr4   0x0c46
+#define H_SPUStartAdr5   0x0c56
+#define H_SPUStartAdr6   0x0c66
+#define H_SPUStartAdr7   0x0c76
+#define H_SPUStartAdr8   0x0c86
+#define H_SPUStartAdr9   0x0c96
+#define H_SPUStartAdr10  0x0ca6
+#define H_SPUStartAdr11  0x0cb6
+#define H_SPUStartAdr12  0x0cc6
+#define H_SPUStartAdr13  0x0cd6
+#define H_SPUStartAdr14  0x0ce6
+#define H_SPUStartAdr15  0x0cf6
+#define H_SPUStartAdr16  0x0d06
+#define H_SPUStartAdr17  0x0d16
+#define H_SPUStartAdr18  0x0d26
+#define H_SPUStartAdr19  0x0d36
+#define H_SPUStartAdr20  0x0d46
+#define H_SPUStartAdr21  0x0d56
+#define H_SPUStartAdr22  0x0d66
+#define H_SPUStartAdr23  0x0d76
+
+#define H_SPULoopAdr0   0x0c0e
+#define H_SPULoopAdr1   0x0c1e
+#define H_SPULoopAdr2   0x0c2e
+#define H_SPULoopAdr3   0x0c3e
+#define H_SPULoopAdr4   0x0c4e
+#define H_SPULoopAdr5   0x0c5e
+#define H_SPULoopAdr6   0x0c6e
+#define H_SPULoopAdr7   0x0c7e
+#define H_SPULoopAdr8   0x0c8e
+#define H_SPULoopAdr9   0x0c9e
+#define H_SPULoopAdr10  0x0cae
+#define H_SPULoopAdr11  0x0cbe
+#define H_SPULoopAdr12  0x0cce
+#define H_SPULoopAdr13  0x0cde
+#define H_SPULoopAdr14  0x0cee
+#define H_SPULoopAdr15  0x0cfe
+#define H_SPULoopAdr16  0x0d0e
+#define H_SPULoopAdr17  0x0d1e
+#define H_SPULoopAdr18  0x0d2e
+#define H_SPULoopAdr19  0x0d3e
+#define H_SPULoopAdr20  0x0d4e
+#define H_SPULoopAdr21  0x0d5e
+#define H_SPULoopAdr22  0x0d6e
+#define H_SPULoopAdr23  0x0d7e
+
+#define H_SPU_ADSRLevel0   0x0c08
+#define H_SPU_ADSRLevel1   0x0c18
+#define H_SPU_ADSRLevel2   0x0c28
+#define H_SPU_ADSRLevel3   0x0c38
+#define H_SPU_ADSRLevel4   0x0c48
+#define H_SPU_ADSRLevel5   0x0c58
+#define H_SPU_ADSRLevel6   0x0c68
+#define H_SPU_ADSRLevel7   0x0c78
+#define H_SPU_ADSRLevel8   0x0c88
+#define H_SPU_ADSRLevel9   0x0c98
+#define H_SPU_ADSRLevel10  0x0ca8
+#define H_SPU_ADSRLevel11  0x0cb8
+#define H_SPU_ADSRLevel12  0x0cc8
+#define H_SPU_ADSRLevel13  0x0cd8
+#define H_SPU_ADSRLevel14  0x0ce8
+#define H_SPU_ADSRLevel15  0x0cf8
+#define H_SPU_ADSRLevel16  0x0d08
+#define H_SPU_ADSRLevel17  0x0d18
+#define H_SPU_ADSRLevel18  0x0d28
+#define H_SPU_ADSRLevel19  0x0d38
+#define H_SPU_ADSRLevel20  0x0d48
+#define H_SPU_ADSRLevel21  0x0d58
+#define H_SPU_ADSRLevel22  0x0d68
+#define H_SPU_ADSRLevel23  0x0d78
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/regs.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,34 @@
+/***************************************************************************
+                           regs.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+
+static void SoundOn(int start,int end,u16 val);
+static void SoundOff(int start,int end,u16 val);
+static void FModOn(int start,int end,u16 val);
+static void NoiseOn(int start,int end,u16 val);
+static void SetVolumeLR(int right, u8 ch,s16 vol);
+static void SetPitch(int ch,u16 val);
+void SPUwriteRegister(u32 reg, u16 val);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/reverb.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,383 @@
+/***************************************************************************
+                          reverb.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2003/03/17 - xodnizel
+// - Implemented Neill's 44.1Khz-22050Hz downsampling data
+//   I also need to check if the ~4 sample delay doesn't screw any sounds
+//   up by making things too out of phase.  It could be fixed easily(elsewhere).
+//
+// 2003/01/19 - Pete
+// - added Neill's reverb (see at the end of file)
+//
+// 2002/12/26 - Pete
+// - adjusted reverb handling
+//
+// 2002/08/14 - Pete
+// - added extra reverb
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#define _IN_REVERB
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// globals
+////////////////////////////////////////////////////////////////////////
+
+// REVERB info and timing vars...
+
+////////////////////////////////////////////////////////////////////////
+
+static INLINE s64 g_buffer(int iOff)                          // get_buffer content helper: takes care about wraps
+{
+ s16 * p=(s16 *)spuMem;
+ iOff=(iOff*4)+rvb.CurrAddr;
+ while(iOff>0x3FFFF)       iOff=rvb.StartAddr+(iOff-0x40000);
+ while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
+ return (int)(s16)BFLIP16(*(p+iOff));
+}
+
+////////////////////////////////////////////////////////////////////////
+
+static INLINE void s_buffer(int iOff,int iVal)                // set_buffer content helper: takes care about wraps and clipping
+{
+ s16 * p=(s16 *)spuMem;
+ iOff=(iOff*4)+rvb.CurrAddr;
+ while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
+ while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
+ if(iVal<-32768L) iVal=-32768L;
+ if(iVal>32767L) iVal=32767L;
+ *(p+iOff)=(s16)BFLIP16((s16)iVal);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+static INLINE void s_buffer1(int iOff,int iVal)                // set_buffer (+1 sample) content helper: takes care about wraps and clipping
+{
+ s16 * p=(s16 *)spuMem;
+ iOff=(iOff*4)+rvb.CurrAddr+1;
+ while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
+ while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
+ if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
+ *(p+iOff)=(s16)BFLIP16((s16)iVal);
+}
+
+static INLINE void MixREVERBLeftRight(s32 *oleft, s32 *oright, s32 inleft, s32 inright)
+{
+   static s32 downbuf[2][8];
+   static s32 upbuf[2][8];
+   static int dbpos=0,ubpos=0;
+   static s32 downcoeffs[8]={ /* Symmetry is sexy. */
+				1283,5344,10895,15243,
+				15243,10895,5344,1283
+			       };
+   int x;
+
+   if(!rvb.StartAddr)                                  // reverb is off
+    {
+     rvb.iRVBLeft=rvb.iRVBRight=0;
+     return;
+    }
+
+   //if(inleft<-32767 || inleft>32767) printf("%d\n",inleft);
+   //if(inright<-32767 || inright>32767) printf("%d\n",inright);
+   downbuf[0][dbpos]=inleft;
+   downbuf[1][dbpos]=inright;
+   dbpos=(dbpos+1)&7;
+
+   if(dbpos&1)                                          // we work on every second left value: downsample to 22 khz
+    {
+     if(spuCtrl&0x80)                                  // -> reverb on? oki
+      {
+       int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;
+       s32 INPUT_SAMPLE_L=0;                         
+       s32 INPUT_SAMPLE_R=0;
+
+       for(x=0;x<8;x++)
+       {
+        INPUT_SAMPLE_L+=(downbuf[0][(dbpos+x)&7]*downcoeffs[x])>>8; /* Lose insignificant
+							    digits to prevent
+							    overflow(check this) */
+        INPUT_SAMPLE_R+=(downbuf[1][(dbpos+x)&7]*downcoeffs[x])>>8;
+       }
+
+       INPUT_SAMPLE_L>>=(16-8);
+       INPUT_SAMPLE_R>>=(16-8);
+       {
+        const s64 IIR_INPUT_A0 = ((g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_L * rvb.IN_COEF_L)>>15);
+        const s64 IIR_INPUT_A1 = ((g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_R * rvb.IN_COEF_R)>>15);
+        const s64 IIR_INPUT_B0 = ((g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_L * rvb.IN_COEF_L)>>15);
+        const s64 IIR_INPUT_B1 = ((g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)>>15) + ((INPUT_SAMPLE_R * rvb.IN_COEF_R)>>15);
+        const s64 IIR_A0 = ((IIR_INPUT_A0 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))>>15);
+        const s64 IIR_A1 = ((IIR_INPUT_A1 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))>>15);
+        const s64 IIR_B0 = ((IIR_INPUT_B0 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))>>15);
+        const s64 IIR_B1 = ((IIR_INPUT_B1 * rvb.IIR_ALPHA)>>15) + ((g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))>>15);
+
+       s_buffer1(rvb.IIR_DEST_A0, IIR_A0);
+       s_buffer1(rvb.IIR_DEST_A1, IIR_A1);
+       s_buffer1(rvb.IIR_DEST_B0, IIR_B0);
+       s_buffer1(rvb.IIR_DEST_B1, IIR_B1);
+ 
+       ACC0 = ((g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)>>15) +
+              ((g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)>>15) +
+              ((g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)>>15) +
+              ((g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)>>15);
+       ACC1 = ((g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)>>15) +
+              ((g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)>>15) +
+              ((g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)>>15) +
+              ((g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)>>15);
+
+       FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A);
+       FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A);
+       FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B);
+       FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B);
+
+       s_buffer(rvb.MIX_DEST_A0, ACC0 - ((FB_A0 * rvb.FB_ALPHA)>>15));
+       s_buffer(rvb.MIX_DEST_A1, ACC1 - ((FB_A1 * rvb.FB_ALPHA)>>15));
+       
+       s_buffer(rvb.MIX_DEST_B0, ((rvb.FB_ALPHA * ACC0)>>15) - ((FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))>>15) - ((FB_B0 * rvb.FB_X)>>15));
+       s_buffer(rvb.MIX_DEST_B1, ((rvb.FB_ALPHA * ACC1)>>15) - ((FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))>>15) - ((FB_B1 * rvb.FB_X)>>15));
+ 
+       rvb.iRVBLeft  = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3;
+       rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3;
+
+       rvb.iRVBLeft  = ((s64)rvb.iRVBLeft * rvb.VolLeft)  >> 14;
+       rvb.iRVBRight = ((s64)rvb.iRVBRight * rvb.VolRight) >> 14;
+
+       upbuf[0][ubpos]=rvb.iRVBLeft;
+       upbuf[1][ubpos]=rvb.iRVBRight;
+       ubpos=(ubpos+1)&7;
+       } // Bracket hack(et).
+      }
+     else                                              // -> reverb off
+      {
+       rvb.iRVBLeft=rvb.iRVBRight=0;
+       return;
+      }
+     rvb.CurrAddr++;
+     if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
+    }
+    else
+    {
+     upbuf[0][ubpos]=0;
+     upbuf[1][ubpos]=0;
+     ubpos=(ubpos+1)&7;
+    }
+   {
+    s32 retl=0,retr=0;
+    for(x=0;x<8;x++)
+    {
+     retl+=(upbuf[0][(ubpos+x)&7]*downcoeffs[x])>>8;
+     retr+=(upbuf[1][(ubpos+x)&7]*downcoeffs[x])>>8;
+    }
+    retl>>=(16-8-1); /* -1 To adjust for the null padding. */
+    retr>>=(16-8-1);
+
+    *oleft+=retl;
+    *oright+=retr;
+   }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#endif
+
+/*
+-----------------------------------------------------------------------------
+PSX reverb hardware notes
+by Neill Corlett
+-----------------------------------------------------------------------------
+
+Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
+yadda yadda.
+
+-----------------------------------------------------------------------------
+
+Basics
+------
+
+- The reverb buffer is 22khz 16-bit mono PCM.
+- It starts at the reverb address given by 1DA2, extends to
+  the end of sound RAM, and wraps back to the 1DA2 address.
+
+Setting the address at 1DA2 resets the current reverb work address.
+
+This work address ALWAYS increments every 1/22050 sec., regardless of
+whether reverb is enabled (bit 7 of 1DAA set).
+
+And the contents of the reverb buffer ALWAYS play, scaled by the
+"reverberation depth left/right" volumes (1D84/1D86).
+(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)
+
+-----------------------------------------------------------------------------
+
+Register names
+--------------
+
+These are probably not their real names.
+These are probably not even correct names.
+We will use them anyway, because we can.
+
+1DC0: FB_SRC_A       (offset)
+1DC2: FB_SRC_B       (offset)
+1DC4: IIR_ALPHA      (coef.)
+1DC6: ACC_COEF_A     (coef.)
+1DC8: ACC_COEF_B     (coef.)
+1DCA: ACC_COEF_C     (coef.)
+1DCC: ACC_COEF_D     (coef.)
+1DCE: IIR_COEF       (coef.)
+1DD0: FB_ALPHA       (coef.)
+1DD2: FB_X           (coef.)
+1DD4: IIR_DEST_A0    (offset)
+1DD6: IIR_DEST_A1    (offset)
+1DD8: ACC_SRC_A0     (offset)
+1DDA: ACC_SRC_A1     (offset)
+1DDC: ACC_SRC_B0     (offset)
+1DDE: ACC_SRC_B1     (offset)
+1DE0: IIR_SRC_A0     (offset)
+1DE2: IIR_SRC_A1     (offset)
+1DE4: IIR_DEST_B0    (offset)
+1DE6: IIR_DEST_B1    (offset)
+1DE8: ACC_SRC_C0     (offset)
+1DEA: ACC_SRC_C1     (offset)
+1DEC: ACC_SRC_D0     (offset)
+1DEE: ACC_SRC_D1     (offset)
+1DF0: IIR_SRC_B1     (offset)
+1DF2: IIR_SRC_B0     (offset)
+1DF4: MIX_DEST_A0    (offset)
+1DF6: MIX_DEST_A1    (offset)
+1DF8: MIX_DEST_B0    (offset)
+1DFA: MIX_DEST_B1    (offset)
+1DFC: IN_COEF_L      (coef.)
+1DFE: IN_COEF_R      (coef.)
+
+The coefficients are signed fractional values.
+-32768 would be -1.0
+ 32768 would be  1.0 (if it were possible... the highest is of course 32767)
+
+The offsets are (byte/8) offsets into the reverb buffer.
+i.e. you multiply them by 8, you get byte offsets.
+You can also think of them as (samples/4) offsets.
+They appear to be signed.  They can be negative.
+None of the documented presets make them negative, though.
+
+Yes, 1DF0 and 1DF2 appear to be backwards.  Not a typo.
+
+-----------------------------------------------------------------------------
+
+What it does
+------------
+
+We take all reverb sources:
+- regular channels that have the reverb bit on
+- cd and external sources, if their reverb bits are on
+and mix them into one stereo 44100hz signal.
+
+Lowpass/downsample that to 22050hz.  The PSX uses a proper bandlimiting
+algorithm here, but I haven't figured out the hysterically exact specifics.
+I use an 8-tap filter with these coefficients, which are nice but probably
+not the real ones:
+
+0.037828187894
+0.157538631280
+0.321159685278
+0.449322115345
+0.449322115345
+0.321159685278
+0.157538631280
+0.037828187894
+
+So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
+
+* IN MY EMULATION, I divide these by 2 to make it clip less.
+  (and of course the L/R output coefficients are adjusted to compensate)
+  The real thing appears to not do this.
+
+At every 22050hz tick:
+- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb
+  steady-state algorithm described below
+- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer
+  (This part may not be exactly right and I guessed at the coefs. TODO: check later.)
+  L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])
+  R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])
+- Advance the current buffer position by 1 sample
+
+The wet out L and R are then upsampled to 44100hz and played at the
+"reverberation depth left/right" (1D84/1D86) volume, independent of the main
+volume.
+
+-----------------------------------------------------------------------------
+
+Reverb steady-state
+-------------------
+
+The reverb steady-state algorithm is fairly clever, and of course by
+"clever" I mean "batshit insane".
+
+buffer[x] is relative to the current buffer position, not the beginning of
+the buffer.  Note that all buffer offsets must wrap around so they're
+contained within the reverb work area.
+
+Clipping is performed at the end... maybe also sooner, but definitely at
+the end.
+
+IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
+IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
+IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
+IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
+
+IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);
+IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);
+IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);
+IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);
+
+buffer[IIR_DEST_A0 + 1sample] = IIR_A0;
+buffer[IIR_DEST_A1 + 1sample] = IIR_A1;
+buffer[IIR_DEST_B0 + 1sample] = IIR_B0;
+buffer[IIR_DEST_B1 + 1sample] = IIR_B1;
+
+ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +
+       buffer[ACC_SRC_B0] * ACC_COEF_B +
+       buffer[ACC_SRC_C0] * ACC_COEF_C +
+       buffer[ACC_SRC_D0] * ACC_COEF_D;
+ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +
+       buffer[ACC_SRC_B1] * ACC_COEF_B +
+       buffer[ACC_SRC_C1] * ACC_COEF_C +
+       buffer[ACC_SRC_D1] * ACC_COEF_D;
+
+FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];
+FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];
+FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];
+FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];
+
+buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;
+buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;
+buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;
+buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;
+
+-----------------------------------------------------------------------------
+*/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/spu.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,641 @@
+/***************************************************************************
+                            spu.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+                       
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+                           
+//*************************************************************************//
+// History of changes:
+//
+// 2003/03/01 - linuzappz
+// - libraryName changes using ALSA
+//
+// 2003/02/28 - Pete
+// - added option for type of interpolation
+// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile)
+// - added MONO support for MSWindows DirectSound
+//
+// 2003/02/20 - kode54
+// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault
+//
+// 2003/02/19 - kode54
+// - moved SPU IRQ handler and changed sample flag processing
+//
+// 2003/02/18 - kode54
+// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that
+//   ADSR timing is relative to the frequency at which a sample is played... I guess
+//   this remains to be seen, and I don't know whether ADSR is applied to noise channels...
+//
+// 2003/02/09 - kode54
+// - one-shot samples now process the end block before stopping
+// - in light of removing fmod hack, now processing ADSR on frequency channel as well
+//
+// 2003/02/08 - kode54
+// - replaced easy interpolation with gaussian
+// - removed fmod averaging hack
+// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :)
+//
+// 2003/02/08 - linuzappz
+// - small bugfix for one usleep that was 1 instead of 1000
+// - added iDisStereo for no stereo (Linux)
+//
+// 2003/01/22 - Pete
+// - added easy interpolation & small noise adjustments
+//
+// 2003/01/19 - Pete
+// - added Neill's reverb
+//
+// 2003/01/12 - Pete
+// - added recording window handlers
+//
+// 2003/01/06 - Pete
+// - added Neill's ADSR timings
+//
+// 2002/12/28 - Pete
+// - adjusted spu irq handling, fmod handling and loop handling
+//
+// 2002/08/14 - Pete
+// - added extra reverb
+//
+// 2002/06/08 - linuzappz
+// - SPUupdate changed for SPUasync
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#define _IN_SPU
+
+#include "../peops/stdafx.h"
+#include "../peops/externals.h"
+#include "../peops/regs.h"
+#include "../peops/registers.h"
+#include "../peops/spu.h"
+
+void SPUirq(void) ;
+
+//#include "PsxMem.h"
+//#include "driver.h"
+
+////////////////////////////////////////////////////////////////////////
+// globals
+////////////////////////////////////////////////////////////////////////
+
+// psx buffer / addresses
+
+static u16  regArea[0x200];
+static u16  spuMem[256*1024];
+static u8 * spuMemC;
+static u8 * pSpuIrq=0;
+static u8 * pSpuBuffer;
+
+// user settings          
+static int             iVolume;
+                               
+// MAIN infos struct for each channel
+
+static SPUCHAN         s_chan[MAXCHAN+1];                     // channel + 1 infos (1 is security for fmod handling)
+static REVERBInfo      rvb;
+
+static u32   dwNoiseVal=1;                          // global noise generator
+
+static u16  spuCtrl=0;                             // some vars to store psx reg infos
+static u16  spuStat=0;
+static u16  spuIrq=0;             
+static u32  spuAddr=0xffffffff;                    // address into spu mem
+static int  bSPUIsOpen=0;
+
+static const int f[5][2] = {   
+			{    0,  0  },
+                        {   60,  0  },
+                        {  115, -52 },
+                        {   98, -55 },
+                        {  122, -60 } };
+s16 * pS;
+static s32 ttemp;
+
+////////////////////////////////////////////////////////////////////////
+// CODE AREA
+////////////////////////////////////////////////////////////////////////
+
+// dirty inline func includes
+
+#include "../peops/reverb.c"        
+#include "../peops/adsr.c"
+
+// Try this to increase speed.
+#include "../peops/registers.c"
+#include "../peops/dma.c"
+
+////////////////////////////////////////////////////////////////////////
+// helpers for so-called "gauss interpolation"
+
+#define gval0 (((int *)(&s_chan[ch].SB[29]))[gpos])
+#define gval(x) (((int *)(&s_chan[ch].SB[29]))[(gpos+x)&3])
+
+#include "gauss_i.h"
+
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// START SOUND... called by main thread to setup a new sound on a channel
+////////////////////////////////////////////////////////////////////////
+
+static INLINE void StartSound(int ch)
+{
+ StartADSR(ch);
+
+ s_chan[ch].pCurr=s_chan[ch].pStart;                   // set sample start
+                         
+ s_chan[ch].s_1=0;                                     // init mixing vars
+ s_chan[ch].s_2=0;
+ s_chan[ch].iSBPos=28;
+
+ s_chan[ch].bNew=0;                                    // init channel flags
+ s_chan[ch].bStop=0;                                   
+ s_chan[ch].bOn=1;
+
+ s_chan[ch].SB[29]=0;                                  // init our interpolation helpers
+ s_chan[ch].SB[30]=0;
+
+ s_chan[ch].spos=0x40000L;s_chan[ch].SB[28]=0;  // -> start with more decoding
+}
+
+////////////////////////////////////////////////////////////////////////
+// MAIN SPU FUNCTION
+// here is the main job handler... thread, timer or direct func call
+// basically the whole sound processing is done in this fat func!
+////////////////////////////////////////////////////////////////////////
+
+static u32 sampcount;
+static u32 decaybegin;
+static u32 decayend;
+
+// Counting to 65536 results in full volume offage.
+void setlength(s32 stop, s32 fade)
+{
+ if(stop==~0)
+ {
+  decaybegin=~0;
+ }
+ else
+ {
+  stop=(stop*441)/10;
+  fade=(fade*441)/10;
+
+  decaybegin=stop;
+  decayend=stop+fade;
+ }
+}
+
+#define CLIP(_x) {if(_x>32767) _x=32767; if(_x<-32767) _x=-32767;}
+int SPUasync(u32 cycles)
+{
+ int volmul=iVolume;
+ static s32 dosampies;
+ s32 temp;
+
+ ttemp+=cycles;
+ dosampies=ttemp/384;
+ if(!dosampies) return(1);
+ ttemp-=dosampies*384;
+ temp=dosampies;
+
+ while(temp)
+ {
+   s32 revLeft=0, revRight=0;
+   s32 sl=0, sr=0;
+   int ch,fa;
+
+   temp--;
+   //--------------------------------------------------//
+   //- main channel loop                              -// 
+   //--------------------------------------------------//
+    {
+     for(ch=0;ch<MAXCHAN;ch++)                         // loop em all.
+      {
+       if(s_chan[ch].bNew) StartSound(ch);             // start new sound
+       if(!s_chan[ch].bOn) continue;                   // channel not playing? next
+
+
+       if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq)   // new psx frequency?
+        {
+         s_chan[ch].iUsedFreq=s_chan[ch].iActFreq;     // -> take it and calc steps
+         s_chan[ch].sinc=s_chan[ch].iRawPitch<<4;
+         if(!s_chan[ch].sinc) s_chan[ch].sinc=1;
+        }
+
+         while(s_chan[ch].spos>=0x10000L)
+          {
+           if(s_chan[ch].iSBPos==28)                   // 28 reached?
+            {
+	     int predict_nr,shift_factor,flags,d,s;
+	     u8* start;unsigned int nSample;
+	     int s_1,s_2;
+
+             start=s_chan[ch].pCurr;                   // set up the current pos
+
+             if (start == (u8*)-1)          // special "stop" sign
+              {
+               s_chan[ch].bOn=0;                       // -> turn everything off
+               s_chan[ch].ADSRX.lVolume=0;
+               s_chan[ch].ADSRX.EnvelopeVol=0;
+               goto ENDX;                              // -> and done for this channel
+              }
+
+             s_chan[ch].iSBPos=0;	// Reset buffer play index.
+
+             //////////////////////////////////////////// spu irq handler here? mmm... do it later
+
+             s_1=s_chan[ch].s_1;
+             s_2=s_chan[ch].s_2;
+
+             predict_nr=(int)*start;start++;           
+             shift_factor=predict_nr&0xf;
+             predict_nr >>= 4;
+             flags=(int)*start;start++;
+
+             // -------------------------------------- // 
+	     // Decode new samples into s_chan[ch].SB[0 through 27]
+             for (nSample=0;nSample<28;start++)      
+              {
+               d=(int)*start;
+               s=((d&0xf)<<12);
+               if(s&0x8000) s|=0xffff0000;
+
+               fa=(s >> shift_factor);
+               fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+               s_2=s_1;s_1=fa;
+               s=((d & 0xf0) << 8);
+
+               s_chan[ch].SB[nSample++]=fa;
+
+               if(s&0x8000) s|=0xffff0000;
+               fa=(s>>shift_factor);              
+               fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+               s_2=s_1;s_1=fa;
+
+               s_chan[ch].SB[nSample++]=fa;
+              }     
+
+             //////////////////////////////////////////// irq check
+
+             if(spuCtrl&0x40)         			// irq active?
+              {
+               if((pSpuIrq >  start-16 &&              // irq address reached?
+                   pSpuIrq <= start) ||
+                  ((flags&1) &&                        // special: irq on looping addr, when stop/loop flag is set 
+                   (pSpuIrq >  s_chan[ch].pLoop-16 && 
+                    pSpuIrq <= s_chan[ch].pLoop)))
+               {
+		 //extern s32 spuirqvoodoo;
+                 s_chan[ch].iIrqDone=1;                // -> debug flag
+		 SPUirq();
+		//puts("IRQ");
+		 //if(spuirqvoodoo!=-1)
+		 //{
+		 // spuirqvoodoo=temp*384;
+		 // temp=0;
+		 //}
+                }
+              }
+      
+             //////////////////////////////////////////// flag handler
+
+             if((flags&4) && (!s_chan[ch].bIgnoreLoop))
+              s_chan[ch].pLoop=start-16;               // loop adress
+
+             if(flags&1)                               // 1: stop/loop
+              {
+               // We play this block out first...
+               //if(!(flags&2))                          // 1+2: do loop... otherwise: stop
+               if(flags!=3 || s_chan[ch].pLoop==NULL)  // PETE: if we don't check exactly for 3, loop hang ups will happen (DQ4, for example)
+                {                                      // and checking if pLoop is set avoids crashes, yeah
+                 start = (u8*)-1;
+                }
+               else
+                {
+                 start = s_chan[ch].pLoop;
+                }
+              }
+
+             s_chan[ch].pCurr=start;                   // store values for next cycle
+             s_chan[ch].s_1=s_1;
+             s_chan[ch].s_2=s_2;      
+
+             ////////////////////////////////////////////
+            }
+
+           fa=s_chan[ch].SB[s_chan[ch].iSBPos++];      // get sample data
+
+           if((spuCtrl&0x4000)==0) fa=0;               // muted?
+	   else CLIP(fa);
+
+	    {
+	     int gpos;
+             gpos = s_chan[ch].SB[28];
+             gval0 = fa;
+             gpos = (gpos+1) & 3;
+             s_chan[ch].SB[28] = gpos;
+	    }
+           s_chan[ch].spos -= 0x10000L;
+          }
+
+         ////////////////////////////////////////////////
+         // noise handler... just produces some noise data
+         // surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used...
+         // and sometimes the noise will be used as fmod modulation... pfff
+
+         if(s_chan[ch].bNoise)
+          {
+	   //puts("Noise");
+           if((dwNoiseVal<<=1)&0x80000000L)
+            {
+             dwNoiseVal^=0x0040001L;
+             fa=((dwNoiseVal>>2)&0x7fff);
+             fa=-fa;
+            }
+           else fa=(dwNoiseVal>>2)&0x7fff;
+
+           // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val
+           fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl&0x3f00)>>9))+1));
+           if(fa>32767L)  fa=32767L;
+           if(fa<-32767L) fa=-32767L;              
+           s_chan[ch].iOldNoise=fa;
+
+          }                                            //----------------------------------------
+         else                                         // NO NOISE (NORMAL SAMPLE DATA) HERE 
+          {
+             int vl, vr, gpos;
+             vl = (s_chan[ch].spos >> 6) & ~3;
+             gpos = s_chan[ch].SB[28];
+             vr=(gauss[vl]*gval0)>>9;
+             vr+=(gauss[vl+1]*gval(1))>>9;
+             vr+=(gauss[vl+2]*gval(2))>>9;
+             vr+=(gauss[vl+3]*gval(3))>>9;
+             fa = vr>>2;
+          }
+
+         s_chan[ch].sval = (MixADSR(ch) * fa)>>10;     // / 1023;  // add adsr
+         if(s_chan[ch].bFMod==2)                       // fmod freq channel
+         {
+           int NP=s_chan[ch+1].iRawPitch;
+           NP=((32768L+s_chan[ch].sval)*NP)>>15; ///32768L;
+
+           if(NP>0x3fff) NP=0x3fff;
+           if(NP<0x1)    NP=0x1;
+                                                        
+	   // mmmm... if I do this, all is screwed              
+	  //           s_chan[ch+1].iRawPitch=NP;
+
+           NP=(44100L*NP)/(4096L);                     // calc frequency
+
+           s_chan[ch+1].iActFreq=NP;
+           s_chan[ch+1].iUsedFreq=NP;
+           s_chan[ch+1].sinc=(((NP/10)<<16)/4410);
+           if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1;
+
+		// mmmm... set up freq decoding positions?
+		//           s_chan[ch+1].iSBPos=28;
+		//           s_chan[ch+1].spos=0x10000L;
+          }                    
+         else
+          {                                          
+           //////////////////////////////////////////////
+           // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff)
+	   int tmpl,tmpr;
+
+		if (1) //ao_channel_enable[ch+PSF_1]) {
+		{
+			tmpl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14;
+			tmpr=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14;
+		} else {
+			tmpl = 0;
+			tmpr = 0;
+		}
+	   sl+=tmpl;
+	   sr+=tmpr;
+
+	   if(((rvb.Enabled>>ch)&1) && (spuCtrl&0x80))
+	   {
+	    revLeft+=tmpl;
+	    revRight+=tmpr;
+	   }
+          }
+
+         s_chan[ch].spos += s_chan[ch].sinc;             
+ ENDX:   ;                                                      
+      }
+    }                                                         
+
+  ///////////////////////////////////////////////////////
+  // mix all channels (including reverb) into one buffer
+  MixREVERBLeftRight(&sl,&sr,revLeft,revRight);
+//  printf("sampcount %d decaybegin %d decayend %d\n", sampcount, decaybegin, decayend);
+  if(sampcount>=decaybegin)
+  {
+   s32 dmul;
+   if(decaybegin!=~0) // Is anyone REALLY going to be playing a song
+		      // for 13 hours?
+   {
+    if(sampcount>=decayend) 
+    {
+//       	    ao_song_done = 1;
+	    return(0);
+    }
+    dmul=256-(256*(sampcount-decaybegin)/(decayend-decaybegin));
+    sl=(sl*dmul)>>8;
+    sr=(sr*dmul)>>8;
+   }
+  }
+
+  sampcount++;
+  sl=(sl*volmul)>>8;
+  sr=(sr*volmul)>>8;
+
+  //{
+  // static double asl=0;
+  // static double asr=0;
+   
+  // asl+=(sl-asl)/5;
+  // asr+=(sl-asr)/5;
+
+   //sl-=asl;
+   //sr-=asr;
+
+  // if(sl>32767 || sl < -32767) printf("Left: %d, %f\n",sl,asl);
+  // if(sr>32767 || sr < -32767) printf("Right: %d, %f\n",sl,asl);
+  //}
+
+  if(sl>32767) sl=32767; if(sl<-32767) sl=-32767;
+  if(sr>32767) sr=32767; if(sr<-32767) sr=-32767;
+
+  *pS++=sl;
+  *pS++=sr;
+ }
+
+ return(1);
+}
+
+void SPU_flushboot(void)
+{
+   if((u8*)pS>((u8*)pSpuBuffer+1024))
+   {
+    spu_update((u8*)pSpuBuffer,(u8*)pS-(u8*)pSpuBuffer);
+    pS=(s16 *)pSpuBuffer;
+   }
+}   
+
+#ifdef TIMEO
+static u64 begintime;
+static u64 gettime64(void)
+{
+ struct timeval tv;
+ u64 ret;
+
+ gettimeofday(&tv,0);
+ ret=tv.tv_sec;
+ ret*=1000000;
+ ret+=tv.tv_usec;
+ return(ret);
+}
+#endif
+////////////////////////////////////////////////////////////////////////
+// INIT/EXIT STUFF
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// SPUINIT: this func will be called first by the main emu
+////////////////////////////////////////////////////////////////////////
+              
+int SPUinit(void)
+{
+ spuMemC=(u8*)spuMem;                      // just small setup
+ memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN));
+ memset((void *)&rvb,0,sizeof(REVERBInfo));
+ memset(regArea,0,sizeof(regArea));
+ memset(spuMem,0,sizeof(spuMem));
+ InitADSR();
+ sampcount=ttemp=0;
+ #ifdef TIMEO
+ begintime=gettime64();
+ #endif
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SETUPSTREAMS: init most of the spu buffers
+////////////////////////////////////////////////////////////////////////
+
+void SetupStreams(void)
+{ 
+ int i;
+
+ pSpuBuffer=(u8*)malloc(32768);            // alloc mixing buffer
+ pS=(s16 *)pSpuBuffer;
+
+ for(i=0;i<MAXCHAN;i++)                                // loop sound channels
+  {
+   s_chan[i].ADSRX.SustainLevel = 1024;                // -> init sustain
+   s_chan[i].iIrqDone=0;
+   s_chan[i].pLoop=spuMemC;
+   s_chan[i].pStart=spuMemC;
+   s_chan[i].pCurr=spuMemC;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// REMOVESTREAMS: free most buffer
+////////////////////////////////////////////////////////////////////////
+
+void RemoveStreams(void)
+{ 
+ free(pSpuBuffer);                                     // free mixing buffer
+ pSpuBuffer=NULL;
+
+ #ifdef TIMEO
+ {
+  u64 tmp;
+  tmp=gettime64();
+  tmp-=begintime;
+  if(tmp)
+   tmp=(u64)sampcount*1000000/tmp;
+  printf("%lld samples per second\n",tmp);
+ }
+ #endif
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// SPUOPEN: called by main emu after init
+////////////////////////////////////////////////////////////////////////
+   
+int SPUopen(void)
+{
+ if(bSPUIsOpen) return 0;                              // security for some stupid main emus
+ spuIrq=0;                       
+
+ spuStat=spuCtrl=0;
+ spuAddr=0xffffffff;
+ dwNoiseVal=1;
+
+ spuMemC=(u8*)spuMem;      
+ memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN));
+ pSpuIrq=0;
+
+ iVolume=255; //85;
+ SetupStreams();                                       // prepare streaming
+
+ bSPUIsOpen=1;
+
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// SPUCLOSE: called before shutdown
+////////////////////////////////////////////////////////////////////////
+
+int SPUclose(void)
+{
+ if(!bSPUIsOpen) return 0;                             // some security
+
+ bSPUIsOpen=0;                                         // no more open
+
+ RemoveStreams();                                      // no more streaming
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SPUSHUTDOWN: called by main emu on final exit
+////////////////////////////////////////////////////////////////////////
+
+int SPUshutdown(void)
+{
+ return 0;
+}
+
+void SPUinjectRAMImage(u16 *pIncoming)
+{
+	int i;
+
+	for (i = 0; i < (256*1024); i++)
+	{
+		spuMem[i] = pIncoming[i];
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/spu.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,39 @@
+/***************************************************************************
+                            spu.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+void sexyd_update(unsigned char* pSound,long lBytes);
+
+int SPUasync(u32 cycles);
+void SPU_flushboot(void);
+int SPUinit(void);
+int SPUopen(void);
+int SPUclose(void);
+int SPUshutdown(void);
+void SPUinjectRAMImage(u16 *pIncoming);
+void SPUreadDMAMem(u32 usPSXMem,int iSize);
+void SPUwriteDMAMem(u32 usPSXMem,int iSize);
+u16 SPUreadRegister(u32 reg);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops/stdafx.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,29 @@
+/***************************************************************************
+                           StdAfx.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include <stdlib.h>
+#include <string.h> 
+#include <math.h>  
Binary file src/psf2/peops2/.DS_Store has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/License.txt	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,282 @@
+#########################################################################
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/adsr.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,656 @@
+/***************************************************************************
+                          adsr.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2003/05/14 - xodnizel
+// - removed stopping of reverb on sample end
+//
+// 2003/01/06 - Pete
+// - added Neill's ADSR timings
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "stdafx.h"
+
+#define _IN_ADSR
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// ADSR func
+////////////////////////////////////////////////////////////////////////
+
+unsigned long RateTable[160];
+
+void InitADSR(void)                                    // INIT ADSR
+{
+ unsigned long r,rs,rd;int i;
+
+ memset(RateTable,0,sizeof(unsigned long)*160);        // build the rate table according to Neill's rules (see at bottom of file)
+
+ r=3;rs=1;rd=0;
+
+ for(i=32;i<160;i++)                                   // we start at pos 32 with the real values... everything before is 0
+  {
+   if(r<0x3FFFFFFF)
+    {
+     r+=rs;
+     rd++;if(rd==5) {rd=1;rs*=2;}
+    }
+   if(r>0x3FFFFFFF) r=0x3FFFFFFF;
+
+   RateTable[i]=r;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StartADSR(int ch)                          // MIX ADSR
+{
+ s_chan[ch].ADSRX.lVolume=1;                           // and init some adsr vars
+ s_chan[ch].ADSRX.State=0;
+ s_chan[ch].ADSRX.EnvelopeVol=0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE int MixADSR(int ch)                             // MIX ADSR
+{    
+ if(s_chan[ch].bStop)                                  // should be stopped:
+  {                                                    // do release
+   if(s_chan[ch].ADSRX.ReleaseModeExp)
+    {
+     switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7)
+      {
+       case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +0 + 32]; break;
+       case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +4 + 32]; break;
+       case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +6 + 32]; break;
+       case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +8 + 32]; break;
+       case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +9 + 32]; break;
+       case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +10+ 32]; break;
+       case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +11+ 32]; break;
+       case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x18 +12+ 32]; break;
+      }
+    }
+   else
+    {
+     s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.ReleaseRate^0x1F))-0x0C + 32];
+    }
+
+   if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+    {
+     s_chan[ch].ADSRX.EnvelopeVol=0;
+     s_chan[ch].bOn=0;
+     //s_chan[ch].bReverb=0;
+     //s_chan[ch].bNoise=0;
+    }
+
+   s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+   return s_chan[ch].ADSRX.lVolume;
+  }
+ else                                                  // not stopped yet?
+  {
+   if(s_chan[ch].ADSRX.State==0)                       // -> attack
+    {
+     if(s_chan[ch].ADSRX.AttackModeExp)
+      {
+       if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) 
+        s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32];
+       else
+        s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x18 + 32];
+      }
+     else
+      {
+       s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.AttackRate^0x7F)-0x10 + 32];
+      }
+
+     if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+      {
+       s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF;
+       s_chan[ch].ADSRX.State=1;
+      }
+
+     s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+     return s_chan[ch].ADSRX.lVolume;
+    }
+   //--------------------------------------------------//
+   if(s_chan[ch].ADSRX.State==1)                       // -> decay
+    {
+     switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7)
+      {
+       case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+0 + 32]; break;
+       case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+4 + 32]; break;
+       case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+6 + 32]; break;
+       case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+8 + 32]; break;
+       case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+9 + 32]; break;
+       case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+10+ 32]; break;
+       case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+11+ 32]; break;
+       case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[(4*(s_chan[ch].ADSRX.DecayRate^0x1F))-0x18+12+ 32]; break;
+      }
+
+     if(s_chan[ch].ADSRX.EnvelopeVol<0) s_chan[ch].ADSRX.EnvelopeVol=0;
+     if(((s_chan[ch].ADSRX.EnvelopeVol>>27)&0xF) <= s_chan[ch].ADSRX.SustainLevel)
+      {
+       s_chan[ch].ADSRX.State=2;
+      }
+
+     s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+     return s_chan[ch].ADSRX.lVolume;
+    }
+   //--------------------------------------------------//
+   if(s_chan[ch].ADSRX.State==2)                       // -> sustain
+    {
+     if(s_chan[ch].ADSRX.SustainIncrease)
+      {
+       if(s_chan[ch].ADSRX.SustainModeExp)
+        {
+         if(s_chan[ch].ADSRX.EnvelopeVol<0x60000000) 
+          s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32];
+         else
+          s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x18 + 32];
+        }
+       else
+        {
+         s_chan[ch].ADSRX.EnvelopeVol+=RateTable[(s_chan[ch].ADSRX.SustainRate^0x7F)-0x10 + 32];
+        }
+
+       if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+        {
+         s_chan[ch].ADSRX.EnvelopeVol=0x7FFFFFFF;
+        }
+      }
+     else
+      {
+       if(s_chan[ch].ADSRX.SustainModeExp)
+        {
+         switch((s_chan[ch].ADSRX.EnvelopeVol>>28)&0x7)
+          {
+           case 0: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +0 + 32];break;
+           case 1: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +4 + 32];break;
+           case 2: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +6 + 32];break;
+           case 3: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +8 + 32];break;
+           case 4: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +9 + 32];break;
+           case 5: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +10+ 32];break;
+           case 6: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +11+ 32];break;
+           case 7: s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x1B +12+ 32];break;
+          }
+        }
+       else
+        {
+         s_chan[ch].ADSRX.EnvelopeVol-=RateTable[((s_chan[ch].ADSRX.SustainRate^0x7F))-0x0F + 32];
+        }
+
+       if(s_chan[ch].ADSRX.EnvelopeVol<0) 
+        {
+         s_chan[ch].ADSRX.EnvelopeVol=0;
+        }
+      }
+     s_chan[ch].ADSRX.lVolume=s_chan[ch].ADSRX.EnvelopeVol>>21;
+     return s_chan[ch].ADSRX.lVolume;
+    }
+  }
+ return 0;
+}
+
+#endif
+
+/*
+James Higgs ADSR investigations:
+
+PSX SPU Envelope Timings
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+First, here is an extract from doomed's SPU doc, which explains the basics
+of the SPU "volume envelope": 
+
+*** doomed doc extract start ***
+
+--------------------------------------------------------------------------
+Voices.
+--------------------------------------------------------------------------
+The SPU has 24 hardware voices. These voices can be used to reproduce sample
+data, noise or can be used as frequency modulator on the next voice.
+Each voice has it's own programmable ADSR envelope filter. The main volume
+can be programmed independently for left and right output.
+
+The ADSR envelope filter works as follows:
+Ar = Attack rate, which specifies the speed at which the volume increases
+     from zero to it's maximum value, as soon as the note on is given. The
+     slope can be set to lineair or exponential.
+Dr = Decay rate specifies the speed at which the volume decreases to the
+     sustain level. Decay is always decreasing exponentially.
+Sl = Sustain level, base level from which sustain starts.
+Sr = Sustain rate is the rate at which the volume of the sustained note
+     increases or decreases. This can be either lineair or exponential.
+Rr = Release rate is the rate at which the volume of the note decreases
+     as soon as the note off is given.
+
+     lvl |
+       ^ |     /\Dr     __
+     Sl _| _  / _ \__---  \
+         |   /       ---__ \ Rr
+         |  /Ar       Sr  \ \
+         | /                \\
+         |/___________________\________
+                                  ->time
+
+The overal volume can also be set to sweep up or down lineairly or
+exponentially from it's current value. This can be done seperately
+for left and right.
+
+Relevant SPU registers:
+-------------------------------------------------------------
+$1f801xx8         Attack/Decay/Sustain level
+bit  |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|
+desc.|Am|         Ar         |Dr         |Sl         |
+
+Am       0        Attack mode Linear
+         1                    Exponential
+
+Ar       0-7f     attack rate
+Dr       0-f      decay rate
+Sl       0-f      sustain level
+-------------------------------------------------------------
+$1f801xxa         Sustain rate, Release Rate.
+bit  |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|
+desc.|Sm|Sd| 0|   Sr               |Rm|Rr            |
+
+Sm       0        sustain rate mode linear
+         1                          exponential
+Sd       0        sustain rate mode increase
+         1                          decrease
+Sr       0-7f     Sustain Rate
+Rm       0        Linear decrease
+         1        Exponential decrease
+Rr       0-1f     Release Rate
+
+Note: decay mode is always Expontial decrease, and thus cannot
+be set.
+-------------------------------------------------------------
+$1f801xxc         Current ADSR volume
+bit  |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|
+desc.|ADSRvol                                        |
+
+ADSRvol           Returns the current envelope volume when
+                  read.
+-- James' Note: return range: 0 -> 32767
+
+*** doomed doc extract end *** 
+
+By using a small PSX proggie to visualise the envelope as it was played,
+the following results for envelope timing were obtained:
+
+1. Attack rate value (linear mode)
+
+   Attack value range: 0 -> 127
+
+   Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |    | 80 |
+   -----------------------------------------------------------------
+   Frames | 11 | 21 | 42 | 84 | 169| 338| 676|    |2890|
+
+   Note: frames is no. of PAL frames to reach full volume (100%
+   amplitude)
+
+   Hmm, noticing that the time taken to reach full volume doubles
+   every time we add 4 to our attack value, we know the equation is
+   of form:
+             frames = k * 2 ^ (value / 4)
+
+   (You may ponder about envelope generator hardware at this point,
+   or maybe not... :)
+
+   By substituting some stuff and running some checks, we get:
+
+       k = 0.00257              (close enuf)
+
+   therefore,
+             frames = 0.00257 * 2 ^ (value / 4)
+   If you just happen to be writing an emulator, then you can probably
+   use an equation like:
+
+       %volume_increase_per_tick = 1 / frames
+
+
+   ------------------------------------
+   Pete:
+   ms=((1<<(value>>2))*514)/10000
+   ------------------------------------
+
+2. Decay rate value (only has log mode)
+
+   Decay value range: 0 -> 15
+
+   Value  |  8 |  9 | 10 | 11 | 12 | 13 | 14 | 15 |
+   ------------------------------------------------
+   frames |    |    |    |    |  6 | 12 | 24 | 47 |
+
+   Note: frames here is no. of PAL frames to decay to 50% volume.
+
+   formula: frames = k * 2 ^ (value)
+
+   Substituting, we get: k = 0.00146
+
+   Further info on logarithmic nature:
+   frames to decay to sustain level 3  =  3 * frames to decay to 
+   sustain level 9
+
+   Also no. of frames to 25% volume = roughly 1.85 * no. of frames to
+   50% volume.
+
+   Frag it - just use linear approx.
+
+   ------------------------------------
+   Pete:
+   ms=((1<<value)*292)/10000
+   ------------------------------------
+
+
+3. Sustain rate value (linear mode)
+
+   Sustain rate range: 0 -> 127
+
+   Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |
+   -------------------------------------------
+   frames |  9 | 19 | 37 | 74 | 147| 293| 587|
+
+   Here, frames = no. of PAL frames for volume amplitude to go from 100%
+   to 0% (or vice-versa).
+
+   Same formula as for attack value, just a different value for k:
+
+   k = 0.00225
+
+   ie: frames = 0.00225 * 2 ^ (value / 4)
+
+   For emulation purposes:
+
+   %volume_increase_or_decrease_per_tick = 1 / frames
+
+   ------------------------------------
+   Pete:
+   ms=((1<<(value>>2))*450)/10000
+   ------------------------------------
+
+
+4. Release rate (linear mode)
+
+   Release rate range: 0 -> 31
+
+   Value  | 13 | 14 | 15 | 16 | 17 |
+   ---------------------------------------------------------------
+   frames | 18 | 36 | 73 | 146| 292|
+
+   Here, frames = no. of PAL frames to decay from 100% vol to 0% vol
+   after "note-off" is triggered.
+
+   Formula: frames = k * 2 ^ (value)
+
+   And so: k = 0.00223
+
+   ------------------------------------
+   Pete:
+   ms=((1<<value)*446)/10000
+   ------------------------------------
+
+
+Other notes:   
+
+Log stuff not figured out. You may get some clues from the "Decay rate"
+stuff above. For emu purposes it may not be important - use linear
+approx.
+
+To get timings in millisecs, multiply frames by 20.
+
+
+
+- James Higgs 17/6/2000
+james7780@yahoo.com
+
+//---------------------------------------------------------------
+
+OLD adsr mixing according to james' rules... has to be called
+every one millisecond
+
+
+ long v,v2,lT,l1,l2,l3;
+
+ if(s_chan[ch].bStop)                                  // psx wants to stop? -> release phase
+  {
+   if(s_chan[ch].ADSR.ReleaseVal!=0)                   // -> release not 0: do release (if 0: stop right now)
+    {
+     if(!s_chan[ch].ADSR.ReleaseVol)                   // --> release just started? set up the release stuff
+      {
+       s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;
+       s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;
+       s_chan[ch].ADSR.ReleaseTime =                   // --> calc how long does it take to reach the wanted sus level
+         (s_chan[ch].ADSR.ReleaseTime*
+          s_chan[ch].ADSR.ReleaseVol)/1024;
+      }
+                                                       // -> NO release exp mode used (yet)
+     v=s_chan[ch].ADSR.ReleaseVol;                     // -> get last volume
+     lT=s_chan[ch].ADSR.lTime-                         // -> how much time is past?
+        s_chan[ch].ADSR.ReleaseStartTime;
+     l1=s_chan[ch].ADSR.ReleaseTime;
+                                                       
+     if(lT<l1)                                         // -> we still have to release
+      {
+       v=v-((v*lT)/l1);                                // --> calc new volume
+      }
+     else                                              // -> release is over: now really stop that sample
+      {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
+    }
+   else                                                // -> release IS 0: release at once
+    {
+     v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;
+    }
+  }
+ else                                               
+  {//--------------------------------------------------// not in release phase:
+   v=1024;
+   lT=s_chan[ch].ADSR.lTime;
+   l1=s_chan[ch].ADSR.AttackTime;
+                                                       
+   if(lT<l1)                                           // attack
+    {                                                  // no exp mode used (yet)
+//     if(s_chan[ch].ADSR.AttackModeExp)
+//      {
+//       v=(v*lT)/l1;
+//      }
+//     else
+      {
+       v=(v*lT)/l1;
+      }
+     if(v==0) v=1;
+    }
+   else                                                // decay
+    {                                                  // should be exp, but who cares? ;)
+     l2=s_chan[ch].ADSR.DecayTime;
+     v2=s_chan[ch].ADSR.SustainLevel;
+
+     lT-=l1;
+     if(lT<l2)
+      {
+       v-=(((v-v2)*lT)/l2);
+      }
+     else                                              // sustain
+      {                                                // no exp mode used (yet)
+       l3=s_chan[ch].ADSR.SustainTime;
+       lT-=l2;
+       if(s_chan[ch].ADSR.SustainModeDec>0)
+        {
+         if(l3!=0) v2+=((v-v2)*lT)/l3;
+         else      v2=v;
+        }
+       else
+        {
+         if(l3!=0) v2-=(v2*lT)/l3;
+         else      v2=v;
+        }
+
+       if(v2>v)  v2=v;
+       if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
+
+       v=v2;
+      }
+    }
+  }
+
+ //----------------------------------------------------// 
+ // ok, done for this channel, so increase time
+
+ s_chan[ch].ADSR.lTime+=1;                             // 1 = 1.020408f ms;      
+
+ if(v>1024)     v=1024;                                // adjust volume
+ if(v<0)        v=0;                                  
+ s_chan[ch].ADSR.lVolume=v;                            // store act volume
+
+ return v;                                             // return the volume factor
+*/
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+
+/*
+-----------------------------------------------------------------------------
+Neill Corlett
+Playstation SPU envelope timing notes
+-----------------------------------------------------------------------------
+
+This is preliminary.  This may be wrong.  But the model described herein fits
+all of my experimental data, and it's just simple enough to sound right.
+
+ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.
+The value returned by channel reg 0xC is (envelope_level>>16).
+
+Each sample, an increment or decrement value will be added to or
+subtracted from this envelope level.
+
+Create the rate log table.  The values double every 4 entries.
+   entry #0 = 4
+
+    4, 5, 6, 7,
+    8,10,12,14,
+   16,20,24,28, ...
+
+   entry #40 = 4096...
+   entry #44 = 8192...
+   entry #48 = 16384...
+   entry #52 = 32768...
+   entry #56 = 65536...
+
+increments and decrements are in terms of ratelogtable[n]
+n may exceed the table bounds (plan on n being between -32 and 127).
+table values are all clipped between 0x00000000 and 0x3FFFFFFF
+
+when you "voice on", the envelope is always fully reset.
+(yes, it may click. the real thing does this too.)
+
+envelope level begins at zero.
+
+each state happens for at least 1 cycle
+(transitions are not instantaneous)
+this may result in some oddness: if the decay rate is uberfast, it will cut
+the envelope from full down to half in one sample, potentially skipping over
+the sustain level
+
+ATTACK
+------
+- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and
+  proceed to DECAY.
+
+Linear attack mode:
+- line extends upward to 0x7FFFFFFF
+- increment per sample is ratelogtable[(Ar^0x7F)-0x10]
+
+Logarithmic attack mode:
+if envelope_level < 0x60000000:
+  - line extends upward to 0x60000000
+  - increment per sample is ratelogtable[(Ar^0x7F)-0x10]
+else:
+  - line extends upward to 0x7FFFFFFF
+  - increment per sample is ratelogtable[(Ar^0x7F)-0x18]
+
+DECAY
+-----
+- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.
+  Do not clip to the sustain level.
+- current line ends at (envelope_level & 0x07FFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+  0: ratelogtable[(4*(Dr^0x1F))-0x18+0]
+  1: ratelogtable[(4*(Dr^0x1F))-0x18+4]
+  2: ratelogtable[(4*(Dr^0x1F))-0x18+6]
+  3: ratelogtable[(4*(Dr^0x1F))-0x18+8]
+  4: ratelogtable[(4*(Dr^0x1F))-0x18+9]
+  5: ratelogtable[(4*(Dr^0x1F))-0x18+10]
+  6: ratelogtable[(4*(Dr^0x1F))-0x18+11]
+  7: ratelogtable[(4*(Dr^0x1F))-0x18+12]
+  (note that this is the same as the release rate formula, except that
+   decay rates 10-1F aren't possible... those would be slower in theory)
+
+SUSTAIN
+-------
+- no terminating condition except for voice off
+- Sd=0 (increase) behavior is identical to ATTACK for both log and linear.
+- Sd=1 (decrease) behavior:
+Linear sustain decrease:
+- line extends to 0x00000000
+- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]
+Logarithmic sustain decrease:
+- current line ends at (envelope_level & 0x07FFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+  0: ratelogtable[(Sr^0x7F)-0x1B+0]
+  1: ratelogtable[(Sr^0x7F)-0x1B+4]
+  2: ratelogtable[(Sr^0x7F)-0x1B+6]
+  3: ratelogtable[(Sr^0x7F)-0x1B+8]
+  4: ratelogtable[(Sr^0x7F)-0x1B+9]
+  5: ratelogtable[(Sr^0x7F)-0x1B+10]
+  6: ratelogtable[(Sr^0x7F)-0x1B+11]
+  7: ratelogtable[(Sr^0x7F)-0x1B+12]
+
+RELEASE
+-------
+- if the envelope level has overflowed to negative, clip to 0 and QUIT.
+
+Linear release mode:
+- line extends to 0x00000000
+- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]
+
+Logarithmic release mode:
+- line extends to (envelope_level & 0x0FFFFFFF)
+- decrement per sample depends on (envelope_level>>28)&0x7
+  0: ratelogtable[(4*(Rr^0x1F))-0x18+0]
+  1: ratelogtable[(4*(Rr^0x1F))-0x18+4]
+  2: ratelogtable[(4*(Rr^0x1F))-0x18+6]
+  3: ratelogtable[(4*(Rr^0x1F))-0x18+8]
+  4: ratelogtable[(4*(Rr^0x1F))-0x18+9]
+  5: ratelogtable[(4*(Rr^0x1F))-0x18+10]
+  6: ratelogtable[(4*(Rr^0x1F))-0x18+11]
+  7: ratelogtable[(4*(Rr^0x1F))-0x18+12]
+
+-----------------------------------------------------------------------------
+*/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/adsr.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,28 @@
+/***************************************************************************
+                           adsr.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+INLINE void StartADSR(int ch);
+INLINE int  MixADSR(int ch);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/dma.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,175 @@
+/***************************************************************************
+                            dma.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "../peops2/stdafx.h"
+
+#define _IN_DMA
+
+#include "../peops2/externals.h"
+#include "../peops2/registers.h"
+//#include "debug.h"
+
+extern uint32 psx_ram[(2*1024*1024)/4];
+
+////////////////////////////////////////////////////////////////////////
+// READ DMA (many values)
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC void CALLBACK SPU2readDMA4Mem(u32 usPSXMem,int iSize)
+{
+ int i;
+ u16 *ram16 = (u16 *)&psx_ram[0];
+
+ for(i=0;i<iSize;i++)
+  {
+   ram16[usPSXMem>>1]=spuMem[spuAddr2[0]];                  // spu addr 0 got by writeregister
+   usPSXMem+=2;
+   spuAddr2[0]++;                                     // inc spu addr
+   if(spuAddr2[0]>0xfffff) spuAddr2[0]=0;             // wrap
+  }
+
+ spuAddr2[0]+=0x20; //?????
+ 
+
+ iSpuAsyncWait=0;
+
+ // got from J.F. and Kanodin... is it needed?
+ regArea[(PS2_C0_ADMAS)>>1]=0;                         // Auto DMA complete
+ spuStat2[0]=0x80;                                     // DMA complete
+}
+
+EXPORT_GCC void CALLBACK SPU2readDMA7Mem(u32 usPSXMem,int iSize)
+{
+ int i;
+ u16 *ram16 = (u16 *)&psx_ram[0];
+
+ for(i=0;i<iSize;i++)
+  {
+   ram16[usPSXMem>>1]=spuMem[spuAddr2[1]];             // spu addr 1 got by writeregister
+   usPSXMem+=2;
+   spuAddr2[1]++;                                      // inc spu addr
+   if(spuAddr2[1]>0xfffff) spuAddr2[1]=0;              // wrap
+  }
+
+ spuAddr2[1]+=0x20; //?????
+
+ iSpuAsyncWait=0;
+
+ // got from J.F. and Kanodin... is it needed?
+ regArea[(PS2_C1_ADMAS)>>1]=0;                         // Auto DMA complete
+ spuStat2[1]=0x80;                                     // DMA complete
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+// to investigate: do sound data updates by writedma affect spu
+// irqs? Will an irq be triggered, if new data is written to
+// the memory irq address?
+
+////////////////////////////////////////////////////////////////////////
+// WRITE DMA (many values)
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC void CALLBACK SPU2writeDMA4Mem(u32 usPSXMem,int iSize)
+{
+ int i;
+ u16 *ram16 = (u16 *)&psx_ram[0];
+
+ for(i=0;i<iSize;i++)
+  {
+   spuMem[spuAddr2[0]] = ram16[usPSXMem>>1];                 // spu addr 0 got by writeregister
+   usPSXMem+=2;
+   spuAddr2[0]++;                                      // inc spu addr
+   if(spuAddr2[0]>0xfffff) spuAddr2[0]=0;              // wrap
+  }
+ 
+ iSpuAsyncWait=0;
+
+ // got from J.F. and Kanodin... is it needed?
+ spuStat2[0]=0x80;                                     // DMA complete
+}
+
+EXPORT_GCC void CALLBACK SPU2writeDMA7Mem(u32 usPSXMem,int iSize)
+{
+ int i;
+ u16 *ram16 = (u16 *)&psx_ram[0];
+
+ for(i=0;i<iSize;i++)
+  {
+   spuMem[spuAddr2[1]] = ram16[usPSXMem>>1];           // spu addr 1 got by writeregister
+   spuAddr2[1]++;                                      // inc spu addr
+   if(spuAddr2[1]>0xfffff) spuAddr2[1]=0;              // wrap
+  }
+ 
+ iSpuAsyncWait=0;
+
+ // got from J.F. and Kanodin... is it needed?
+ spuStat2[1]=0x80;                                     // DMA complete
+}
+
+////////////////////////////////////////////////////////////////////////
+// INTERRUPTS
+////////////////////////////////////////////////////////////////////////
+
+void InterruptDMA4(void) 
+{
+// taken from linuzappz NULL spu2
+//	spu2Rs16(CORE0_ATTR)&= ~0x30;
+//	spu2Rs16(REG__1B0) = 0;
+//	spu2Rs16(SPU2_STATX_WRDY_M)|= 0x80;
+
+ spuCtrl2[0]&=~0x30;
+ regArea[(PS2_C0_ADMAS)>>1]=0;
+ spuStat2[0]|=0x80;
+}
+                       
+EXPORT_GCC void CALLBACK SPU2interruptDMA4(void) 
+{
+ InterruptDMA4();
+}
+
+void InterruptDMA7(void) 
+{
+// taken from linuzappz NULL spu2
+//	spu2Rs16(CORE1_ATTR)&= ~0x30;
+//	spu2Rs16(REG__5B0) = 0;
+//	spu2Rs16(SPU2_STATX_DREQ)|= 0x80;
+
+ spuCtrl2[1]&=~0x30;
+ regArea[(PS2_C1_ADMAS)>>1]=0;
+ spuStat2[1]|=0x80;
+}
+
+EXPORT_GCC void CALLBACK SPU2interruptDMA7(void) 
+{
+ InterruptDMA7();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/dma.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,29 @@
+/***************************************************************************
+                            dma.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+void InterruptDMA4(void);
+void InterruptDMA7(void); 
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/externals.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,385 @@
+/***************************************************************************
+                         externals.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2002/04/04 - Pete
+// - increased channel struct for interpolation
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#ifndef PEOPS2_EXTERNALS
+#define PEOPS2_EXTERNALS
+
+#include "ao.h"
+
+typedef int8 s8;
+typedef int16 s16;
+typedef int32 s32;
+typedef int64 s64;
+
+typedef uint8 u8;
+typedef uint16 u16;
+typedef uint32 u32;
+typedef uint64 u64;
+
+#if LSB_FIRST
+static INLINE u16 BFLIP16(u16 x)
+{
+ return x;
+}
+#else
+static INLINE u16 BFLIP16(u16 x)
+{
+ return( ((x>>8)&0xFF)| ((x&0xFF)<<8) );
+}
+#endif
+
+/////////////////////////////////////////////////////////
+// generic defines
+/////////////////////////////////////////////////////////
+
+//#define PSE_LT_SPU                  4
+//#define PSE_SPU_ERR_SUCCESS         0
+//#define PSE_SPU_ERR                 -60
+//#define PSE_SPU_ERR_NOTCONFIGURED   PSE_SPU_ERR - 1
+//#define PSE_SPU_ERR_INIT            PSE_SPU_ERR - 2
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+
+////////////////////////////////////////////////////////////////////////
+// spu defines
+////////////////////////////////////////////////////////////////////////
+
+// sound buffer sizes
+// 400 ms complete sound buffer
+#define SOUNDSIZE   76800
+// 137 ms test buffer... if less than that is buffered, a new upload will happen
+#define TESTSIZE    26304
+
+// num of channels
+#define MAXCHAN     48
+#define HLFCHAN     24
+
+// ~ 1 ms of data (was 45)
+#define NSSIZE 	1
+//45
+
+///////////////////////////////////////////////////////////
+// struct defines
+///////////////////////////////////////////////////////////
+
+// ADSR INFOS PER CHANNEL
+typedef struct
+{
+ int            AttackModeExp;
+ long           AttackTime;
+ long           DecayTime;
+ long           SustainLevel;
+ int            SustainModeExp;
+ long           SustainModeDec;
+ long           SustainTime;
+ int            ReleaseModeExp;
+ unsigned long  ReleaseVal;
+ long           ReleaseTime;
+ long           ReleaseStartTime; 
+ long           ReleaseVol; 
+ long           lTime;
+ long           lVolume;
+} ADSRInfo;
+
+typedef struct
+{
+ int            State;
+ int            AttackModeExp;
+ int            AttackRate;
+ int            DecayRate;
+ int            SustainLevel;
+ int            SustainModeExp;
+ int            SustainIncrease;
+ int            SustainRate;
+ int            ReleaseModeExp;
+ int            ReleaseRate;
+ int            EnvelopeVol;
+ long           lVolume;
+ long           lDummy1;
+ long           lDummy2;
+} ADSRInfoEx;
+              
+///////////////////////////////////////////////////////////
+
+// Tmp Flags
+
+// used for debug channel muting
+#define FLAG_MUTE  1
+
+// used for simple interpolation
+#define FLAG_IPOL0 2
+#define FLAG_IPOL1 4
+
+///////////////////////////////////////////////////////////
+
+// MAIN CHANNEL STRUCT
+typedef struct
+{
+ // no mutexes used anymore... don't need them to sync access
+ //HANDLE            hMutex;
+
+ int               bNew;                               // start flag
+
+ int               iSBPos;                             // mixing stuff
+ int               spos;
+ int               sinc;
+ int               SB[32+32];                          // Pete added another 32 dwords in 1.6 ... prevents overflow issues with gaussian/cubic interpolation (thanx xodnizel!), and can be used for even better interpolations, eh? :)
+ int               sval;
+
+ unsigned char *   pStart;                             // start ptr into sound mem
+ unsigned char *   pCurr;                              // current pos in sound mem
+ unsigned char *   pLoop;                              // loop ptr in sound mem
+
+ int               iStartAdr;
+ int               iLoopAdr; 
+ int               iNextAdr; 
+
+ int               bOn;                                // is channel active (sample playing?)
+ int               bStop;                              // is channel stopped (sample _can_ still be playing, ADSR Release phase)
+ int               bEndPoint;                          // end point reached
+ int               bReverbL;                           // can we do reverb on this channel? must have ctrl register bit, to get active
+ int               bReverbR; 
+ 
+ int               bVolumeL;                           // Volume on/off
+ int               bVolumeR;
+ 
+ int               iActFreq;                           // current psx pitch
+ int               iUsedFreq;                          // current pc pitch
+ int               iLeftVolume;                        // left volume
+ int               iLeftVolRaw;                        // left psx volume value
+ int               bIgnoreLoop;                        // ignore loop bit, if an external loop address is used
+ int               iMute;                              // mute mode
+ int               iRightVolume;                       // right volume
+ int               iRightVolRaw;                       // right psx volume value
+ int               iRawPitch;                          // raw pitch (0...3fff)
+ int               iIrqDone;                           // debug irq done flag
+ int               s_1;                                // last decoding infos
+ int               s_2;
+ int               bRVBActive;                         // reverb active flag
+ int               bNoise;                             // noise active flag
+ int               bFMod;                              // freq mod (0=off, 1=sound channel, 2=freq channel)
+ int               iOldNoise;                          // old noise val for this channel   
+ ADSRInfo          ADSR;                               // active ADSR settings
+ ADSRInfoEx        ADSRX;                              // next ADSR settings (will be moved to active on sample start)
+
+} SPUCHAN;
+
+///////////////////////////////////////////////////////////
+
+typedef struct
+{
+ int StartAddr;      // reverb area start addr in samples
+ int EndAddr;        // reverb area end addr in samples
+ int CurrAddr;       // reverb area curr addr in samples
+
+ int VolLeft;
+ int VolRight;
+ int iLastRVBLeft;
+ int iLastRVBRight;
+ int iRVBLeft;
+ int iRVBRight;
+ int iCnt;
+
+ int FB_SRC_A;       // (offset)
+ int FB_SRC_B;       // (offset)
+ int IIR_ALPHA;      // (coef.)
+ int ACC_COEF_A;     // (coef.)
+ int ACC_COEF_B;     // (coef.)
+ int ACC_COEF_C;     // (coef.)
+ int ACC_COEF_D;     // (coef.)
+ int IIR_COEF;       // (coef.)
+ int FB_ALPHA;       // (coef.)
+ int FB_X;           // (coef.)
+ int IIR_DEST_A0;    // (offset)
+ int IIR_DEST_A1;    // (offset)
+ int ACC_SRC_A0;     // (offset)
+ int ACC_SRC_A1;     // (offset)
+ int ACC_SRC_B0;     // (offset)
+ int ACC_SRC_B1;     // (offset)
+ int IIR_SRC_A0;     // (offset)
+ int IIR_SRC_A1;     // (offset)
+ int IIR_DEST_B0;    // (offset)
+ int IIR_DEST_B1;    // (offset)
+ int ACC_SRC_C0;     // (offset)
+ int ACC_SRC_C1;     // (offset)
+ int ACC_SRC_D0;     // (offset)
+ int ACC_SRC_D1;     // (offset)
+ int IIR_SRC_B1;     // (offset)
+ int IIR_SRC_B0;     // (offset)
+ int MIX_DEST_A0;    // (offset)
+ int MIX_DEST_A1;    // (offset)
+ int MIX_DEST_B0;    // (offset)
+ int MIX_DEST_B1;    // (offset)
+ int IN_COEF_L;      // (coef.)
+ int IN_COEF_R;      // (coef.)
+} REVERBInfo;
+
+#ifdef _WINDOWS
+//extern HINSTANCE hInst;
+//#define WM_MUTE (WM_USER+543)
+#endif
+
+///////////////////////////////////////////////////////////
+// SPU.C globals
+///////////////////////////////////////////////////////////
+
+#ifndef _IN_SPU
+
+// psx buffers / addresses
+
+extern unsigned short  regArea[];                        
+extern unsigned short  spuMem[];
+extern unsigned char * spuMemC;
+extern unsigned char * pSpuIrq[];
+extern unsigned char * pSpuBuffer;
+
+// user settings
+
+extern int        iUseXA;
+extern int        iVolume;
+extern int        iXAPitch;
+extern int        iUseTimer;
+extern int        iSPUIRQWait;
+extern int        iDebugMode;
+extern int        iRecordMode;
+extern int        iUseReverb;
+extern int        iUseInterpolation;
+extern int        iDisStereo;
+// MISC
+
+extern SPUCHAN s_chan[];
+extern REVERBInfo rvb[];
+
+extern unsigned long dwNoiseVal;
+extern unsigned short spuCtrl2[];
+extern unsigned short spuStat2[];
+extern unsigned long  spuIrq2[];
+extern unsigned long  spuAddr2[];
+extern unsigned long   spuRvbAddr2[];
+extern unsigned long   spuRvbAEnd2[];
+
+extern int      bEndThread; 
+extern int      bThreadEnded;
+extern int      bSpuInit;
+
+extern int      SSumR[];
+extern int      SSumL[];
+extern int      iCycle;
+extern short *  pS;
+extern unsigned long dwNewChannel2[];
+extern unsigned long dwEndChannel2[];
+
+extern int iSpuAsyncWait;
+
+#ifdef _WINDOWS
+//extern HWND    hWMain;                               // window handle
+//extern HWND    hWDebug;
+#endif
+
+extern void (CALLBACK *cddavCallback)(unsigned short,unsigned short);
+
+#endif
+
+///////////////////////////////////////////////////////////
+// CFG.C globals
+///////////////////////////////////////////////////////////
+
+#ifndef _IN_CFG
+
+#ifndef _WINDOWS
+extern char * pConfigFile;
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////
+// DSOUND.C globals
+///////////////////////////////////////////////////////////
+
+#ifndef _IN_DSOUND
+
+#ifdef _WINDOWS
+extern unsigned long LastWrite;
+extern unsigned long LastPlay;
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////
+// RECORD.C globals
+///////////////////////////////////////////////////////////
+
+#ifndef _IN_RECORD
+
+#ifdef _WINDOWS
+extern int iDoRecord;
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////
+// XA.C globals
+///////////////////////////////////////////////////////////
+
+#ifndef _IN_XA
+
+extern xa_decode_t   * xapGlobal;
+
+extern unsigned long * XAFeed;
+extern unsigned long * XAPlay;
+extern unsigned long * XAStart;
+extern unsigned long * XAEnd;
+
+extern unsigned long   XARepeat;
+extern unsigned long   XALastVal;
+
+extern int           iLeftXAVol;
+extern int           iRightXAVol;
+
+#endif
+
+///////////////////////////////////////////////////////////
+// REVERB.C globals
+///////////////////////////////////////////////////////////
+
+#ifndef _IN_REVERB
+
+extern int *          sRVBPlay[];
+extern int *          sRVBEnd[];
+extern int *          sRVBStart[];
+
+#endif
+
+#endif // PEOPS2_EXTERNALS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/gauss_i.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,162 @@
+/***************************************************************************
+                          gauss_i.h  -  description
+                           -----------------------
+    begin                : Sun Feb 08 2003
+    copyright            : (C) 2003 by Chris Moeller, eh, whatever
+    email                : chris@kode54.tk
+ ***************************************************************************/
+                       
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+                           
+//*************************************************************************//
+// History of changes:
+//
+// 2003/02/08 - kode54
+// - generated by interleaving table from gauss.h from the libopenspc
+//   project; a gaussian bell curve table logged from the SPC-700,
+//   though Neill says he logged the same curve from a PSX SPU. Also
+//   says that interleaving the coefficients together runs faster. Meh.
+//
+//*************************************************************************//
+
+#ifndef GAUSS_H
+#define GAUSS_H
+
+static const int gauss[]={
+	0x172, 0x519, 0x176, 0x000, 0x16E, 0x519, 0x17A, 0x000, 
+	0x16A, 0x518, 0x17D, 0x000, 0x166, 0x518, 0x181, 0x000, 
+	0x162, 0x518, 0x185, 0x000, 0x15F, 0x518, 0x189, 0x000, 
+	0x15B, 0x518, 0x18D, 0x000, 0x157, 0x517, 0x191, 0x000, 
+	0x153, 0x517, 0x195, 0x000, 0x150, 0x517, 0x19A, 0x000, 
+	0x14C, 0x516, 0x19E, 0x000, 0x148, 0x516, 0x1A2, 0x000, 
+	0x145, 0x515, 0x1A6, 0x000, 0x141, 0x514, 0x1AA, 0x000, 
+	0x13E, 0x514, 0x1AE, 0x000, 0x13A, 0x513, 0x1B2, 0x000, 
+	0x137, 0x512, 0x1B7, 0x001, 0x133, 0x511, 0x1BB, 0x001, 
+	0x130, 0x511, 0x1BF, 0x001, 0x12C, 0x510, 0x1C3, 0x001, 
+	0x129, 0x50F, 0x1C8, 0x001, 0x125, 0x50E, 0x1CC, 0x001, 
+	0x122, 0x50D, 0x1D0, 0x001, 0x11E, 0x50C, 0x1D5, 0x001, 
+	0x11B, 0x50B, 0x1D9, 0x001, 0x118, 0x50A, 0x1DD, 0x001, 
+	0x114, 0x508, 0x1E2, 0x001, 0x111, 0x507, 0x1E6, 0x002, 
+	0x10E, 0x506, 0x1EB, 0x002, 0x10B, 0x504, 0x1EF, 0x002, 
+	0x107, 0x503, 0x1F3, 0x002, 0x104, 0x502, 0x1F8, 0x002, 
+	0x101, 0x500, 0x1FC, 0x002, 0x0FE, 0x4FF, 0x201, 0x002, 
+	0x0FB, 0x4FD, 0x205, 0x003, 0x0F8, 0x4FB, 0x20A, 0x003, 
+	0x0F5, 0x4FA, 0x20F, 0x003, 0x0F2, 0x4F8, 0x213, 0x003, 
+	0x0EF, 0x4F6, 0x218, 0x003, 0x0EC, 0x4F5, 0x21C, 0x004, 
+	0x0E9, 0x4F3, 0x221, 0x004, 0x0E6, 0x4F1, 0x226, 0x004, 
+	0x0E3, 0x4EF, 0x22A, 0x004, 0x0E0, 0x4ED, 0x22F, 0x004, 
+	0x0DD, 0x4EB, 0x233, 0x005, 0x0DA, 0x4E9, 0x238, 0x005, 
+	0x0D7, 0x4E7, 0x23D, 0x005, 0x0D4, 0x4E5, 0x241, 0x005, 
+	0x0D2, 0x4E3, 0x246, 0x006, 0x0CF, 0x4E0, 0x24B, 0x006, 
+	0x0CC, 0x4DE, 0x250, 0x006, 0x0C9, 0x4DC, 0x254, 0x006, 
+	0x0C7, 0x4D9, 0x259, 0x007, 0x0C4, 0x4D7, 0x25E, 0x007, 
+	0x0C1, 0x4D5, 0x263, 0x007, 0x0BF, 0x4D2, 0x267, 0x008, 
+	0x0BC, 0x4D0, 0x26C, 0x008, 0x0BA, 0x4CD, 0x271, 0x008, 
+	0x0B7, 0x4CB, 0x276, 0x009, 0x0B4, 0x4C8, 0x27B, 0x009, 
+	0x0B2, 0x4C5, 0x280, 0x009, 0x0AF, 0x4C3, 0x284, 0x00A, 
+	0x0AD, 0x4C0, 0x289, 0x00A, 0x0AB, 0x4BD, 0x28E, 0x00A, 
+	0x0A8, 0x4BA, 0x293, 0x00B, 0x0A6, 0x4B7, 0x298, 0x00B, 
+	0x0A3, 0x4B5, 0x29D, 0x00B, 0x0A1, 0x4B2, 0x2A2, 0x00C, 
+	0x09F, 0x4AF, 0x2A6, 0x00C, 0x09C, 0x4AC, 0x2AB, 0x00D, 
+	0x09A, 0x4A9, 0x2B0, 0x00D, 0x098, 0x4A6, 0x2B5, 0x00E, 
+	0x096, 0x4A2, 0x2BA, 0x00E, 0x093, 0x49F, 0x2BF, 0x00F, 
+	0x091, 0x49C, 0x2C4, 0x00F, 0x08F, 0x499, 0x2C9, 0x00F, 
+	0x08D, 0x496, 0x2CE, 0x010, 0x08B, 0x492, 0x2D3, 0x010, 
+	0x089, 0x48F, 0x2D8, 0x011, 0x086, 0x48C, 0x2DC, 0x011, 
+	0x084, 0x488, 0x2E1, 0x012, 0x082, 0x485, 0x2E6, 0x013, 
+	0x080, 0x481, 0x2EB, 0x013, 0x07E, 0x47E, 0x2F0, 0x014, 
+	0x07C, 0x47A, 0x2F5, 0x014, 0x07A, 0x477, 0x2FA, 0x015, 
+	0x078, 0x473, 0x2FF, 0x015, 0x076, 0x470, 0x304, 0x016, 
+	0x075, 0x46C, 0x309, 0x017, 0x073, 0x468, 0x30E, 0x017, 
+	0x071, 0x465, 0x313, 0x018, 0x06F, 0x461, 0x318, 0x018, 
+	0x06D, 0x45D, 0x31D, 0x019, 0x06B, 0x459, 0x322, 0x01A, 
+	0x06A, 0x455, 0x326, 0x01B, 0x068, 0x452, 0x32B, 0x01B, 
+	0x066, 0x44E, 0x330, 0x01C, 0x064, 0x44A, 0x335, 0x01D, 
+	0x063, 0x446, 0x33A, 0x01D, 0x061, 0x442, 0x33F, 0x01E, 
+	0x05F, 0x43E, 0x344, 0x01F, 0x05E, 0x43A, 0x349, 0x020, 
+	0x05C, 0x436, 0x34E, 0x020, 0x05A, 0x432, 0x353, 0x021, 
+	0x059, 0x42E, 0x357, 0x022, 0x057, 0x42A, 0x35C, 0x023, 
+	0x056, 0x425, 0x361, 0x024, 0x054, 0x421, 0x366, 0x024, 
+	0x053, 0x41D, 0x36B, 0x025, 0x051, 0x419, 0x370, 0x026, 
+	0x050, 0x415, 0x374, 0x027, 0x04E, 0x410, 0x379, 0x028, 
+	0x04D, 0x40C, 0x37E, 0x029, 0x04C, 0x408, 0x383, 0x02A, 
+	0x04A, 0x403, 0x388, 0x02B, 0x049, 0x3FF, 0x38C, 0x02C, 
+	0x047, 0x3FB, 0x391, 0x02D, 0x046, 0x3F6, 0x396, 0x02E, 
+	0x045, 0x3F2, 0x39B, 0x02F, 0x043, 0x3ED, 0x39F, 0x030, 
+	0x042, 0x3E9, 0x3A4, 0x031, 0x041, 0x3E5, 0x3A9, 0x032, 
+	0x040, 0x3E0, 0x3AD, 0x033, 0x03E, 0x3DC, 0x3B2, 0x034, 
+	0x03D, 0x3D7, 0x3B7, 0x035, 0x03C, 0x3D2, 0x3BB, 0x036, 
+	0x03B, 0x3CE, 0x3C0, 0x037, 0x03A, 0x3C9, 0x3C5, 0x038, 
+	0x038, 0x3C5, 0x3C9, 0x03A, 0x037, 0x3C0, 0x3CE, 0x03B, 
+	0x036, 0x3BB, 0x3D2, 0x03C, 0x035, 0x3B7, 0x3D7, 0x03D, 
+	0x034, 0x3B2, 0x3DC, 0x03E, 0x033, 0x3AD, 0x3E0, 0x040, 
+	0x032, 0x3A9, 0x3E5, 0x041, 0x031, 0x3A4, 0x3E9, 0x042, 
+	0x030, 0x39F, 0x3ED, 0x043, 0x02F, 0x39B, 0x3F2, 0x045, 
+	0x02E, 0x396, 0x3F6, 0x046, 0x02D, 0x391, 0x3FB, 0x047, 
+	0x02C, 0x38C, 0x3FF, 0x049, 0x02B, 0x388, 0x403, 0x04A, 
+	0x02A, 0x383, 0x408, 0x04C, 0x029, 0x37E, 0x40C, 0x04D, 
+	0x028, 0x379, 0x410, 0x04E, 0x027, 0x374, 0x415, 0x050, 
+	0x026, 0x370, 0x419, 0x051, 0x025, 0x36B, 0x41D, 0x053, 
+	0x024, 0x366, 0x421, 0x054, 0x024, 0x361, 0x425, 0x056, 
+	0x023, 0x35C, 0x42A, 0x057, 0x022, 0x357, 0x42E, 0x059, 
+	0x021, 0x353, 0x432, 0x05A, 0x020, 0x34E, 0x436, 0x05C, 
+	0x020, 0x349, 0x43A, 0x05E, 0x01F, 0x344, 0x43E, 0x05F, 
+	0x01E, 0x33F, 0x442, 0x061, 0x01D, 0x33A, 0x446, 0x063, 
+	0x01D, 0x335, 0x44A, 0x064, 0x01C, 0x330, 0x44E, 0x066, 
+	0x01B, 0x32B, 0x452, 0x068, 0x01B, 0x326, 0x455, 0x06A, 
+	0x01A, 0x322, 0x459, 0x06B, 0x019, 0x31D, 0x45D, 0x06D, 
+	0x018, 0x318, 0x461, 0x06F, 0x018, 0x313, 0x465, 0x071, 
+	0x017, 0x30E, 0x468, 0x073, 0x017, 0x309, 0x46C, 0x075, 
+	0x016, 0x304, 0x470, 0x076, 0x015, 0x2FF, 0x473, 0x078, 
+	0x015, 0x2FA, 0x477, 0x07A, 0x014, 0x2F5, 0x47A, 0x07C, 
+	0x014, 0x2F0, 0x47E, 0x07E, 0x013, 0x2EB, 0x481, 0x080, 
+	0x013, 0x2E6, 0x485, 0x082, 0x012, 0x2E1, 0x488, 0x084, 
+	0x011, 0x2DC, 0x48C, 0x086, 0x011, 0x2D8, 0x48F, 0x089, 
+	0x010, 0x2D3, 0x492, 0x08B, 0x010, 0x2CE, 0x496, 0x08D, 
+	0x00F, 0x2C9, 0x499, 0x08F, 0x00F, 0x2C4, 0x49C, 0x091, 
+	0x00F, 0x2BF, 0x49F, 0x093, 0x00E, 0x2BA, 0x4A2, 0x096, 
+	0x00E, 0x2B5, 0x4A6, 0x098, 0x00D, 0x2B0, 0x4A9, 0x09A, 
+	0x00D, 0x2AB, 0x4AC, 0x09C, 0x00C, 0x2A6, 0x4AF, 0x09F, 
+	0x00C, 0x2A2, 0x4B2, 0x0A1, 0x00B, 0x29D, 0x4B5, 0x0A3, 
+	0x00B, 0x298, 0x4B7, 0x0A6, 0x00B, 0x293, 0x4BA, 0x0A8, 
+	0x00A, 0x28E, 0x4BD, 0x0AB, 0x00A, 0x289, 0x4C0, 0x0AD, 
+	0x00A, 0x284, 0x4C3, 0x0AF, 0x009, 0x280, 0x4C5, 0x0B2, 
+	0x009, 0x27B, 0x4C8, 0x0B4, 0x009, 0x276, 0x4CB, 0x0B7, 
+	0x008, 0x271, 0x4CD, 0x0BA, 0x008, 0x26C, 0x4D0, 0x0BC, 
+	0x008, 0x267, 0x4D2, 0x0BF, 0x007, 0x263, 0x4D5, 0x0C1, 
+	0x007, 0x25E, 0x4D7, 0x0C4, 0x007, 0x259, 0x4D9, 0x0C7, 
+	0x006, 0x254, 0x4DC, 0x0C9, 0x006, 0x250, 0x4DE, 0x0CC, 
+	0x006, 0x24B, 0x4E0, 0x0CF, 0x006, 0x246, 0x4E3, 0x0D2, 
+	0x005, 0x241, 0x4E5, 0x0D4, 0x005, 0x23D, 0x4E7, 0x0D7, 
+	0x005, 0x238, 0x4E9, 0x0DA, 0x005, 0x233, 0x4EB, 0x0DD, 
+	0x004, 0x22F, 0x4ED, 0x0E0, 0x004, 0x22A, 0x4EF, 0x0E3, 
+	0x004, 0x226, 0x4F1, 0x0E6, 0x004, 0x221, 0x4F3, 0x0E9, 
+	0x004, 0x21C, 0x4F5, 0x0EC, 0x003, 0x218, 0x4F6, 0x0EF, 
+	0x003, 0x213, 0x4F8, 0x0F2, 0x003, 0x20F, 0x4FA, 0x0F5, 
+	0x003, 0x20A, 0x4FB, 0x0F8, 0x003, 0x205, 0x4FD, 0x0FB, 
+	0x002, 0x201, 0x4FF, 0x0FE, 0x002, 0x1FC, 0x500, 0x101, 
+	0x002, 0x1F8, 0x502, 0x104, 0x002, 0x1F3, 0x503, 0x107, 
+	0x002, 0x1EF, 0x504, 0x10B, 0x002, 0x1EB, 0x506, 0x10E, 
+	0x002, 0x1E6, 0x507, 0x111, 0x001, 0x1E2, 0x508, 0x114, 
+	0x001, 0x1DD, 0x50A, 0x118, 0x001, 0x1D9, 0x50B, 0x11B, 
+	0x001, 0x1D5, 0x50C, 0x11E, 0x001, 0x1D0, 0x50D, 0x122, 
+	0x001, 0x1CC, 0x50E, 0x125, 0x001, 0x1C8, 0x50F, 0x129, 
+	0x001, 0x1C3, 0x510, 0x12C, 0x001, 0x1BF, 0x511, 0x130, 
+	0x001, 0x1BB, 0x511, 0x133, 0x001, 0x1B7, 0x512, 0x137, 
+	0x000, 0x1B2, 0x513, 0x13A, 0x000, 0x1AE, 0x514, 0x13E, 
+	0x000, 0x1AA, 0x514, 0x141, 0x000, 0x1A6, 0x515, 0x145, 
+	0x000, 0x1A2, 0x516, 0x148, 0x000, 0x19E, 0x516, 0x14C, 
+	0x000, 0x19A, 0x517, 0x150, 0x000, 0x195, 0x517, 0x153, 
+	0x000, 0x191, 0x517, 0x157, 0x000, 0x18D, 0x518, 0x15B, 
+	0x000, 0x189, 0x518, 0x15F, 0x000, 0x185, 0x518, 0x162, 
+	0x000, 0x181, 0x518, 0x166, 0x000, 0x17D, 0x518, 0x16A, 
+	0x000, 0x17A, 0x519, 0x16E, 0x000, 0x176, 0x519, 0x172};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/psemuxa.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,28 @@
+//============================================
+//=== Audio XA decoding
+//=== Kazzuya
+//============================================
+
+#ifndef DECODEXA_H
+#define DECODEXA_H
+
+typedef struct
+{
+	long	y0, y1;
+} ADPCM_Decode_t;
+
+typedef struct
+{                                                                   
+	int				freq;
+	int				nbits;
+	int				stereo;
+	int				nsamples;
+	ADPCM_Decode_t	left, right;
+	short			pcm[16384];
+} xa_decode_t;
+
+long xa_decode_sector( xa_decode_t *xdp,
+					   unsigned char *sectorp,
+					   int is_first_sector );
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/registers.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,1343 @@
+/***************************************************************************
+                         registers.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2003/02/09 - kode54
+// - removed &0x3fff from reverb volume registers, fixes a few games,
+//   hopefully won't be breaking anything
+//
+// 2003/01/19 - Pete
+// - added Neill's reverb
+//
+// 2003/01/06 - Pete
+// - added Neill's ADSR timings
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "stdafx.h"
+
+#define _IN_REGISTERS
+
+#include "../peops2/externals.h"
+#include "../peops2/registers.h"
+#include "../peops2/regs.h"
+#include "../peops2/reverb.h"
+
+/*
+// adsr time values (in ms) by James Higgs ... see the end of
+// the adsr.c source for details
+
+#define ATTACK_MS     514L
+#define DECAYHALF_MS  292L
+#define DECAY_MS      584L
+#define SUSTAIN_MS    450L
+#define RELEASE_MS    446L
+*/
+
+// we have a timebase of 1.020408f ms, not 1 ms... so adjust adsr defines
+#define ATTACK_MS      494L
+#define DECAYHALF_MS   286L
+#define DECAY_MS       572L
+#define SUSTAIN_MS     441L
+#define RELEASE_MS     437L
+
+// Prototypes
+void SetVolumeL(unsigned char ch,short vol);
+void SetVolumeR(unsigned char ch,short vol);
+void ReverbOn(int start,int end,unsigned short val,int iRight);
+void SetReverbAddr(int core);
+void VolumeOn(int start,int end,unsigned short val,int iRight);
+
+////////////////////////////////////////////////////////////////////////
+// WRITE REGISTERS: called by main emu
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC void CALLBACK SPU2write(unsigned long reg, unsigned short val)
+{
+ long r=reg&0xffff;
+
+ regArea[r>>1] = val;
+
+//	printf("SPU2: %04x to %08x\n", val, reg);
+
+ if((r>=0x0000 && r<0x0180)||(r>=0x0400 && r<0x0580))  // some channel info?
+  {
+   int ch=(r>>4)&0x1f;
+   if(r>=0x400) ch+=24;
+   
+   switch(r&0x0f)
+    {
+     //------------------------------------------------// r volume
+     case 0:                                           
+       SetVolumeL((unsigned char)ch,val);
+       break;
+     //------------------------------------------------// l volume
+     case 2:                                           
+       SetVolumeR((unsigned char)ch,val);
+       break;
+     //------------------------------------------------// pitch
+     case 4:                                           
+       SetPitch(ch,val);
+       break;
+     //------------------------------------------------// level with pre-calcs
+     case 6:
+       {
+        const unsigned long lval=val;unsigned long lx;
+        //---------------------------------------------//
+        s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; 
+        s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;
+        s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;
+        s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;
+        //---------------------------------------------//
+        if(!iDebugMode) break;
+        //---------------------------------------------// stuff below is only for debug mode
+
+        s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0;        //0x007f
+
+        lx=(((lval>>8) & 0x007f)>>2);                  // attack time to run from 0 to 100% volume
+        lx=min(31,lx);                                 // no overflow on shift!
+        if(lx) 
+         { 
+          lx = (1<<lx);
+          if(lx<2147483) lx=(lx*ATTACK_MS)/10000L;     // another overflow check
+          else           lx=(lx/10000L)*ATTACK_MS;
+          if(!lx) lx=1;
+         }
+        s_chan[ch].ADSR.AttackTime=lx;                
+
+        s_chan[ch].ADSR.SustainLevel=                 // our adsr vol runs from 0 to 1024, so scale the sustain level
+         (1024*((lval) & 0x000f))/15;
+
+        lx=(lval>>4) & 0x000f;                         // decay:
+        if(lx)                                         // our const decay value is time it takes from 100% to 0% of volume
+         {
+          lx = ((1<<(lx))*DECAY_MS)/10000L;
+          if(!lx) lx=1;
+         }
+        s_chan[ch].ADSR.DecayTime =                   // so calc how long does it take to run from 100% to the wanted sus level
+         (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024;
+       }
+      break;
+     //------------------------------------------------// adsr times with pre-calcs
+     case 8:
+      {
+       const unsigned long lval=val;unsigned long lx;
+
+       //----------------------------------------------//
+       s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;
+       s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;
+       s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;
+       s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;
+       s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;
+       //----------------------------------------------//
+       if(!iDebugMode) break;
+       //----------------------------------------------// stuff below is only for debug mode
+
+       s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0;
+       s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0;
+                   
+       lx=((((lval>>6) & 0x007f)>>2));                 // sustain time... often very high
+       lx=min(31,lx);                                  // values are used to hold the volume
+       if(lx)                                          // until a sound stop occurs
+        {                                              // the highest value we reach (due to 
+         lx = (1<<lx);                                 // overflow checking) is: 
+         if(lx<2147483) lx=(lx*SUSTAIN_MS)/10000L;     // 94704 seconds = 1578 minutes = 26 hours... 
+         else           lx=(lx/10000L)*SUSTAIN_MS;     // should be enuff... if the stop doesn't 
+         if(!lx) lx=1;                                 // come in this time span, I don't care :)
+        }
+       s_chan[ch].ADSR.SustainTime = lx;
+
+       lx=(lval & 0x001f);
+       s_chan[ch].ADSR.ReleaseVal     =lx;
+       if(lx)                                          // release time from 100% to 0%
+        {                                              // note: the release time will be
+         lx = (1<<lx);                                 // adjusted when a stop is coming,
+         if(lx<2147483) lx=(lx*RELEASE_MS)/10000L;     // so at this time the adsr vol will 
+         else           lx=(lx/10000L)*RELEASE_MS;     // run from (current volume) to 0%
+         if(!lx) lx=1;
+        }
+       s_chan[ch].ADSR.ReleaseTime=lx;
+
+       if(lval & 0x4000)                               // add/dec flag
+            s_chan[ch].ADSR.SustainModeDec=-1;
+       else s_chan[ch].ADSR.SustainModeDec=1;
+      }
+     break;
+     //------------------------------------------------//
+    }
+
+   iSpuAsyncWait=0;
+
+   return;
+  }
+
+ if((r>=0x01c0 && r<0x02E0)||(r>=0x05c0 && r<0x06E0))  // some channel info?
+  {
+   int ch=0;
+   if(r>=0x400) {ch=24;r-=0x400;}
+
+   ch+=(r-0x1c0)/12;
+   r-=(ch%24)*12;
+   switch(r)
+    {
+     //------------------------------------------------//
+     case 0x1C0:
+      s_chan[ch].iStartAdr=(((unsigned long)val&0xf)<<16)|(s_chan[ch].iStartAdr&0xFFFF);
+      s_chan[ch].pStart=spuMemC+(s_chan[ch].iStartAdr<<1);
+      break;
+     case 0x1C2:
+      s_chan[ch].iStartAdr=(s_chan[ch].iStartAdr & 0xF0000) | (val & 0xFFFF);
+      s_chan[ch].pStart=spuMemC+(s_chan[ch].iStartAdr<<1);
+      break;
+     //------------------------------------------------//
+     case 0x1C4:
+      s_chan[ch].iLoopAdr=(((unsigned long)val&0xf)<<16)|(s_chan[ch].iLoopAdr&0xFFFF);
+      s_chan[ch].pLoop=spuMemC+(s_chan[ch].iLoopAdr<<1);
+      s_chan[ch].bIgnoreLoop=1;
+      break;
+     case 0x1C6:
+      s_chan[ch].iLoopAdr=(s_chan[ch].iLoopAdr & 0xF0000) | (val & 0xFFFF);
+      s_chan[ch].pLoop=spuMemC+(s_chan[ch].iLoopAdr<<1);
+      s_chan[ch].bIgnoreLoop=1;
+      break;
+     //------------------------------------------------//
+     case 0x1C8:
+      // unused... check if it gets written as well
+      s_chan[ch].iNextAdr=(((unsigned long)val&0xf)<<16)|(s_chan[ch].iNextAdr&0xFFFF);
+      break;
+     case 0x1CA:
+      // unused... check if it gets written as well
+      s_chan[ch].iNextAdr=(s_chan[ch].iNextAdr & 0xF0000) | (val & 0xFFFF);
+      break;
+     //------------------------------------------------//
+    }
+
+   iSpuAsyncWait=0;
+
+   return;
+  } 
+
+ switch(r)
+   {
+    //-------------------------------------------------//
+    case PS2_C0_SPUaddr_Hi:
+      spuAddr2[0] = (((unsigned long)val&0xf)<<16)|(spuAddr2[0]&0xFFFF);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUaddr_Lo:
+      spuAddr2[0] = (spuAddr2[0] & 0xF0000) | (val & 0xFFFF);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUaddr_Hi:
+      spuAddr2[1] = (((unsigned long)val&0xf)<<16)|(spuAddr2[1]&0xFFFF);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUaddr_Lo:
+      spuAddr2[1] = (spuAddr2[1] & 0xF0000) | (val & 0xFFFF);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUdata:
+      spuMem[spuAddr2[0]] = val;
+      spuAddr2[0]++;
+      if(spuAddr2[0]>0xfffff) spuAddr2[0]=0;
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUdata:
+      spuMem[spuAddr2[1]] = val;
+      spuAddr2[1]++;
+      if(spuAddr2[1]>0xfffff) spuAddr2[1]=0;
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_ATTR:
+      spuCtrl2[0]=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_ATTR:
+      spuCtrl2[1]=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUstat:
+      spuStat2[0]=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUstat:
+      spuStat2[1]=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_ReverbAddr_Hi:
+      spuRvbAddr2[0] = (((unsigned long)val&0xf)<<16)|(spuRvbAddr2[0]&0xFFFF);
+      SetReverbAddr(0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_ReverbAddr_Lo:
+      spuRvbAddr2[0] = (spuRvbAddr2[0] & 0xF0000) | (val & 0xFFFF);
+      SetReverbAddr(0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_ReverbAEnd_Hi:
+      spuRvbAEnd2[0] = (((unsigned long)val&0xf)<<16)|(/*spuRvbAEnd2[0]&*/0xFFFF);
+      rvb[0].EndAddr=spuRvbAEnd2[0];
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_ReverbAEnd_Hi:
+      spuRvbAEnd2[1] = (((unsigned long)val&0xf)<<16)|(/*spuRvbAEnd2[1]&*/0xFFFF);
+      rvb[1].EndAddr=spuRvbAEnd2[1];
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_ReverbAddr_Hi:
+      spuRvbAddr2[1] = (((unsigned long)val&0xf)<<16)|(spuRvbAddr2[1]&0xFFFF);
+      SetReverbAddr(1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_ReverbAddr_Lo:
+      spuRvbAddr2[1] = (spuRvbAddr2[1] & 0xF0000) | (val & 0xFFFF);
+      SetReverbAddr(1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUirqAddr_Hi:
+      spuIrq2[0] = (((unsigned long)val&0xf)<<16)|(spuIrq2[0]&0xFFFF);
+      pSpuIrq[0]=spuMemC+(spuIrq2[0]<<1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUirqAddr_Lo:
+      spuIrq2[0] = (spuIrq2[0] & 0xF0000) | (val & 0xFFFF);
+      pSpuIrq[0]=spuMemC+(spuIrq2[0]<<1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUirqAddr_Hi:
+      spuIrq2[1] = (((unsigned long)val&0xf)<<16)|(spuIrq2[1]&0xFFFF);
+      pSpuIrq[1]=spuMemC+(spuIrq2[1]<<1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUirqAddr_Lo:
+      spuIrq2[1] = (spuIrq2[1] & 0xF0000) | (val & 0xFFFF);
+      pSpuIrq[1]=spuMemC+(spuIrq2[1]<<1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUrvolL:
+      rvb[0].VolLeft=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUrvolR:
+      rvb[0].VolRight=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUrvolL:
+      rvb[1].VolLeft=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUrvolR:
+      rvb[1].VolRight=val;
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUon1:
+      SoundOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUon2:
+      SoundOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUon1:
+      SoundOn(24,40,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUon2:
+      SoundOn(40,48,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUoff1:
+      SoundOff(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUoff2:
+      SoundOff(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUoff1:
+      SoundOff(24,40,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUoff2:
+      SoundOff(40,48,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_SPUend1:
+    case PS2_C0_SPUend2:
+      if(val) dwEndChannel2[0]=0;
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_SPUend1:
+    case PS2_C1_SPUend2:
+      if(val) dwEndChannel2[1]=0;
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_FMod1:
+      FModOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_FMod2:
+      FModOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_FMod1:
+      FModOn(24,40,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_FMod2:
+      FModOn(40,48,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_Noise1:
+      NoiseOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_Noise2:
+      NoiseOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_Noise1:
+      NoiseOn(24,40,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_Noise2:
+      NoiseOn(40,48,val);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_DryL1:
+      VolumeOn(0,16,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_DryL2:
+      VolumeOn(16,24,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_DryL1:
+      VolumeOn(24,40,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_DryL2:
+      VolumeOn(40,48,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_DryR1:
+      VolumeOn(0,16,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_DryR2:
+      VolumeOn(16,24,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_DryR1:
+      VolumeOn(24,40,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_DryR2:
+      VolumeOn(40,48,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_RVBon1_L:
+      ReverbOn(0,16,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_RVBon2_L:
+      ReverbOn(16,24,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_RVBon1_L:
+      ReverbOn(24,40,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_RVBon2_L:
+      ReverbOn(40,48,val,0);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_RVBon1_R:
+      ReverbOn(0,16,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_RVBon2_R:
+      ReverbOn(16,24,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_RVBon1_R:
+      ReverbOn(24,40,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C1_RVBon2_R:
+      ReverbOn(40,48,val,1);
+      break;
+    //-------------------------------------------------//
+    case PS2_C0_Reverb+0:
+      rvb[0].FB_SRC_A=(((unsigned long)val&0xf)<<16)|(rvb[0].FB_SRC_A&0xFFFF);
+      break;
+    case PS2_C0_Reverb+2:
+      rvb[0].FB_SRC_A=(rvb[0].FB_SRC_A & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+4:
+      rvb[0].FB_SRC_B=(((unsigned long)val&0xf)<<16)|(rvb[0].FB_SRC_B&0xFFFF);
+      break;
+    case PS2_C0_Reverb+6:
+      rvb[0].FB_SRC_B=(rvb[0].FB_SRC_B & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+8:
+      rvb[0].IIR_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_A0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+10:
+      rvb[0].IIR_DEST_A0=(rvb[0].IIR_DEST_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+12:
+      rvb[0].IIR_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_A1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+14:
+      rvb[0].IIR_DEST_A1=(rvb[0].IIR_DEST_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+16:
+      rvb[0].ACC_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_A0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+18:
+      rvb[0].ACC_SRC_A0=(rvb[0].ACC_SRC_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+20:
+      rvb[0].ACC_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_A1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+22:
+      rvb[0].ACC_SRC_A1=(rvb[0].ACC_SRC_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+24:
+      rvb[0].ACC_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_B0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+26:
+      rvb[0].ACC_SRC_B0=(rvb[0].ACC_SRC_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+28:
+      rvb[0].ACC_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_B1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+30:
+      rvb[0].ACC_SRC_B1=(rvb[0].ACC_SRC_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+32:
+      rvb[0].IIR_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_A0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+34:
+      rvb[0].IIR_SRC_A0=(rvb[0].IIR_SRC_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+36:
+      rvb[0].IIR_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_A1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+38:
+      rvb[0].IIR_SRC_A1=(rvb[0].IIR_SRC_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+40:
+      rvb[0].IIR_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_B0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+42:
+      rvb[0].IIR_DEST_B0=(rvb[0].IIR_DEST_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+44:
+      rvb[0].IIR_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_DEST_B1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+46:
+      rvb[0].IIR_DEST_B1=(rvb[0].IIR_DEST_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+48:
+      rvb[0].ACC_SRC_C0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_C0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+50:
+      rvb[0].ACC_SRC_C0=(rvb[0].ACC_SRC_C0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+52:
+      rvb[0].ACC_SRC_C1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_C1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+54:
+      rvb[0].ACC_SRC_C1=(rvb[0].ACC_SRC_C1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+56:
+      rvb[0].ACC_SRC_D0=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_D0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+58:
+      rvb[0].ACC_SRC_D0=(rvb[0].ACC_SRC_D0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+60:
+      rvb[0].ACC_SRC_D1=(((unsigned long)val&0xf)<<16)|(rvb[0].ACC_SRC_D1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+62:
+      rvb[0].ACC_SRC_D1=(rvb[0].ACC_SRC_D1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+64:
+      rvb[0].IIR_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_B1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+66:
+      rvb[0].IIR_SRC_B1=(rvb[0].IIR_SRC_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+68:
+      rvb[0].IIR_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].IIR_SRC_B0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+70:
+      rvb[0].IIR_SRC_B0=(rvb[0].IIR_SRC_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+72:
+      rvb[0].MIX_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_A0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+74:
+      rvb[0].MIX_DEST_A0=(rvb[0].MIX_DEST_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+76:
+      rvb[0].MIX_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_A1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+78:
+      rvb[0].MIX_DEST_A1=(rvb[0].MIX_DEST_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+80:
+      rvb[0].MIX_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_B0&0xFFFF);
+      break;
+    case PS2_C0_Reverb+82:
+      rvb[0].MIX_DEST_B0=(rvb[0].MIX_DEST_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_Reverb+84:
+      rvb[0].MIX_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[0].MIX_DEST_B1&0xFFFF);
+      break;
+    case PS2_C0_Reverb+86:
+      rvb[0].MIX_DEST_B1=(rvb[0].MIX_DEST_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C0_ReverbX+0:  rvb[0].IIR_ALPHA=(short)val;      break;
+    case PS2_C0_ReverbX+2:  rvb[0].ACC_COEF_A=(short)val;     break;
+    case PS2_C0_ReverbX+4:  rvb[0].ACC_COEF_B=(short)val;     break;
+    case PS2_C0_ReverbX+6:  rvb[0].ACC_COEF_C=(short)val;     break;
+    case PS2_C0_ReverbX+8:  rvb[0].ACC_COEF_D=(short)val;     break;
+    case PS2_C0_ReverbX+10: rvb[0].IIR_COEF=(short)val;       break;
+    case PS2_C0_ReverbX+12: rvb[0].FB_ALPHA=(short)val;       break;
+    case PS2_C0_ReverbX+14: rvb[0].FB_X=(short)val;           break;
+    case PS2_C0_ReverbX+16: rvb[0].IN_COEF_L=(short)val;      break;
+    case PS2_C0_ReverbX+18: rvb[0].IN_COEF_R=(short)val;      break;
+    //-------------------------------------------------//
+    case PS2_C1_Reverb+0:
+      rvb[1].FB_SRC_A=(((unsigned long)val&0xf)<<16)|(rvb[1].FB_SRC_A&0xFFFF);
+      break;
+    case PS2_C1_Reverb+2:
+      rvb[1].FB_SRC_A=(rvb[1].FB_SRC_A & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+4:
+      rvb[1].FB_SRC_B=(((unsigned long)val&0xf)<<16)|(rvb[1].FB_SRC_B&0xFFFF);
+      break;
+    case PS2_C1_Reverb+6:
+      rvb[1].FB_SRC_B=(rvb[1].FB_SRC_B & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+8:
+      rvb[1].IIR_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_A0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+10:
+      rvb[1].IIR_DEST_A0=(rvb[1].IIR_DEST_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+12:
+      rvb[1].IIR_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_A1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+14:
+      rvb[1].IIR_DEST_A1=(rvb[1].IIR_DEST_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+16:
+      rvb[1].ACC_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_A0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+18:
+      rvb[1].ACC_SRC_A0=(rvb[1].ACC_SRC_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+20:
+      rvb[1].ACC_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_A1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+22:
+      rvb[1].ACC_SRC_A1=(rvb[1].ACC_SRC_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+24:
+      rvb[1].ACC_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_B0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+26:
+      rvb[1].ACC_SRC_B0=(rvb[1].ACC_SRC_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+28:
+      rvb[1].ACC_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_B1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+30:
+      rvb[1].ACC_SRC_B1=(rvb[1].ACC_SRC_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+32:
+      rvb[1].IIR_SRC_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_A0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+34:
+      rvb[1].IIR_SRC_A0=(rvb[1].IIR_SRC_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+36:
+      rvb[1].IIR_SRC_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_A1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+38:
+      rvb[1].IIR_SRC_A1=(rvb[1].IIR_SRC_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+40:
+      rvb[1].IIR_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_B0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+42:
+      rvb[1].IIR_DEST_B0=(rvb[1].IIR_DEST_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+44:
+      rvb[1].IIR_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_DEST_B1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+46:
+      rvb[1].IIR_DEST_B1=(rvb[1].IIR_DEST_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+48:
+      rvb[1].ACC_SRC_C0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_C0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+50:
+      rvb[1].ACC_SRC_C0=(rvb[1].ACC_SRC_C0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+52:
+      rvb[1].ACC_SRC_C1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_C1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+54:
+      rvb[1].ACC_SRC_C1=(rvb[1].ACC_SRC_C1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+56:
+      rvb[1].ACC_SRC_D0=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_D0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+58:
+      rvb[1].ACC_SRC_D0=(rvb[1].ACC_SRC_D0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+60:
+      rvb[1].ACC_SRC_D1=(((unsigned long)val&0xf)<<16)|(rvb[1].ACC_SRC_D1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+62:
+      rvb[1].ACC_SRC_D1=(rvb[1].ACC_SRC_D1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+64:
+      rvb[1].IIR_SRC_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_B1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+66:
+      rvb[1].IIR_SRC_B1=(rvb[1].IIR_SRC_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+68:
+      rvb[1].IIR_SRC_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].IIR_SRC_B0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+70:
+      rvb[1].IIR_SRC_B0=(rvb[1].IIR_SRC_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+72:
+      rvb[1].MIX_DEST_A0=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_A0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+74:
+      rvb[1].MIX_DEST_A0=(rvb[1].MIX_DEST_A0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+76:
+      rvb[1].MIX_DEST_A1=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_A1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+78:
+      rvb[1].MIX_DEST_A1=(rvb[1].MIX_DEST_A1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+80:
+      rvb[1].MIX_DEST_B0=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_B0&0xFFFF);
+      break;
+    case PS2_C1_Reverb+82:
+      rvb[1].MIX_DEST_B0=(rvb[1].MIX_DEST_B0 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_Reverb+84:
+      rvb[1].MIX_DEST_B1=(((unsigned long)val&0xf)<<16)|(rvb[1].MIX_DEST_B1&0xFFFF);
+      break;
+    case PS2_C1_Reverb+86:
+      rvb[1].MIX_DEST_B1=(rvb[1].MIX_DEST_B1 & 0xF0000) | ((val) & 0xFFFF);
+      break;
+    case PS2_C1_ReverbX+0:  rvb[1].IIR_ALPHA=(short)val;      break;
+    case PS2_C1_ReverbX+2:  rvb[1].ACC_COEF_A=(short)val;     break;
+    case PS2_C1_ReverbX+4:  rvb[1].ACC_COEF_B=(short)val;     break;
+    case PS2_C1_ReverbX+6:  rvb[1].ACC_COEF_C=(short)val;     break;
+    case PS2_C1_ReverbX+8:  rvb[1].ACC_COEF_D=(short)val;     break;
+    case PS2_C1_ReverbX+10: rvb[1].IIR_COEF=(short)val;       break;
+    case PS2_C1_ReverbX+12: rvb[1].FB_ALPHA=(short)val;       break;
+    case PS2_C1_ReverbX+14: rvb[1].FB_X=(short)val;           break;
+    case PS2_C1_ReverbX+16: rvb[1].IN_COEF_L=(short)val;      break;
+    case PS2_C1_ReverbX+18: rvb[1].IN_COEF_R=(short)val;      break;
+   }
+
+ iSpuAsyncWait=0;
+
+}
+
+////////////////////////////////////////////////////////////////////////
+// READ REGISTER: called by main emu
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC unsigned short CALLBACK SPU2read(unsigned long reg)
+{
+ long r=reg&0xffff;
+
+#ifdef _WINDOWS
+// if(iDebugMode==1) logprintf("R_REG %X\r\n",reg&0xFFFF);
+#endif
+
+ iSpuAsyncWait=0;
+
+ if((r>=0x0000 && r<0x0180)||(r>=0x0400 && r<0x0580))  // some channel info?
+  {
+   switch(r&0x0f)
+    {
+     //------------------------------------------------// env value
+     case 10:                                           
+      {
+       int ch=(r>>4)&0x1f;
+       if(r>=0x400) ch+=24;
+       if(s_chan[ch].bNew) return 1;                   // we are started, but not processed? return 1
+       if(s_chan[ch].ADSRX.lVolume &&                  // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
+          !s_chan[ch].ADSRX.EnvelopeVol)                   
+        return 1;
+       return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16);
+      }break;
+    }
+  }
+
+ if((r>=0x01c0 && r<0x02E0)||(r>=0x05c0 && r<0x06E0))  // some channel info?
+  {
+   int ch=0;unsigned long rx=r;
+   if(rx>=0x400) {ch=24;rx-=0x400;}
+        
+   ch+=(rx-0x1c0)/12;
+   rx-=(ch%24)*12;
+   
+   switch(rx)
+    {
+     //------------------------------------------------//
+     case 0x1C4:
+      return (((s_chan[ch].pLoop-spuMemC)>>17)&0xF);
+      break;
+     case 0x1C6:
+      return (((s_chan[ch].pLoop-spuMemC)>>1)&0xFFFF);
+      break;
+     //------------------------------------------------//
+     case 0x1C8:
+      return (((s_chan[ch].pCurr-spuMemC)>>17)&0xF);
+      break;
+     case 0x1CA:
+      return (((s_chan[ch].pCurr-spuMemC)>>1)&0xFFFF);
+      break;
+     //------------------------------------------------//
+    }
+  }
+
+ switch(r)
+  {
+   //--------------------------------------------------//
+   case PS2_C0_SPUend1:
+     return (unsigned short)((dwEndChannel2[0]&0xFFFF));
+   case PS2_C0_SPUend2:
+     return (unsigned short)((dwEndChannel2[0]>>16));
+   //--------------------------------------------------//
+   case PS2_C1_SPUend1:
+     return (unsigned short)((dwEndChannel2[1]&0xFFFF));
+   case PS2_C1_SPUend2:
+     return (unsigned short)((dwEndChannel2[1]>>16));
+   //--------------------------------------------------//
+   case PS2_C0_ATTR:
+     return spuCtrl2[0];
+     break;
+   //--------------------------------------------------//
+   case PS2_C1_ATTR:
+     return spuCtrl2[1];
+     break;
+   //--------------------------------------------------//
+   case PS2_C0_SPUstat:
+     return spuStat2[0];
+     break;
+   //--------------------------------------------------//
+   case PS2_C1_SPUstat:
+     return spuStat2[1];
+     break;
+   //--------------------------------------------------//
+   case PS2_C0_SPUdata:
+     {
+      unsigned short s=spuMem[spuAddr2[0]];
+      spuAddr2[0]++;
+      if(spuAddr2[0]>0xfffff) spuAddr2[0]=0;
+      return s;
+     }
+   //--------------------------------------------------//
+   case PS2_C1_SPUdata:
+     {
+      unsigned short s=spuMem[spuAddr2[1]];
+      spuAddr2[1]++;
+      if(spuAddr2[1]>0xfffff) spuAddr2[1]=0;
+      return s;
+     }
+   //--------------------------------------------------//
+   case PS2_C0_SPUaddr_Hi:
+     return (unsigned short)((spuAddr2[0]>>16)&0xF);
+     break;
+   case PS2_C0_SPUaddr_Lo:
+     return (unsigned short)((spuAddr2[0]&0xFFFF));
+     break;
+   //--------------------------------------------------//
+   case PS2_C1_SPUaddr_Hi:
+     return (unsigned short)((spuAddr2[1]>>16)&0xF);
+     break;
+   case PS2_C1_SPUaddr_Lo:
+     return (unsigned short)((spuAddr2[1]&0xFFFF));
+     break;
+   //--------------------------------------------------//
+  }
+
+ return regArea[r>>1];
+}
+
+EXPORT_GCC void CALLBACK SPU2writePS1Port(unsigned long reg, unsigned short val)
+{
+ const u32 r=reg&0xfff;
+
+ if(r>=0xc00 && r<0xd80)	// channel info
+ {
+ 	SPU2write(r-0xc00, val);
+ 	return;
+ }
+
+ switch(r)
+   {
+    //-------------------------------------------------//
+    case H_SPUaddr:
+      spuAddr2[0] = (u32) val<<2;
+      break;
+    //-------------------------------------------------//
+    case H_SPUdata:
+      spuMem[spuAddr2[0]] = BFLIP16(val);
+      spuAddr2[0]++;
+      if(spuAddr2[0]>0xfffff) spuAddr2[0]=0;
+      break;
+    //-------------------------------------------------//
+    case H_SPUctrl:
+//      spuCtrl=val;
+      break;
+    //-------------------------------------------------//
+    case H_SPUstat:
+      spuStat2[0]=val & 0xf800;
+      break;
+    //-------------------------------------------------//
+    case H_SPUReverbAddr:
+      spuRvbAddr2[0] = val;
+      SetReverbAddr(0);
+      break;
+    //-------------------------------------------------//
+    case H_SPUirqAddr:
+      spuIrq2[0] = val<<2;
+      pSpuIrq[0]=spuMemC+((u32) val<<1);
+      break;
+    //-------------------------------------------------//
+    /* Volume settings appear to be at least 15-bit unsigned in this case.  
+       Definitely NOT 15-bit signed.  Probably 16-bit signed, so s16 type cast.
+       Check out "Chrono Cross:  Shadow's End Forest"
+    */
+    case H_SPUrvolL:
+      rvb[0].VolLeft=(s16)val;
+      //printf("%d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUrvolR:
+      rvb[0].VolRight=(s16)val;
+      //printf("%d\n",val);
+      break;
+    //-------------------------------------------------//
+
+/*
+    case H_ExtLeft:
+     //auxprintf("EL %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_ExtRight:
+     //auxprintf("ER %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUmvolL:
+     //auxprintf("ML %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUmvolR:
+     //auxprintf("MR %d\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUMute1:
+     //printf("M0 %04x\n",val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUMute2:
+    // printf("M1 %04x\n",val);
+      break;
+*/
+    //-------------------------------------------------//
+    case H_SPUon1:
+      SoundOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+     case H_SPUon2:
+      //printf("Boop: %08x: %04x\n",reg,val);
+      SoundOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUoff1:
+      SoundOff(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case H_SPUoff2:
+      SoundOff(16,24,val);
+	// printf("Boop: %08x: %04x\n",reg,val);
+      break;
+    //-------------------------------------------------//
+    case H_FMod1:
+      FModOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case H_FMod2:
+      FModOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case H_Noise1:
+      NoiseOn(0,16,val);
+      break;
+    //-------------------------------------------------//
+    case H_Noise2:
+      NoiseOn(16,24,val);
+      break;
+    //-------------------------------------------------//
+    case H_RVBon1:
+      ReverbOn(0,16,val,0);
+      break;
+
+    //-------------------------------------------------//
+    case H_RVBon2:
+      ReverbOn(16,24,val,0);
+      break;
+
+    //-------------------------------------------------//
+    case H_Reverb+0:
+      rvb[0].FB_SRC_A=val;
+      break;
+
+    case H_Reverb+2   : rvb[0].FB_SRC_B=(s16)val;       break;
+    case H_Reverb+4   : rvb[0].IIR_ALPHA=(s16)val;      break;
+    case H_Reverb+6   : rvb[0].ACC_COEF_A=(s16)val;     break;
+    case H_Reverb+8   : rvb[0].ACC_COEF_B=(s16)val;     break;
+    case H_Reverb+10  : rvb[0].ACC_COEF_C=(s16)val;     break;
+    case H_Reverb+12  : rvb[0].ACC_COEF_D=(s16)val;     break;
+    case H_Reverb+14  : rvb[0].IIR_COEF=(s16)val;       break;
+    case H_Reverb+16  : rvb[0].FB_ALPHA=(s16)val;       break;
+    case H_Reverb+18  : rvb[0].FB_X=(s16)val;           break;
+    case H_Reverb+20  : rvb[0].IIR_DEST_A0=(s16)val;    break;
+    case H_Reverb+22  : rvb[0].IIR_DEST_A1=(s16)val;    break;
+    case H_Reverb+24  : rvb[0].ACC_SRC_A0=(s16)val;     break;
+    case H_Reverb+26  : rvb[0].ACC_SRC_A1=(s16)val;     break;
+    case H_Reverb+28  : rvb[0].ACC_SRC_B0=(s16)val;     break;
+    case H_Reverb+30  : rvb[0].ACC_SRC_B1=(s16)val;     break;
+    case H_Reverb+32  : rvb[0].IIR_SRC_A0=(s16)val;     break;
+    case H_Reverb+34  : rvb[0].IIR_SRC_A1=(s16)val;     break;
+    case H_Reverb+36  : rvb[0].IIR_DEST_B0=(s16)val;    break;
+    case H_Reverb+38  : rvb[0].IIR_DEST_B1=(s16)val;    break;
+    case H_Reverb+40  : rvb[0].ACC_SRC_C0=(s16)val;     break;
+    case H_Reverb+42  : rvb[0].ACC_SRC_C1=(s16)val;     break;
+    case H_Reverb+44  : rvb[0].ACC_SRC_D0=(s16)val;     break;
+    case H_Reverb+46  : rvb[0].ACC_SRC_D1=(s16)val;     break;
+    case H_Reverb+48  : rvb[0].IIR_SRC_B1=(s16)val;     break;
+    case H_Reverb+50  : rvb[0].IIR_SRC_B0=(s16)val;     break;
+    case H_Reverb+52  : rvb[0].MIX_DEST_A0=(s16)val;    break;
+    case H_Reverb+54  : rvb[0].MIX_DEST_A1=(s16)val;    break;
+    case H_Reverb+56  : rvb[0].MIX_DEST_B0=(s16)val;    break;
+    case H_Reverb+58  : rvb[0].MIX_DEST_B1=(s16)val;    break;
+    case H_Reverb+60  : rvb[0].IN_COEF_L=(s16)val;      break;
+    case H_Reverb+62  : rvb[0].IN_COEF_R=(s16)val;      break;
+   }
+}
+
+EXPORT_GCC unsigned short CALLBACK SPU2readPS1Port(unsigned long reg)
+{
+ const u32 r=reg&0xfff;
+
+ if(r>=0x0c00 && r<0x0d80)
+  {
+  	return SPU2read(r-0xc00);
+  }
+
+ switch(r)
+  {
+//    case H_SPUctrl:
+//     return spuCtrl;
+     break;
+
+    case H_SPUstat:
+     return spuStat2[0];
+     break;
+        
+    case H_SPUaddr:
+     return (u16)(spuAddr2[0]>>2);
+     break;
+
+    case H_SPUdata:
+     {
+      u16 s=BFLIP16(spuMem[spuAddr2[0]]);
+      spuAddr2[0]++;
+      if(spuAddr2[0]>0xfffff) spuAddr2[0]=0;
+      return s;
+     }
+     break;
+
+    case H_SPUirqAddr:
+     return spuIrq2[0]>>2;
+     break;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SOUND ON register write
+////////////////////////////////////////////////////////////////////////
+
+void SoundOn(int start,int end,unsigned short val)     // SOUND ON PSX COMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if((val&1) && s_chan[ch].pStart)                    // mmm... start has to be set before key on !?!
+    {
+     s_chan[ch].bIgnoreLoop=0;
+     s_chan[ch].bNew=1;
+     dwNewChannel2[ch/24]|=(1<<(ch%24));               // bitfield for faster testing
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// SOUND OFF register write
+////////////////////////////////////////////////////////////////////////
+
+void SoundOff(int start,int end,unsigned short val)    // SOUND OFF PSX COMMAND
+{
+ int ch;
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // && s_chan[i].bOn)  mmm...
+    {
+     s_chan[ch].bStop=1;
+    }                                                  
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// FMOD register write
+////////////////////////////////////////////////////////////////////////
+
+void FModOn(int start,int end,unsigned short val)      // FMOD ON PSX COMMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // -> fmod on/off
+    {
+     if(ch>0) 
+      {
+       s_chan[ch].bFMod=1;                             // --> sound channel
+       s_chan[ch-1].bFMod=2;                           // --> freq channel
+      }
+    }
+   else
+    {
+     s_chan[ch].bFMod=0;                               // --> turn off fmod
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// NOISE register write
+////////////////////////////////////////////////////////////////////////
+
+void NoiseOn(int start,int end,unsigned short val)     // NOISE ON PSX COMMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // -> noise on/off
+    {
+     s_chan[ch].bNoise=1;
+    }
+   else 
+    {
+     s_chan[ch].bNoise=0;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// LEFT VOLUME register write
+////////////////////////////////////////////////////////////////////////
+
+// please note: sweep and phase invert are wrong... but I've never seen
+// them used
+
+void SetVolumeL(unsigned char ch,short vol)            // LEFT VOLUME
+{
+ s_chan[ch].iLeftVolRaw=vol;
+
+ if(vol&0x8000)                                        // sweep?
+  {
+   short sInc=1;                                       // -> sweep up?
+   if(vol&0x2000) sInc=-1;                             // -> or down?
+   if(vol&0x1000) vol^=0xffff;                         // -> mmm... phase inverted? have to investigate this
+   vol=((vol&0x7f)+1)/2;                               // -> sweep: 0..127 -> 0..64
+   vol+=vol/(2*sInc);                                  // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
+   vol*=128;
+  }
+ else                                                  // no sweep:
+  {
+   if(vol&0x4000)                                      // -> mmm... phase inverted? have to investigate this
+    //vol^=0xffff;
+    vol=0x3fff-(vol&0x3fff);
+  }
+
+ vol&=0x3fff;
+ s_chan[ch].iLeftVolume=vol;                           // store volume
+}
+
+////////////////////////////////////////////////////////////////////////
+// RIGHT VOLUME register write
+////////////////////////////////////////////////////////////////////////
+
+void SetVolumeR(unsigned char ch,short vol)            // RIGHT VOLUME
+{
+ s_chan[ch].iRightVolRaw=vol;
+
+ if(vol&0x8000)                                        // comments... see above :)
+  {
+   short sInc=1;
+   if(vol&0x2000) sInc=-1;
+   if(vol&0x1000) vol^=0xffff;
+   vol=((vol&0x7f)+1)/2;        
+   vol+=vol/(2*sInc);
+   vol*=128;
+  }
+ else            
+  {
+   if(vol&0x4000) //vol=vol^=0xffff;
+    vol=0x3fff-(vol&0x3fff);
+  }
+
+ vol&=0x3fff;
+ s_chan[ch].iRightVolume=vol;
+}
+
+////////////////////////////////////////////////////////////////////////
+// PITCH register write
+////////////////////////////////////////////////////////////////////////
+
+void SetPitch(int ch,unsigned short val)               // SET PITCH
+{
+ int NP;
+ double intr;
+
+ if(val>0x3fff) NP=0x3fff;                             // get pitch val
+ else           NP=val;
+
+ intr = (double)48000.0f / (double)44100.0f * (double)NP;
+ NP = (UINT32)intr;
+
+ s_chan[ch].iRawPitch=NP;
+
+ NP=(44100L*NP)/4096L;                                 // calc frequency
+
+ if(NP<1) NP=1;                                        // some security
+ s_chan[ch].iActFreq=NP;                               // store frequency
+}
+
+////////////////////////////////////////////////////////////////////////
+// REVERB register write
+////////////////////////////////////////////////////////////////////////
+
+void ReverbOn(int start,int end,unsigned short val,int iRight)  // REVERB ON PSX COMMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // -> reverb on/off
+    {
+     if(iRight) s_chan[ch].bReverbR=1;
+     else       s_chan[ch].bReverbL=1;
+    }
+   else 
+    {
+     if(iRight) s_chan[ch].bReverbR=0;
+     else       s_chan[ch].bReverbL=0;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// REVERB START register write
+////////////////////////////////////////////////////////////////////////
+
+void SetReverbAddr(int core)
+{
+ long val=spuRvbAddr2[core];
+ 
+ if(rvb[core].StartAddr!=val)
+  {
+   if(val<=0x27ff)
+    {
+     rvb[core].StartAddr=rvb[core].CurrAddr=0;
+    }
+   else
+    {
+     rvb[core].StartAddr=val;
+     rvb[core].CurrAddr=rvb[core].StartAddr;
+    } 
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// DRY LEFT/RIGHT per voice switches
+////////////////////////////////////////////////////////////////////////
+
+void VolumeOn(int start,int end,unsigned short val,int iRight)  // VOLUME ON PSX COMMAND
+{
+ int ch;
+
+ for(ch=start;ch<end;ch++,val>>=1)                     // loop channels
+  {
+   if(val&1)                                           // -> reverb on/off
+    {
+     if(iRight) s_chan[ch].bVolumeR=1;
+     else       s_chan[ch].bVolumeL=1;
+    }
+   else 
+    {
+     if(iRight) s_chan[ch].bVolumeR=0;
+     else       s_chan[ch].bVolumeL=0;
+    }
+  }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/registers.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,845 @@
+/***************************************************************************
+                         registers.h  -  description
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - generic cleanup for the Peops release... register values by Kanodin &
+//   his team
+//
+//*************************************************************************//
+
+//###########################################################################
+
+#define PS2_C0_SPUaddr_Hi    (0x000 + 0x1A8)
+#define PS2_C0_SPUaddr_Lo    (0x000 + 0x1AA)
+#define PS2_C1_SPUaddr_Hi    (0x400 + 0x1A8)
+#define PS2_C1_SPUaddr_Lo    (0x400 + 0x1AA)
+#define PS2_C0_SPUdata       (0x000 + 0x1AC)
+#define PS2_C1_SPUdata       (0x400 + 0x1AC)
+
+#define PS2_C0_SPUDMActrl    (0x000 + 0x1AE)
+#define PS2_C1_SPUDMActrl    (0x400 + 0x1AE)
+
+#define PS2_C0_SPUstat       (0x000 + 0x344)
+#define PS2_C1_SPUstat       (0x400 + 0x344)
+#define PS2_C0_ReverbAddr_Hi (0x000 + 0x2E0)
+#define PS2_C0_ReverbAddr_Lo (0x000 + 0x2E2)
+#define PS2_C1_ReverbAddr_Hi (0x400 + 0x2E0)
+#define PS2_C1_ReverbAddr_Lo (0x400 + 0x2E2)
+
+#define PS2_C0_ReverbAEnd_Hi (0x000 + 0x33C)
+#define PS2_C0_ReverbAEnd_Lo (0x000 + 0x33E)
+#define PS2_C1_ReverbAEnd_Hi (0x400 + 0x33C)
+#define PS2_C1_ReverbAEnd_Lo (0x400 + 0x33E)
+
+#define PS2_C0_DryL1         (0x000 + 0x188)
+#define PS2_C1_DryL1         (0x400 + 0x188)
+#define PS2_C0_DryL2         (0x000 + 0x18A)
+#define PS2_C1_DryL2         (0x400 + 0x18A)
+
+#define PS2_C0_DryR1         (0x000 + 0x190)
+#define PS2_C1_DryR1         (0x400 + 0x190)
+#define PS2_C0_DryR2         (0x000 + 0x192)
+#define PS2_C1_DryR2         (0x400 + 0x192)
+
+#define PS2_C0_ATTR          (0x000 + 0x19A)
+#define PS2_C1_ATTR          (0x400 + 0x19A)
+#define PS2_C0_ADMAS         (0x000 + 0x1B0)
+#define PS2_C1_ADMAS         (0x400 + 0x1B0)
+
+#define PS2_C0_SPUirqAddr_Hi (0x000 + 0x19C)
+#define PS2_C0_SPUirqAddr_Lo (0x000 + 0x19D)
+#define PS2_C1_SPUirqAddr_Hi (0x400 + 0x19C)
+#define PS2_C1_SPUirqAddr_Lo (0x400 + 0x19D)
+#define PS2_C0_SPUrvolL      (0x000 + 0x764)
+#define PS2_C0_SPUrvolR      (0x000 + 0x766)
+#define PS2_C1_SPUrvolL      (0x028 + 0x764)
+#define PS2_C1_SPUrvolR      (0x028 + 0x766)
+#define PS2_C0_SPUon1        (0x000 + 0x1A0)
+#define PS2_C0_SPUon2        (0x000 + 0x1A2)
+#define PS2_C1_SPUon1        (0x400 + 0x1A0)
+#define PS2_C1_SPUon2        (0x400 + 0x1A2)
+#define PS2_C0_SPUoff1       (0x000 + 0x1A4)
+#define PS2_C0_SPUoff2       (0x000 + 0x1A6)
+#define PS2_C1_SPUoff1       (0x400 + 0x1A4)
+#define PS2_C1_SPUoff2       (0x400 + 0x1A6)
+#define PS2_C0_FMod1         (0x000 + 0x180)
+#define PS2_C0_FMod2         (0x000 + 0x182)
+#define PS2_C1_FMod1         (0x400 + 0x180)
+#define PS2_C1_FMod2         (0x400 + 0x182)
+#define PS2_C0_Noise1        (0x000 + 0x184)
+#define PS2_C0_Noise2        (0x000 + 0x186)
+#define PS2_C1_Noise1        (0x400 + 0x184)
+#define PS2_C1_Noise2        (0x400 + 0x186)
+
+#define PS2_C0_RVBon1_L      (0x000 + 0x18C)
+#define PS2_C0_RVBon2_L      (0x000 + 0x18E)
+#define PS2_C0_RVBon1_R      (0x000 + 0x194)
+#define PS2_C0_RVBon2_R      (0x000 + 0x196)
+
+#define PS2_C1_RVBon1_L      (0x400 + 0x18C)
+#define PS2_C1_RVBon2_L      (0x400 + 0x18E)
+#define PS2_C1_RVBon1_R      (0x400 + 0x194)
+#define PS2_C1_RVBon2_R      (0x400 + 0x196)
+#define PS2_C0_Reverb        (0x000 + 0x2E4)
+#define PS2_C1_Reverb        (0x400 + 0x2E4)
+#define PS2_C0_ReverbX       (0x000 + 0x774)
+#define PS2_C1_ReverbX       (0x028 + 0x774)
+#define PS2_C0_SPUend1       (0x000 + 0x340)
+#define PS2_C0_SPUend2       (0x000 + 0x342)
+#define PS2_C1_SPUend1       (0x400 + 0x340)
+#define PS2_C1_SPUend2       (0x400 + 0x342)
+
+#define H_SPUReverbAddr  0x0da2
+   
+#define H_SPUirqAddr     0x0da4
+   
+#define H_SPUaddr        0x0da6
+   
+#define H_SPUdata        0x0da8
+   
+#define H_SPUctrl        0x0daa
+   
+#define H_SPUstat        0x0dae
+   
+#define H_SPUmvolL       0x0d80
+   
+#define H_SPUmvolR       0x0d82
+   
+#define H_SPUrvolL       0x0d84
+   
+#define H_SPUrvolR       0x0d86
+   
+#define H_SPUon1         0x0d88
+   
+#define H_SPUon2         0x0d8a
+   
+#define H_SPUoff1        0x0d8c
+   
+#define H_SPUoff2        0x0d8e
+   
+#define H_FMod1          0x0d90
+   
+#define H_FMod2          0x0d92
+   
+#define H_Noise1         0x0d94
+   
+#define H_Noise2         0x0d96
+   
+#define H_RVBon1         0x0d98
+   
+#define H_RVBon2         0x0d9a
+#define H_SPUMute1       0x0d9c
+#define H_SPUMute2       0x0d9e
+#define H_CDLeft         0x0db0
+#define H_CDRight        0x0db2
+#define H_ExtLeft        0x0db4
+#define H_ExtRight       0x0db6
+#define H_Reverb         0x0dc0
+
+
+//###########################################################################
+
+/*
+ Included the info received in Regs.txt list by Neill Corlett - Kanodin
+
+ Voice parameters:
+ SD_VP_VOLL, SD_VP_VOLR   - Volume left/right per voice.  Assuming identical to PS1.
+ SD_VP_PITCH              - Pitch scaler 0000-3FFF. Assuming identical to PS1.
+ SD_VP_ADSR1, SD_VP_ADSR1 - Envelope data. Bitfields are documented as identical to PS1.
+ SD_VP_ENVX               - Current envelope value. Assuming identical to PS1.
+ SD_VP_VOLXL, SD_VP_VOLXR - Current voice volume left/right. Does not exist on the PS1.
+                            Guessing that this is handy for the increase/decrease modes.
+
+ Voice addresses:
+
+ SD_VA_SSA                - Sample start address; assuming identical to PS1
+ SD_VA_LSAX               - Loop start address; assuming identical to PS1
+ SD_VA_NAX                - Seems to be documented as the current playing address.
+                            Does not exist on PS1.
+
+ Switches:
+
+ SD_S_PMON                - Pitch mod; assuming identical to PS1
+ SD_S_NON                 - Noise; assuming identical to PS1
+ SD_S_VMIXL, SD_S_VMIXR   - Voice mix L/R. Guessing this is just a separate L/R version 
+                            of the "voice enable" bits on the PS1.
+ SD_S_VMIXEL, SD_S_VMIXER - Voice effect mix L/R. Guessing this is just a separate L/R
+                            version of the "voice reverb enable" bits on the PS1.
+ SD_S_KON, SD_S_KOFF      - Key on/off; assuming identical to PS1
+
+
+ Addresses:
+
+ SD_A_TSA                 - Transfer start address; assuming identical to PS1
+ SD_A_ESA                 - Effect start address - this is probably analogous to the
+                            PS1's reverb work area start address
+ SD_A_EEA                 - Effect end address - this would've been fixed to 0x7FFFF on
+                            the PS1; settable in 128K increments on the PS2.
+ SD_A_IRQA                - IRQ address; assuming identical to PS1
+
+ Volume parameters:
+
+ SD_P_MVOLL, SD_P_MVOLR   - Master volume L/R; assuming identical to PS1
+ SD_P_EVOLL, SD_P_EVOLR   - Effect volume L/R; assuming analogous to RVOL on the PS1
+ SD_P_AVOLL, SD_P_AVOLR   - External input volume L/R
+                            This is probably where CORE0 connects to CORE1
+ SD_P_BVOLL, SD_P_BVOLR   - Sound data input volume - perhaps this is the volume of
+                            the raw PCM auto-DMA input? analogous to CD input volume?
+ SD_P_MVOLXL, SD_P_MVOLXR - Current master volume L/R; seems self-explanatory
+
+ SD_P_MMIX                - Mixer / effect enable bits.
+                             bit 11 = MSNDL  = voice output dry L
+                                 10 = MSNDR  = voice output dry R
+                                  9 = MSNDEL = voice output wet L
+                                  8 = MSNDER = voice output wet R
+                                  7 = MINL   = sound data input dry L
+                                  6 = MINR   = sound data input dry R
+                                  5 = MINEL  = sound data input wet L
+                                  4 = MINER  = sound data input wet R
+                                  3 = SINL   = core external input dry L
+                                  2 = SINR   = core external input dry R
+                                  1 = SINEL  = core external input wet L
+                                  0 = SINER  = core external input wet R
+
+Core attributes (SD_C)
+
+    bit 4..5      - DMA related
+    bit 6         - IRQ enable
+    bit 7         - effect enable (reverb enable)
+    bit 13..8     - noise clock
+    bit 14        - mute
+
+ - if you READ the two DMA related bits, if either are set, the channel is
+   considered "busy" by sceSdVoiceTrans
+
+
+
+Reverb parameters:
+
+  Same as PS1 reverb (I used the names from my reverb doc).
+
+
+Other PS2 IOP notes
+
+ There's two DMA controllers:
+ The original one at 1F801080-1F8010FF (channels 0-6)
+ A new one at        1F801500-1F80157F (channels 7-13)
+
+ They appear to function the same way - 7 channels each.
+
+ SPU CORE0's DMA channel is 4 as per usual
+ SPU CORE1's DMA channel is 7
+
+DMA channel 10 is SIF
+ 
+ Original INTR controller at 1F801000-1F80107F
+
+ All interrupt handling seems to be done using the old INTR, but
+ with some new bits defined:
+
+
+
+ Reading from 1F801078 masks interrupts and returns 1 if they weren't
+ masked before.  Writing 1 to 1F801078 re-enables interrupts.
+ Writing 0 doesn't.  Maybe it was like that on the original PS1 too.
+
+Six root counters:
+
+ RTC#   address      sources           size     prescale     interrupt#
+0      0x1F801100   sysclock,pixel    16 bit   1 only       4
+1      0x1F801110   sysclock,hline    16 bit   1 only       5
+2      0x1F801120   sysclock          16 bit   1,8          6
+3      0x1F801480   sysclock,hline    32 bit   1 only       14
+4      0x1F801490   sysclock          32 bit   1,8,16,256   15
+5      0x1F8014A0   sysclock          32 bit   1,8,16,256   16
+
+Count (0x0) and Compare (0x8) registers work as before, only with more bits
+in the new counters.
+
+Mode (0x4) works like this when written:
+
+  bits 0..2    gate
+  bit  3       reset on target
+  bit  4       target interrupt enable
+  bit  5       overflow interrupt enable
+  bit  6       master enable (?)
+  bit  7       ?
+  bit  8       clock select
+  bit  9       prescale (OLD)
+  bit  10..12  ?
+  bit  13..14  prescale (NEW)
+  bit  15      ? always set to 1
+
+Gate:
+   TM_NO_GATE                   000
+   TM_GATE_ON_Count             001
+   TM_GATE_ON_ClearStart        011
+   TM_GATE_ON_Clear_OFF_Start   101
+   TM_GATE_ON_Start             111
+
+   V-blank  ----+    +----------------------------+    +------
+                |    |                            |    |
+                |    |                            |    |
+                +----+                            +----+
+ TM_NO_GATE:
+
+                0================================>============
+
+ TM_GATE_ON_Count:
+
+                <---->0==========================><---->0=====
+
+ TM_GATE_ON_ClearStart:
+
+                0====>0================================>0=====
+
+ TM_GATE_ON_Clear_OFF_Start:
+
+                0====><-------------------------->0====><-----
+
+ TM_GATE_ON_Start:
+
+                <---->0==========================>============
+
+  reset on target: if set, counter resets to 0 when Compare value is reached
+
+  target interrupt enable: if set, interrupt when Compare value is reached
+  overflow interrupt enable: if set, interrupt when counter overflows
+
+  master enable: if this bit is clear, the timer should do nothing.
+
+  clock select: for counters 0, 1, and 3, setting this will select the alternate
+  counter (pixel or hline)
+
+  prescale (OLD): for counter 2 only. set this to prescale (divide) by 8.
+
+  prescale (NEW): for counters 4 and 5 only:
+
+  00 = prescale by 1
+  01 = prescale by 8
+  10 = prescale by 16
+  11 = prescale by 256
+
+Writing 0x4 also clears the counter. (I think.)
+
+When 0x4 is read, it becomes Status:
+
+  bit  0..10   ?
+  bit  11      compare value was reached
+  bit  12      count overflowed
+  bit  13..15  ?
+
+Reading probably clears these bits.
+
+
+
+ 1F8014B0 (word) - timer-related but otherwise unknown
+ 1F8014C0 (word) - timer-related but otherwise unknown
+
+
+ don't currently know how the interrupts work for DMA ch7 yet
+
+ 1F801060 (word) - address of some kind.
+
+ 1F801450 (word) -
+    if bit 3 is SET, we're in PS1 mode.
+    if bit 3 is CLEAR, we're in PS2 IOP mode.
+
+ 1F802070 (byte) - unknown. status byte of some kind? visible to EE?
+
+ 1D000000-1D00007F (?) - SIF related
+
+ 1D000020 (word) - read counter of some sort?
+                   sceSifInit waits for bit 0x10000 of this to be set.
+ 1D000030 (word) - read counter of some sort?
+ 1D000040 (word) - read bits 0x20, 0x40 mean something
+ 1D000060 (word) - used to detect whether the SIF interface exists
+                   read must be 0x1D000060, or the top 20 bits must be zero 
+*/
+
+/*
+
+// DirectX Audio SPU2 Driver for PCSX2
+// audio.c by J.F. and Kanodin (hooper1@cox.net)
+//
+// Copyright 2003 J.F. and Kanodin, and distributed under the
+// terms of the GNU General Public License, v2 or later.
+// http://www.gnu.org/copyleft/gpl.html.
+
+Included these just in case you need them J.F. - Kanodin
+
+// Core Start Addresses
+#define CORE0 0x1f900000
+#define CORE1 0x1f900400
+
+
+   #define IOP_INT_VBLANK  (1<<0)
+   #define IOP_INT_GM      (1<<1)
+   #define IOP_INT_CDROM   (1<<2)
+   #define IOP_INT_DMA     (1<<3)
+   #define IOP_INT_RTC0    (1<<4)
+   #define IOP_INT_RTC1    (1<<5)
+   #define IOP_INT_RTC2    (1<<6)
+   #define IOP_INT_SIO0    (1<<7)
+   #define IOP_INT_SIO1    (1<<8)
+   #define IOP_INT_SPU     (1<<9)
+   #define IOP_INT_PIO     (1<<10)
+   #define IOP_INT_EVBLANK (1<<11)
+   #define IOP_INT_DVD     (1<<12)
+   #define IOP_INT_PCMCIA  (1<<13)
+   #define IOP_INT_RTC3    (1<<14)
+   #define IOP_INT_RTC4    (1<<15)
+   #define IOP_INT_RTC5    (1<<16)
+   #define IOP_INT_SIO2    (1<<17)
+   #define IOP_INT_HTR0    (1<<18)
+   #define IOP_INT_HTR1    (1<<19)
+   #define IOP_INT_HTR2    (1<<20)
+   #define IOP_INT_HTR3    (1<<21)
+   #define IOP_INT_USB     (1<<22)
+   #define IOP_INT_EXTR    (1<<23)
+   #define IOP_INT_FWRE    (1<<24)
+   #define IOP_INT_FDMA    (1<<25)    
+
+// CORE0 => +0x000, CORE1 => +0x400
+
+// individual voice parameter regs
+
+#define VP_VOLL(cr, vc)  (0x400 * cr + 0x000 + (vc << 4))    // voice volume (left)
+#define VP_VOLR(cr, vc)  (0x400 * cr + 0x002 + (vc << 4))    // voice volume (right)
+#define VP_PITCH(cr, vc) (0x400 * cr + 0x004 + (vc << 4))    // voice pitch
+#define VP_ADSR1(cr, vc) (0x400 * cr + 0x006 + (vc << 4))    // voice envelope (AR, DR, SL)
+#define VP_ADSR2(cr, vc) (0x400 * cr + 0x008 + (vc << 4))    // voice envelope (SR, RR)
+#define VP_ENVX(cr, vc)  (0x400 * cr + 0x00A + (vc << 4))    // voice envelope (current value)
+#define VP_VOLXL(cr, vc) (0x400 * cr + 0x00C + (vc << 4))    // voice volume (current value left)
+#define VP_VOLXR(cr, vc) (0x400 * cr + 0x00E + (vc << 4))    // voice volume (current value right)
+
+#define VA_SSA(cr, vc)   (0x400 * cr + 0x1C0 + (vc * 12))    // voice waveform data start address
+#define VA_LSAX(cr, vc)  (0x400 * cr + 0x1C4 + (vc * 12))    // voice waveform data loop address
+#define VA_NAX(cr, vc)   (0x400 * cr + 0x1C8 + (vc * 12))    // voice waveform data next address
+
+// common settings
+
+#define S_PMON(cr)       (0x400 * cr + 0x180)                // pitch modulation on
+#define S_NON(cr)        (0x400 * cr + 0x184)                // noise generator on
+#define S_VMIXL(cr)      (0x400 * cr + 0x188)                // voice output mixing (dry left)
+#define S_VMIXEL(cr)     (0x400 * cr + 0x18C)                // voice output mixing (wet left)
+#define S_VMIXR(cr)      (0x400 * cr + 0x190)                // voice output mixing (dry right)
+#define S_VMIXER(cr)     (0x400 * cr + 0x194)                // voice output mixing (wet right)
+#define P_MMIX(cr)       (0x400 * cr + 0x198)                // output type after voice mixing (See paragraph below)
+#define P_ATTR(cr)       (0x400 * cr + 0x19A)                // core attributes (See paragraph below)
+#define A_IRQA(cr)       (0x400 * cr + 0x19C)                // IRQ address 
+#define S_KON(cr)        (0x400 * cr + 0x1A0)                // key on (start voice sound generation)
+#define S_KOFF(cr)       (0x400 * cr + 0x1A4)                // key off (end voice sound generation)
+#define A_TSA(cr)        (0x400 * cr + 0x1A8)                // DMA transfer start address
+#define P_DATA(cr)       (0x400 * cr + 0x1AC)                // DMA data register
+#define P_CTRL(cr)       (0x400 * cr + 0x1AE)                // DMA control register
+#define P_ADMAS(cr)      (0x400 * cr + 0x1B0)                // AutoDMA status
+
+#define A_ESA(cr)        (0x400 * cr + 0x2E0)                // effects work area start address
+
+#define FB_SRC_A(cr)     (0x400 * cr + 0x2E4)      
+#define FB_SRC_B(cr)     (0x400 * cr + 0x2E8)      
+#define IIR_DEST_A0(cr)  (0x400 * cr + 0x2EC)      
+#define IIR_DEST_A1(cr)  (0x400 * cr + 0x2F0)      
+#define ACC_SRC_A0(cr)   (0x400 * cr + 0x2F4)     
+#define ACC_SRC_A1(cr)   (0x400 * cr + 0x2F8)
+#define ACC_SRC_B0(cr)   (0x400 * cr + 0x2FC)  
+
+#define ACC_SRC_B1(cr)   (0x400 * cr + 0x300)      
+#define IIR_SRC_A0(cr)   (0x400 * cr + 0x304)      
+#define IIR_SRC_A1(cr)   (0x400 * cr + 0x308)      
+#define IIR_DEST_B0(cr)  (0x400 * cr + 0x30C)      
+#define IIR_DEST_B1(cr)  (0x400 * cr + 0x310)      
+#define ACC_SRC_C0(cr)   (0x400 * cr + 0x314)      
+#define ACC_SRC_C1(cr)   (0x400 * cr + 0x318)  
+
+#define ACC_SRC_D0(cr)   (0x400 * cr + 0x31C)      
+#define ACC_SRC_D1(cr)   (0x400 * cr + 0x320)      
+#define IIR_SRC_B1(cr)   (0x400 * cr + 0x324)      
+#define IIR_SRC_B0(cr)   (0x400 * cr + 0x328)      
+#define MIX_DEST_A0(cr)  (0x400 * cr + 0x32C)      
+#define MIX_DEST_A1(cr)  (0x400 * cr + 0x330)      
+#define MIX_DEST_B0(cr)  (0x400 * cr + 0x334)      
+#define MIX_DEST_B1(cr)  (0x400 * cr + 0x338)      
+
+#define A_EEA(cr)        (0x400 * cr + 0x33C)                // effects work area end address
+
+#define P_ENDX(cr)       (0x400 * cr + 0x340)                // voice loop end status
+#define P_STAT(cr)       (0x400 * cr + 0x344)                // DMA status register
+#define P_ENDS(cr)       (0x400 * cr + 0x346)                // ?
+
+// CORE0 => +0x400, CORE1 => +0x428
+
+#define P_MVOLL(cr)      (0x28 * cr + 0x760)                 // master volume (left)
+#define P_MVOLR(cr)      (0x28 * cr + 0x762)                 // master volume (right)
+#define P_EVOLL(cr)      (0x28 * cr + 0x764)                 // effect return volume (left)
+#define P_EVOLR(cr)      (0x28 * cr + 0x766)                 // effect return volume (right)
+#define P_AVOLL(cr)      (0x28 * cr + 0x768)                 // core external input volume (left)
+#define P_AVOLR(cr)      (0x28 * cr + 0x76A)                 // core external input volume (right)
+#define P_BVOLL(cr)      (0x28 * cr + 0x76C)                 // sound data input volume (left)
+#define P_BVOLR(cr)      (0x28 * cr + 0x76E)                 // sound data input volume (right)
+#define P_MVOLXL(cr)     (0x28 * cr + 0x770)                 // current master volume (left)
+#define P_MVOLXR(cr)     (0x28 * cr + 0x772)                 // current master volume (right)
+
+#define IIR_ALPHA(cr)    (0x28 * cr + 0x774)      
+#define ACC_COEF_A(cr)   (0x28 * cr + 0x776)      
+#define ACC_COEF_B(cr)   (0x28 * cr + 0x778)      
+#define ACC_COEF_C(cr)   (0x28 * cr + 0x77A)      
+#define ACC_COEF_D(cr)   (0x28 * cr + 0x77C)      
+#define IIR_COEF(cr)     (0x28 * cr + 0x77E)      
+#define FB_ALPHA(cr)     (0x28 * cr + 0x780)      
+#define FB_X(cr)         (0x28 * cr + 0x782)      
+#define IN_COEF_L(cr)    (0x28 * cr + 0x784)      
+#define IN_COEF_R(cr)    (0x28 * cr + 0x786)      
+
+// CORE1 only => +0x400
+
+#define SPDIF_OUT        0x7C0                               // SPDIF Out: OFF/'PCM'/Bitstream/Bypass 
+#define SPDIF_MODE       0x7C6
+#define SPDIF_MEDIA      0x7C8                               // SPDIF Media: 'CD'/DVD	
+#define SPDIF_COPY       0x7CA                               // SPDIF Copy Protection
+
+// PS1 SPU CORE
+
+// individual voice settings
+
+#define SPU_VP_PITCH(vc) (0xC04 + (vc << 4))                 // voice pitch
+#define SPU_VA_SSA(vc)   (0xC06 + (vc << 4))                 // voice waveform data start address
+#define SPU_VP_ADSR(vc)  (0xC08 + (vc << 4))                 // voice envelope
+#define SPU_VA_SSA(vc)   (0xC0E + (vc << 4))                 // voice waveform data loop address
+
+// common settings
+
+#define SPU_P_MVOLL      0xD80                               // master volume (left)
+#define SPU_P_MVOLR      0xD82                               // master volume (right)
+#define SPU_P_RVOLL      0xD84                               // effect return volume (left)
+#define SPU_P_RVOLR      0xD86                               // effect return volume (right)
+#define SPU_S_KON1       0xD88                               // key on
+#define SPU_S_KON2       0xD8A                               // 
+#define SPU_S_KOFF1      0xD8C                               // key off
+#define SPU_S_KOFF2      0xD8E                               // 
+#define SPU_S_PMON1      0xD90                               // pitch modulation on
+#define SPU_S_PMON2      0xD92                               // 
+#define SPU_S_NON1       0xD94                               // noise generator on
+#define SPU_S_NON2       0xD96                               // 
+#define SPU_S_RVBON1     0xD98                               // effects on
+#define SPU_S_RVBON2     0xD9A                               // 
+#define SPU_S_MUTE1      0xD9C                               // voice mute
+#define SPU_S_MUTE2      0xD9E                               // 
+
+#define SPU_A_ESA        0xDA2                               // effects work area start
+#define SPU_A_IRQA       0xDA4                               // IRQ address
+#define SPU_A_TSA        0xDA6                               // DMA transfer start address
+#define SPU_P_DATA       0xDA8                               // DMA data register
+#define SPU_P_CTRL       0xDAA                               // DMA control register
+#define SPU_P_STAT       0xDAE                               // DMA status register
+
+#define SPU_P_CDL        0xDB0                               // sound data input volume (left)
+#define SPU_P_CDR        0xDB2                               // sound data input volume (right)
+#define SPU_P_EXTL       0xDB4                               // external input volume (left)
+#define SPU_P_EXTR       0xDB6                               // external input volume (right)
+
+#define SPU_P_REVERB     0xDC0                               // effects control
+
+
+// Individual voice parameter regs CORE 0
+// Only
+
+
+#define VP_VOLL(cr, vc)  (0x400 * cr + 0x000 + (vc << 4))    // voice volume (left)
+#define VP_VOLR(cr, vc)  (0x400 * cr + 0x002 + (vc << 4))    // voice volume (right)
+#define VP_PITCH(cr, vc) (0x400 * cr + 0x004 + (vc << 4))    // voice pitch
+#define VP_ADSR1(cr, vc) (0x400 * cr + 0x006 + (vc << 4))    // voice envelope (AR, DR, SL)
+#define VP_ADSR2(cr, vc) (0x400 * cr + 0x008 + (vc << 4))    // voice envelope (SR, RR)
+#define VP_ENVX(cr, vc)  (0x400 * cr + 0x00A + (vc << 4))    // voice envelope (current value)
+#define VP_VOLXL(cr, vc) (0x400 * cr + 0x00C + (vc << 4))    // voice volume (current value left)
+#define VP_VOLXR(cr, vc) (0x400 * cr + 0x00E + (vc << 4))    // voice volume (current value right)
+
+#define VA_SSA(cr, vc)   (0x400 * cr + 0x1C0 + (vc * 12))    // voice waveform data start address
+#define VA_LSAX(cr, vc)  (0x400 * cr + 0x1C4 + (vc * 12))    // voice waveform data loop address
+#define VA_NAX(cr, vc)   (0x400 * cr + 0x1C8 + (vc * 12))    // voice waveform data next address
+
+
+// CORE 0 Common Settings
+
+
+#define S_PMON(cr)       (0x400 * cr + 0x180)                // pitch modulation on
+#define S_NON(cr)        (0x400 * cr + 0x184)                // noise generator on
+#define S_VMIXL(cr)      (0x400 * cr + 0x188)                // voice output mixing (dry left)
+#define S_VMIXEL(cr)     (0x400 * cr + 0x18C)                // voice output mixing (wet left)
+#define S_VMIXR(cr)      (0x400 * cr + 0x190)                // voice output mixing (dry right)
+#define S_VMIXER(cr)     (0x400 * cr + 0x194)                // voice output mixing (wet right)
+#define P_MMIX(cr)       (0x400 * cr + 0x198)                // output type after voice mixing (See paragraph below)
+#define P_ATTR(cr)       (0x400 * cr + 0x19A)                // core attributes (See paragraph below)
+#define A_IRQA(cr)       (0x400 * cr + 0x19C)                // IRQ address 
+#define S_KON(cr)        (0x400 * cr + 0x1A0)                // key on (start voice sound generation)
+#define S_KOFF(cr)       (0x400 * cr + 0x1A4)                // key off (end voice sound generation)
+#define A_TSA(cr)        (0x400 * cr + 0x1A8)                // DMA transfer start address
+#define P_DATA(cr)       (0x400 * cr + 0x1AC)                // DMA data register
+#define P_CTRL(cr)       (0x400 * cr + 0x1AE)                // DMA control register
+#define P_ADMAS(cr)      (0x400 * cr + 0x1B0)                // AutoDMA status
+
+#define A_ESA(cr)        (0x400 * cr + 0x2E0)                // effects work area start address
+
+
+// Core 0 Reverb Addresses
+
+
+#define FB_SRC_A(cr)     (0x400 * cr + 0x2E4)      
+#define FB_SRC_B(cr)     (0x400 * cr + 0x2E8)      
+#define IIR_DEST_A0(cr)  (0x400 * cr + 0x2EC)      
+#define IIR_DEST_A1(cr)  (0x400 * cr + 0x2F0)      
+#define ACC_SRC_A0(cr)   (0x400 * cr + 0x2F4)     
+#define ACC_SRC_A1(cr)   (0x400 * cr + 0x2F8)
+#define ACC_SRC_B0(cr)   (0x400 * cr + 0x2FC)  
+
+#define ACC_SRC_B1(cr)   (0x400 * cr + 0x300)      
+#define IIR_SRC_A0(cr)   (0x400 * cr + 0x304)      
+#define IIR_SRC_A1(cr)   (0x400 * cr + 0x308)      
+#define IIR_DEST_B0(cr)  (0x400 * cr + 0x30C)      
+#define IIR_DEST_B1(cr)  (0x400 * cr + 0x310)      
+#define ACC_SRC_C0(cr)   (0x400 * cr + 0x314)      
+#define ACC_SRC_C1(cr)   (0x400 * cr + 0x318)  
+
+#define ACC_SRC_D0(cr)   (0x400 * cr + 0x31C)      
+#define ACC_SRC_D1(cr)   (0x400 * cr + 0x320)      
+#define IIR_SRC_B1(cr)   (0x400 * cr + 0x324)      
+#define IIR_SRC_B0(cr)   (0x400 * cr + 0x328)      
+#define MIX_DEST_A0(cr)  (0x400 * cr + 0x32C)      
+#define MIX_DEST_A1(cr)  (0x400 * cr + 0x330)      
+#define MIX_DEST_B0(cr)  (0x400 * cr + 0x334)      
+#define MIX_DEST_B1(cr)  (0x400 * cr + 0x338)      
+
+#define A_EEA(cr)        (0x400 * cr + 0x33C)                // effects work area end address
+
+#define P_ENDX(cr)       (0x400 * cr + 0x340)                // voice loop end status
+#define P_STAT(cr)       (0x400 * cr + 0x344)                // DMA status register
+#define P_ENDS(cr)       (0x400 * cr + 0x346)                // ?
+
+
+// CORE 0 Specific
+
+
+#define P_MVOLL(cr)      (0x28 * cr + 0x760)                 // master volume (left)
+#define P_MVOLR(cr)      (0x28 * cr + 0x762)                 // master volume (right)
+#define P_EVOLL(cr)      (0x28 * cr + 0x764)                 // effect return volume (left)
+#define P_EVOLR(cr)      (0x28 * cr + 0x766)                 // effect return volume (right)
+#define P_AVOLL(cr)      (0x28 * cr + 0x768)                 // core external input volume (left)
+#define P_AVOLR(cr)      (0x28 * cr + 0x76A)                 // core external input volume (right)
+#define P_BVOLL(cr)      (0x28 * cr + 0x76C)                 // sound data input volume (left)
+#define P_BVOLR(cr)      (0x28 * cr + 0x76E)                 // sound data input volume (right)
+#define P_MVOLXL(cr)     (0x28 * cr + 0x770)                 // current master volume (left)
+#define P_MVOLXR(cr)     (0x28 * cr + 0x772)                 // current master volume (right)
+
+
+// More CORE 0 Reverb
+
+
+#define IIR_ALPHA(cr)    (0x28 * cr + 0x774)      
+#define ACC_COEF_A(cr)   (0x28 * cr + 0x776)      
+#define ACC_COEF_B(cr)   (0x28 * cr + 0x778)      
+#define ACC_COEF_C(cr)   (0x28 * cr + 0x77A)      
+#define ACC_COEF_D(cr)   (0x28 * cr + 0x77C)      
+#define IIR_COEF(cr)     (0x28 * cr + 0x77E)      
+#define FB_ALPHA(cr)     (0x28 * cr + 0x780)      
+#define FB_X(cr)         (0x28 * cr + 0x782)      
+#define IN_COEF_L(cr)    (0x28 * cr + 0x784)      
+#define IN_COEF_R(cr)    (0x28 * cr + 0x786) 
+
+
+// CORE 1 only
+
+#define SPDIF_OUT        0x7C0                                // SPDIF Out: OFF/'PCM'/Bitstream/Bypass 
+#define SPDIF_MODE       0x7C6 
+#define SPDIF_MEDIA      0x7C8                                // SPDIF Media: 'CD'/DVD	
+#define SPDIF_COPY       0x7CA                                // SPDIF Copy Protection
+*/
+
+/* PS1 SPU CORE
+
+*** The below really isn't needed, only if you ***
+*** want to add SPU support to the plugin      ***
+*** which I see no need to add at this time.   ***
+*** individual voice settings                  ***
+
+#define SPU_VP_PITCH(vc) (0xC04 + (vc << 4))                 // voice pitch
+#define SPU_VA_SSA(vc)   (0xC06 + (vc << 4))                 // voice waveform data start address
+#define SPU_VP_ADSR(vc)  (0xC08 + (vc << 4))                 // voice envelope
+#define SPU_VA_SSA(vc)   (0xC0E + (vc << 4))                 // voice waveform data loop address
+
+// common settings
+
+#define SPU_P_MVOLL      0xD80                               // master volume (left)
+#define SPU_P_MVOLR      0xD82                               // master volume (right)
+#define SPU_P_RVOLL      0xD84                               // effect return volume (left)
+#define SPU_P_RVOLR      0xD86                               // effect return volume (right)
+#define SPU_S_KON1       0xD88                               // key on
+#define SPU_S_KON2       0xD8A                               // 
+#define SPU_S_KOFF1      0xD8C                               // key off
+#define SPU_S_KOFF2      0xD8E                               // 
+#define SPU_S_PMON1      0xD90                               // pitch modulation on
+#define SPU_S_PMON2      0xD92                               // 
+#define SPU_S_NON1       0xD94                               // noise generator on
+#define SPU_S_NON2       0xD96                               // 
+#define SPU_S_RVBON1     0xD98                               // effects on
+#define SPU_S_RVBON2     0xD9A                               // 
+#define SPU_S_MUTE1      0xD9C                               // voice mute
+#define SPU_S_MUTE2      0xD9E                               // 
+
+#define SPU_A_ESA        0xDA2                               // effects work area start
+#define SPU_A_IRQA       0xDA4                               // IRQ address
+#define SPU_A_TSA        0xDA6                               // DMA transfer start address
+#define SPU_P_DATA       0xDA8                               // DMA data register
+#define SPU_P_CTRL       0xDAA                               // DMA control register
+#define SPU_P_STAT       0xDAE                               // DMA status register
+
+#define SPU_P_CDL        0xDB0                               // sound data input volume (left)
+#define SPU_P_CDR        0xDB2                               // sound data input volume (right)
+#define SPU_P_EXTL       0xDB4                               // external input volume (left)
+#define SPU_P_EXTR       0xDB6                               // external input volume (right)
+
+#define SPU_P_REVERB     0xDC0                               // effects control
+*/
+
+/*
+#define H_SPUReverbAddr  0x0da2
+#define H_SPUirqAddr     0x0da4
+#define H_SPUaddr        0x0da6
+#define H_SPUdata        0x0da8
+#define H_SPUctrl        0x0daa
+#define H_SPUstat        0x0dae
+#define H_SPUmvolL       0x0d80
+#define H_SPUmvolR       0x0d82
+#define H_SPUrvolL       0x0d84
+#define H_SPUrvolR       0x0d86
+#define H_SPUon1         0x0d88
+#define H_SPUon2         0x0d8a
+#define H_SPUoff1        0x0d8c
+#define H_SPUoff2        0x0d8e
+#define H_FMod1          0x0d90
+#define H_FMod2          0x0d92
+#define H_Noise1         0x0d94
+#define H_Noise2         0x0d96
+#define H_RVBon1         0x0d98
+#define H_RVBon2         0x0d9a
+#define H_SPUMute1       0x0d9c
+#define H_SPUMute2       0x0d9e
+#define H_CDLeft         0x0db0
+#define H_CDRight        0x0db2
+#define H_ExtLeft        0x0db4
+#define H_ExtRight       0x0db6
+#define H_Reverb         0x0dc0
+#define H_SPUPitch0      0x0c04
+#define H_SPUPitch1      0x0c14
+#define H_SPUPitch2      0x0c24
+#define H_SPUPitch3      0x0c34
+#define H_SPUPitch4      0x0c44
+#define H_SPUPitch5      0x0c54
+#define H_SPUPitch6      0x0c64
+#define H_SPUPitch7      0x0c74
+#define H_SPUPitch8      0x0c84
+#define H_SPUPitch9      0x0c94
+#define H_SPUPitch10     0x0ca4
+#define H_SPUPitch11     0x0cb4
+#define H_SPUPitch12     0x0cc4
+#define H_SPUPitch13     0x0cd4
+#define H_SPUPitch14     0x0ce4
+#define H_SPUPitch15     0x0cf4
+#define H_SPUPitch16     0x0d04
+#define H_SPUPitch17     0x0d14
+#define H_SPUPitch18     0x0d24
+#define H_SPUPitch19     0x0d34
+#define H_SPUPitch20     0x0d44
+#define H_SPUPitch21     0x0d54
+#define H_SPUPitch22     0x0d64
+#define H_SPUPitch23     0x0d74
+
+#define H_SPUStartAdr0   0x0c06
+#define H_SPUStartAdr1   0x0c16
+#define H_SPUStartAdr2   0x0c26
+#define H_SPUStartAdr3   0x0c36
+#define H_SPUStartAdr4   0x0c46
+#define H_SPUStartAdr5   0x0c56
+#define H_SPUStartAdr6   0x0c66
+#define H_SPUStartAdr7   0x0c76
+#define H_SPUStartAdr8   0x0c86
+#define H_SPUStartAdr9   0x0c96
+#define H_SPUStartAdr10  0x0ca6
+#define H_SPUStartAdr11  0x0cb6
+#define H_SPUStartAdr12  0x0cc6
+#define H_SPUStartAdr13  0x0cd6
+#define H_SPUStartAdr14  0x0ce6
+#define H_SPUStartAdr15  0x0cf6
+#define H_SPUStartAdr16  0x0d06
+#define H_SPUStartAdr17  0x0d16
+#define H_SPUStartAdr18  0x0d26
+#define H_SPUStartAdr19  0x0d36
+#define H_SPUStartAdr20  0x0d46
+#define H_SPUStartAdr21  0x0d56
+#define H_SPUStartAdr22  0x0d66
+#define H_SPUStartAdr23  0x0d76
+
+#define H_SPULoopAdr0   0x0c0e
+#define H_SPULoopAdr1   0x0c1e
+#define H_SPULoopAdr2   0x0c2e
+#define H_SPULoopAdr3   0x0c3e
+#define H_SPULoopAdr4   0x0c4e
+#define H_SPULoopAdr5   0x0c5e
+#define H_SPULoopAdr6   0x0c6e
+#define H_SPULoopAdr7   0x0c7e
+#define H_SPULoopAdr8   0x0c8e
+#define H_SPULoopAdr9   0x0c9e
+#define H_SPULoopAdr10  0x0cae
+#define H_SPULoopAdr11  0x0cbe
+#define H_SPULoopAdr12  0x0cce
+#define H_SPULoopAdr13  0x0cde
+#define H_SPULoopAdr14  0x0cee
+#define H_SPULoopAdr15  0x0cfe
+#define H_SPULoopAdr16  0x0d0e
+#define H_SPULoopAdr17  0x0d1e
+#define H_SPULoopAdr18  0x0d2e
+#define H_SPULoopAdr19  0x0d3e
+#define H_SPULoopAdr20  0x0d4e
+#define H_SPULoopAdr21  0x0d5e
+#define H_SPULoopAdr22  0x0d6e
+#define H_SPULoopAdr23  0x0d7e
+
+#define H_SPU_ADSRLevel0   0x0c08
+#define H_SPU_ADSRLevel1   0x0c18
+#define H_SPU_ADSRLevel2   0x0c28
+#define H_SPU_ADSRLevel3   0x0c38
+#define H_SPU_ADSRLevel4   0x0c48
+#define H_SPU_ADSRLevel5   0x0c58
+#define H_SPU_ADSRLevel6   0x0c68
+#define H_SPU_ADSRLevel7   0x0c78
+#define H_SPU_ADSRLevel8   0x0c88
+#define H_SPU_ADSRLevel9   0x0c98
+#define H_SPU_ADSRLevel10  0x0ca8
+#define H_SPU_ADSRLevel11  0x0cb8
+#define H_SPU_ADSRLevel12  0x0cc8
+#define H_SPU_ADSRLevel13  0x0cd8
+#define H_SPU_ADSRLevel14  0x0ce8
+#define H_SPU_ADSRLevel15  0x0cf8
+#define H_SPU_ADSRLevel16  0x0d08
+#define H_SPU_ADSRLevel17  0x0d18
+#define H_SPU_ADSRLevel18  0x0d28
+#define H_SPU_ADSRLevel19  0x0d38
+#define H_SPU_ADSRLevel20  0x0d48
+#define H_SPU_ADSRLevel21  0x0d58
+#define H_SPU_ADSRLevel22  0x0d68
+#define H_SPU_ADSRLevel23  0x0d78
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/regs.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,43 @@
+/***************************************************************************
+                           regs.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+
+void SoundOn(int start,int end,unsigned short val);
+void SoundOff(int start,int end,unsigned short val);
+void VolumeOn(int start,int end,unsigned short val,int iRight);
+void FModOn(int start,int end,unsigned short val);
+void NoiseOn(int start,int end,unsigned short val);
+void SetVolumeL(unsigned char ch,short vol);
+void SetVolumeR(unsigned char ch,short vol);
+void SetPitch(int ch,unsigned short val);
+void ReverbOn(int start,int end,unsigned short val,int iRight);
+void SetReverbAddr(int core);
+
+EXPORT_GCC void CALLBACK SPU2write(unsigned long reg, unsigned short val);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/reverb.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,420 @@
+/***************************************************************************
+                          reverb.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed to SPU2 functionality
+//
+// 2003/01/19 - Pete
+// - added Neill's reverb (see at the end of file)
+//
+// 2002/12/26 - Pete
+// - adjusted reverb handling
+//
+// 2002/08/14 - Pete
+// - added extra reverb
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "stdafx.h"
+
+#define _IN_REVERB
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// globals
+////////////////////////////////////////////////////////////////////////
+
+// REVERB info and timing vars...
+
+int *          sRVBPlay[2];
+int *          sRVBEnd[2];
+int *          sRVBStart[2];
+
+////////////////////////////////////////////////////////////////////////
+// START REVERB
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StartREVERB(int ch)
+{
+ int core=ch/24;
+ 
+ if((s_chan[ch].bReverbL || s_chan[ch].bReverbR) && (spuCtrl2[core]&0x80))       // reverb possible?
+  {
+   if(iUseReverb==1) s_chan[ch].bRVBActive=1;
+  }
+ else s_chan[ch].bRVBActive=0;                         // else -> no reverb
+}
+
+////////////////////////////////////////////////////////////////////////
+// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf
+////////////////////////////////////////////////////////////////////////
+
+INLINE void InitREVERB(void)
+{
+ if(iUseReverb==1)
+  {
+   memset(sRVBStart[0],0,NSSIZE*2*4);
+   memset(sRVBStart[1],0,NSSIZE*2*4);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// STORE REVERB
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StoreREVERB(int ch,int ns)
+{
+ int core=ch/24;
+ 
+ if(iUseReverb==0) return;
+ else
+ if(iUseReverb==1) // -------------------------------- // Neil's reverb
+  {
+   const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume*s_chan[ch].bReverbL)/0x4000;
+   const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume*s_chan[ch].bReverbR)/0x4000;
+
+   ns<<=1;
+
+   *(sRVBStart[core]+ns)  +=iRxl;                      // -> we mix all active reverb channels into an extra buffer
+   *(sRVBStart[core]+ns+1)+=iRxr;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE int g_buffer(int iOff,int core)                   // get_buffer content helper: takes care about wraps
+{
+ short * p=(short *)spuMem;
+ iOff=(iOff)+rvb[core].CurrAddr;
+ while(iOff>rvb[core].EndAddr)   iOff=rvb[core].StartAddr+(iOff-(rvb[core].EndAddr+1));
+ while(iOff<rvb[core].StartAddr) iOff=rvb[core].EndAddr-(rvb[core].StartAddr-iOff);
+ return (int)*(p+iOff);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE void s_buffer(int iOff,int iVal,int core)        // set_buffer content helper: takes care about wraps and clipping
+{
+ short * p=(short *)spuMem;
+ iOff=(iOff)+rvb[core].CurrAddr;
+ while(iOff>rvb[core].EndAddr) iOff=rvb[core].StartAddr+(iOff-(rvb[core].EndAddr+1));
+ while(iOff<rvb[core].StartAddr) iOff=rvb[core].EndAddr-(rvb[core].StartAddr-iOff);
+ if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
+ *(p+iOff)=(short)iVal;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE void s_buffer1(int iOff,int iVal,int core)      // set_buffer (+1 sample) content helper: takes care about wraps and clipping
+{
+ short * p=(short *)spuMem;
+ iOff=(iOff)+rvb[core].CurrAddr+1;
+ while(iOff>rvb[core].EndAddr) iOff=rvb[core].StartAddr+(iOff-(rvb[core].EndAddr+1));
+ while(iOff<rvb[core].StartAddr) iOff=rvb[core].EndAddr-(rvb[core].StartAddr-iOff);
+ if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
+ *(p+iOff)=(short)iVal;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE int MixREVERBLeft(int ns,int core)
+{
+ if(iUseReverb==1)
+  {
+   if(!rvb[core].StartAddr || !rvb[core].EndAddr || 
+      rvb[core].StartAddr>=rvb[core].EndAddr)          // reverb is off
+    {
+     rvb[core].iLastRVBLeft=rvb[core].iLastRVBRight=rvb[core].iRVBLeft=rvb[core].iRVBRight=0;
+     return 0;
+    }
+
+   rvb[core].iCnt++;                                    
+
+   if(rvb[core].iCnt&1)                                // we work on every second left value: downsample to 22 khz
+    {
+     if((spuCtrl2[core]&0x80))                         // -> reverb on? oki
+      {
+       int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;
+
+       const int INPUT_SAMPLE_L=*(sRVBStart[core]+(ns<<1));                         
+       const int INPUT_SAMPLE_R=*(sRVBStart[core]+(ns<<1)+1);                     
+
+       const int IIR_INPUT_A0 = (g_buffer(rvb[core].IIR_SRC_A0,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb[core].IN_COEF_L)/32768L;
+       const int IIR_INPUT_A1 = (g_buffer(rvb[core].IIR_SRC_A1,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb[core].IN_COEF_R)/32768L;
+       const int IIR_INPUT_B0 = (g_buffer(rvb[core].IIR_SRC_B0,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb[core].IN_COEF_L)/32768L;
+       const int IIR_INPUT_B1 = (g_buffer(rvb[core].IIR_SRC_B1,core) * rvb[core].IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb[core].IN_COEF_R)/32768L;
+
+       const int IIR_A0 = (IIR_INPUT_A0 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_A0,core) * (32768L - rvb[core].IIR_ALPHA))/32768L;
+       const int IIR_A1 = (IIR_INPUT_A1 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_A1,core) * (32768L - rvb[core].IIR_ALPHA))/32768L;
+       const int IIR_B0 = (IIR_INPUT_B0 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_B0,core) * (32768L - rvb[core].IIR_ALPHA))/32768L;
+       const int IIR_B1 = (IIR_INPUT_B1 * rvb[core].IIR_ALPHA)/32768L + (g_buffer(rvb[core].IIR_DEST_B1,core) * (32768L - rvb[core].IIR_ALPHA))/32768L;
+
+       s_buffer1(rvb[core].IIR_DEST_A0, IIR_A0,core);
+       s_buffer1(rvb[core].IIR_DEST_A1, IIR_A1,core);
+       s_buffer1(rvb[core].IIR_DEST_B0, IIR_B0,core);
+       s_buffer1(rvb[core].IIR_DEST_B1, IIR_B1,core);
+ 
+       ACC0 = (g_buffer(rvb[core].ACC_SRC_A0,core) * rvb[core].ACC_COEF_A)/32768L +
+              (g_buffer(rvb[core].ACC_SRC_B0,core) * rvb[core].ACC_COEF_B)/32768L +
+              (g_buffer(rvb[core].ACC_SRC_C0,core) * rvb[core].ACC_COEF_C)/32768L +
+              (g_buffer(rvb[core].ACC_SRC_D0,core) * rvb[core].ACC_COEF_D)/32768L;
+       ACC1 = (g_buffer(rvb[core].ACC_SRC_A1,core) * rvb[core].ACC_COEF_A)/32768L +
+              (g_buffer(rvb[core].ACC_SRC_B1,core) * rvb[core].ACC_COEF_B)/32768L +
+              (g_buffer(rvb[core].ACC_SRC_C1,core) * rvb[core].ACC_COEF_C)/32768L +
+              (g_buffer(rvb[core].ACC_SRC_D1,core) * rvb[core].ACC_COEF_D)/32768L;
+
+       FB_A0 = g_buffer(rvb[core].MIX_DEST_A0 - rvb[core].FB_SRC_A,core);
+       FB_A1 = g_buffer(rvb[core].MIX_DEST_A1 - rvb[core].FB_SRC_A,core);
+       FB_B0 = g_buffer(rvb[core].MIX_DEST_B0 - rvb[core].FB_SRC_B,core);
+       FB_B1 = g_buffer(rvb[core].MIX_DEST_B1 - rvb[core].FB_SRC_B,core);
+
+       s_buffer(rvb[core].MIX_DEST_A0, ACC0 - (FB_A0 * rvb[core].FB_ALPHA)/32768L,core);
+       s_buffer(rvb[core].MIX_DEST_A1, ACC1 - (FB_A1 * rvb[core].FB_ALPHA)/32768L,core);
+       
+       s_buffer(rvb[core].MIX_DEST_B0, (rvb[core].FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb[core].FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb[core].FB_X)/32768L,core);
+       s_buffer(rvb[core].MIX_DEST_B1, (rvb[core].FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb[core].FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb[core].FB_X)/32768L,core);
+ 
+       rvb[core].iLastRVBLeft  = rvb[core].iRVBLeft;
+       rvb[core].iLastRVBRight = rvb[core].iRVBRight;
+
+       rvb[core].iRVBLeft  = (g_buffer(rvb[core].MIX_DEST_A0,core)+g_buffer(rvb[core].MIX_DEST_B0,core))/3;
+       rvb[core].iRVBRight = (g_buffer(rvb[core].MIX_DEST_A1,core)+g_buffer(rvb[core].MIX_DEST_B1,core))/3;
+
+       rvb[core].iRVBLeft  = (rvb[core].iRVBLeft  * rvb[core].VolLeft)  / 0x4000;
+       rvb[core].iRVBRight = (rvb[core].iRVBRight * rvb[core].VolRight) / 0x4000;
+
+       rvb[core].CurrAddr++;
+       if(rvb[core].CurrAddr>rvb[core].EndAddr) rvb[core].CurrAddr=rvb[core].StartAddr;
+
+       return rvb[core].iLastRVBLeft+(rvb[core].iRVBLeft-rvb[core].iLastRVBLeft)/2;
+      }
+     else                                              // -> reverb off
+      {
+       rvb[core].iLastRVBLeft=rvb[core].iLastRVBRight=rvb[core].iRVBLeft=rvb[core].iRVBRight=0;
+      }
+
+     rvb[core].CurrAddr++;
+     if(rvb[core].CurrAddr>rvb[core].EndAddr) rvb[core].CurrAddr=rvb[core].StartAddr;
+    }
+
+   return rvb[core].iLastRVBLeft;
+  }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+INLINE int MixREVERBRight(int core)
+{
+ if(iUseReverb==1)                                     // Neill's reverb:
+  {
+   int i=rvb[core].iLastRVBRight+(rvb[core].iRVBRight-rvb[core].iLastRVBRight)/2;
+   rvb[core].iLastRVBRight=rvb[core].iRVBRight;
+   return i;                                           // -> just return the last right reverb val (little bit scaled by the previous right val)
+  }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#endif
+
+/*
+-----------------------------------------------------------------------------
+PSX reverb hardware notes
+by Neill Corlett
+-----------------------------------------------------------------------------
+
+Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
+yadda yadda.
+
+-----------------------------------------------------------------------------
+
+Basics
+------
+
+- The reverb buffer is 22khz 16-bit mono PCM.
+- It starts at the reverb address given by 1DA2, extends to
+  the end of sound RAM, and wraps back to the 1DA2 address.
+
+Setting the address at 1DA2 resets the current reverb work address.
+
+This work address ALWAYS increments every 1/22050 sec., regardless of
+whether reverb is enabled (bit 7 of 1DAA set).
+
+And the contents of the reverb buffer ALWAYS play, scaled by the
+"reverberation depth left/right" volumes (1D84/1D86).
+(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)
+
+-----------------------------------------------------------------------------
+
+Register names
+--------------
+
+These are probably not their real names.
+These are probably not even correct names.
+We will use them anyway, because we can.
+
+1DC0: FB_SRC_A       (offset)
+1DC2: FB_SRC_B       (offset)
+1DC4: IIR_ALPHA      (coef.)
+1DC6: ACC_COEF_A     (coef.)
+1DC8: ACC_COEF_B     (coef.)
+1DCA: ACC_COEF_C     (coef.)
+1DCC: ACC_COEF_D     (coef.)
+1DCE: IIR_COEF       (coef.)
+1DD0: FB_ALPHA       (coef.)
+1DD2: FB_X           (coef.)
+1DD4: IIR_DEST_A0    (offset)
+1DD6: IIR_DEST_A1    (offset)
+1DD8: ACC_SRC_A0     (offset)
+1DDA: ACC_SRC_A1     (offset)
+1DDC: ACC_SRC_B0     (offset)
+1DDE: ACC_SRC_B1     (offset)
+1DE0: IIR_SRC_A0     (offset)
+1DE2: IIR_SRC_A1     (offset)
+1DE4: IIR_DEST_B0    (offset)
+1DE6: IIR_DEST_B1    (offset)
+1DE8: ACC_SRC_C0     (offset)
+1DEA: ACC_SRC_C1     (offset)
+1DEC: ACC_SRC_D0     (offset)
+1DEE: ACC_SRC_D1     (offset)
+1DF0: IIR_SRC_B1     (offset)
+1DF2: IIR_SRC_B0     (offset)
+1DF4: MIX_DEST_A0    (offset)
+1DF6: MIX_DEST_A1    (offset)
+1DF8: MIX_DEST_B0    (offset)
+1DFA: MIX_DEST_B1    (offset)
+1DFC: IN_COEF_L      (coef.)
+1DFE: IN_COEF_R      (coef.)
+
+The coefficients are signed fractional values.
+-32768 would be -1.0
+ 32768 would be  1.0 (if it were possible... the highest is of course 32767)
+
+The offsets are (byte/8) offsets into the reverb buffer.
+i.e. you multiply them by 8, you get byte offsets.
+You can also think of them as (samples/4) offsets.
+They appear to be signed.  They can be negative.
+None of the documented presets make them negative, though.
+
+Yes, 1DF0 and 1DF2 appear to be backwards.  Not a typo.
+
+-----------------------------------------------------------------------------
+
+What it does
+------------
+
+We take all reverb sources:
+- regular channels that have the reverb bit on
+- cd and external sources, if their reverb bits are on
+and mix them into one stereo 44100hz signal.
+
+Lowpass/downsample that to 22050hz.  The PSX uses a proper bandlimiting
+algorithm here, but I haven't figured out the hysterically exact specifics.
+I use an 8-tap filter with these coefficients, which are nice but probably
+not the real ones:
+
+0.037828187894
+0.157538631280
+0.321159685278
+0.449322115345
+0.449322115345
+0.321159685278
+0.157538631280
+0.037828187894
+
+So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
+
+* IN MY EMULATION, I divide these by 2 to make it clip less.
+  (and of course the L/R output coefficients are adjusted to compensate)
+  The real thing appears to not do this.
+
+At every 22050hz tick:
+- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb
+  steady-state algorithm described below
+- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer
+  (This part may not be exactly right and I guessed at the coefs. TODO: check later.)
+  L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])
+  R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])
+- Advance the current buffer position by 1 sample
+
+The wet out L and R are then upsampled to 44100hz and played at the
+"reverberation depth left/right" (1D84/1D86) volume, independent of the main
+volume.
+
+-----------------------------------------------------------------------------
+
+Reverb steady-state
+-------------------
+
+The reverb steady-state algorithm is fairly clever, and of course by
+"clever" I mean "batshit insane".
+
+buffer[x] is relative to the current buffer position, not the beginning of
+the buffer.  Note that all buffer offsets must wrap around so they're
+contained within the reverb work area.
+
+Clipping is performed at the end... maybe also sooner, but definitely at
+the end.
+
+IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
+IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
+IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
+IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
+
+IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);
+IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);
+IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);
+IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);
+
+buffer[IIR_DEST_A0 + 1sample] = IIR_A0;
+buffer[IIR_DEST_A1 + 1sample] = IIR_A1;
+buffer[IIR_DEST_B0 + 1sample] = IIR_B0;
+buffer[IIR_DEST_B1 + 1sample] = IIR_B1;
+
+ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +
+       buffer[ACC_SRC_B0] * ACC_COEF_B +
+       buffer[ACC_SRC_C0] * ACC_COEF_C +
+       buffer[ACC_SRC_D0] * ACC_COEF_D;
+ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +
+       buffer[ACC_SRC_B1] * ACC_COEF_B +
+       buffer[ACC_SRC_C1] * ACC_COEF_C +
+       buffer[ACC_SRC_D1] * ACC_COEF_D;
+
+FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];
+FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];
+FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];
+FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];
+
+buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;
+buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;
+buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;
+buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;
+
+-----------------------------------------------------------------------------
+*/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/reverb.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,33 @@
+/***************************************************************************
+                          reverb.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+
+INLINE void StartREVERB(int ch);
+INLINE void StoreREVERB(int ch,int ns);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/spu.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,1026 @@
+/***************************************************************************
+                            spu.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+                       
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+                           
+//*************************************************************************//
+// History of changes:
+//
+// 2005/08/29 - Pete
+// - changed to 48Khz output
+//
+// 2004/12/25 - Pete
+// - inc'd version for pcsx2-0.7
+//
+// 2004/04/18 - Pete
+// - changed all kind of things in the plugin
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2003/04/07 - Eric
+// - adjusted cubic interpolation algorithm
+//
+// 2003/03/16 - Eric
+// - added cubic interpolation
+//
+// 2003/03/01 - linuzappz
+// - libraryName changes using ALSA
+//
+// 2003/02/28 - Pete
+// - added option for type of interpolation
+// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile)
+// - added MONO support for MSWindows DirectSound
+//
+// 2003/02/20 - kode54
+// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault
+//
+// 2003/02/19 - kode54
+// - moved SPU IRQ handler and changed sample flag processing
+//
+// 2003/02/18 - kode54
+// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that
+//   ADSR timing is relative to the frequency at which a sample is played... I guess
+//   this remains to be seen, and I don't know whether ADSR is applied to noise channels...
+//
+// 2003/02/09 - kode54
+// - one-shot samples now process the end block before stopping
+// - in light of removing fmod hack, now processing ADSR on frequency channel as well
+//
+// 2003/02/08 - kode54
+// - replaced easy interpolation with gaussian
+// - removed fmod averaging hack
+// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :)
+//
+// 2003/02/08 - linuzappz
+// - small bugfix for one usleep that was 1 instead of 1000
+// - added iDisStereo for no stereo (Linux)
+//
+// 2003/01/22 - Pete
+// - added easy interpolation & small noise adjustments
+//
+// 2003/01/19 - Pete
+// - added Neill's reverb
+//
+// 2003/01/12 - Pete
+// - added recording window handlers
+//
+// 2003/01/06 - Pete
+// - added Neill's ADSR timings
+//
+// 2002/12/28 - Pete
+// - adjusted spu irq handling, fmod handling and loop handling
+//
+// 2002/08/14 - Pete
+// - added extra reverb
+//
+// 2002/06/08 - linuzappz
+// - SPUupdate changed for SPUasync
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "stdafx.h"
+
+#define _IN_SPU
+
+#include "../peops2/externals.h"
+#include "../peops2/regs.h"
+#include "../peops2/dma.h"
+ 
+////////////////////////////////////////////////////////////////////////
+// globals
+////////////////////////////////////////////////////////////////////////
+
+// psx buffer / addresses
+
+unsigned short  regArea[32*1024];                        
+unsigned short  spuMem[1*1024*1024];
+unsigned char * spuMemC;
+unsigned char * pSpuIrq[2];
+unsigned char * pSpuBuffer;
+
+// user settings          
+
+int             iUseXA=0;
+int             iVolume=3;
+int             iXAPitch=1;
+int             iUseTimer=2;
+int             iSPUIRQWait=1;
+int             iDebugMode=0;
+int             iRecordMode=0;
+int             iUseReverb=1;
+int             iUseInterpolation=2;
+                             
+// MAIN infos struct for each channel
+
+SPUCHAN         s_chan[MAXCHAN+1];                     // channel + 1 infos (1 is security for fmod handling)
+REVERBInfo      rvb[2];
+
+unsigned long   dwNoiseVal=1;                          // global noise generator
+
+unsigned short  spuCtrl2[2];                           // some vars to store psx reg infos
+unsigned short  spuStat2[2];
+unsigned long   spuIrq2[2];             
+unsigned long   spuAddr2[2];                           // address into spu mem
+unsigned long   spuRvbAddr2[2];
+unsigned long   spuRvbAEnd2[2];
+int             bEndThread=0;                          // thread handlers
+int             bThreadEnded=0;
+int             bSpuInit=0;
+int             bSPUIsOpen=0;
+
+unsigned long dwNewChannel2[2];                        // flags for faster testing, if new channel starts
+unsigned long dwEndChannel2[2];
+
+// UNUSED IN PS2 YET
+void (CALLBACK *irqCallback)(void)=0;                  // func of main emu, called on spu irq
+void (CALLBACK *cddavCallback)(unsigned short,unsigned short)=0;
+
+// certain globals (were local before, but with the new timeproc I need em global)
+
+const int f[5][2] = {   {    0,  0  },
+                        {   60,  0  },
+                        {  115, -52 },
+                        {   98, -55 },
+                        {  122, -60 } };
+int SSumR[NSSIZE];
+int SSumL[NSSIZE];
+int iCycle=0;
+short * pS;
+
+static int lastch=-1;      // last channel processed on spu irq in timer mode
+static int lastns=0;       // last ns pos
+static int iSecureStart=0; // secure start counter
+
+extern void psf2_update(unsigned char *samples, long lBytes, void *data);
+
+////////////////////////////////////////////////////////////////////////
+// CODE AREA
+////////////////////////////////////////////////////////////////////////
+
+// dirty inline func includes
+
+#include "reverb.c"        
+#include "adsr.c"
+
+////////////////////////////////////////////////////////////////////////
+// helpers for simple interpolation
+
+//
+// easy interpolation on upsampling, no special filter, just "Pete's common sense" tm
+//
+// instead of having n equal sample values in a row like:
+//       ____
+//           |____
+//
+// we compare the current delta change with the next delta change.
+//
+// if curr_delta is positive,
+//
+//  - and next delta is smaller (or changing direction):
+//         \.
+//          -__
+//
+//  - and next delta significant (at least twice) bigger:
+//         --_
+//            \.
+//
+//  - and next delta is nearly same:
+//          \.
+//           \.
+//
+//
+// if curr_delta is negative,
+//
+//  - and next delta is smaller (or changing direction):
+//          _--
+//         /
+//
+//  - and next delta significant (at least twice) bigger:
+//            /
+//         __- 
+//         
+//  - and next delta is nearly same:
+//           /
+//          /
+//     
+
+
+INLINE void InterpolateUp(int ch)
+{
+ if(s_chan[ch].SB[32]==1)                              // flag == 1? calc step and set flag... and don't change the value in this pass
+  {
+   const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29];  // curr delta to next val
+   const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30];  // and next delta to next-next val :)
+
+   s_chan[ch].SB[32]=0;
+
+   if(id1>0)                                           // curr delta positive
+    {
+     if(id2<id1)
+      {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;}
+     else
+     if(id2<(id1<<1))
+      s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L;
+     else
+      s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; 
+    }
+   else                                                // curr delta negative
+    {
+     if(id2>id1)
+      {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;}
+     else
+     if(id2>(id1<<1))
+      s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L;
+     else
+      s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; 
+    }
+  }
+ else
+ if(s_chan[ch].SB[32]==2)                              // flag 1: calc step and set flag... and don't change the value in this pass
+  {
+   s_chan[ch].SB[32]=0;
+
+   s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L;
+   if(s_chan[ch].sinc<=0x8000)
+        s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1));
+   else s_chan[ch].SB[29]+=s_chan[ch].SB[28];
+  }
+ else                                                  // no flags? add bigger val (if possible), calc smaller step, set flag1
+  s_chan[ch].SB[29]+=s_chan[ch].SB[28];
+}
+
+//
+// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm
+//
+
+INLINE void InterpolateDown(int ch)
+{
+ if(s_chan[ch].sinc>=0x20000L)                                 // we would skip at least one val?
+  {
+   s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight
+   if(s_chan[ch].sinc>=0x30000L)                               // we would skip even more vals?
+    s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// helpers for gauss interpolation
+
+#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos])
+#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3])
+
+#include "gauss_i.h"
+
+////////////////////////////////////////////////////////////////////////
+
+//#include "xa.c"
+
+////////////////////////////////////////////////////////////////////////
+// START SOUND... called by main thread to setup a new sound on a channel
+////////////////////////////////////////////////////////////////////////
+
+INLINE void StartSound(int ch)
+{
+ dwNewChannel2[ch/24]&=~(1<<(ch%24));                  // clear new channel bit
+ dwEndChannel2[ch/24]&=~(1<<(ch%24));                  // clear end channel bit
+
+ StartADSR(ch);
+ StartREVERB(ch);      
+                          
+ s_chan[ch].pCurr=s_chan[ch].pStart;                   // set sample start
+                         
+ s_chan[ch].s_1=0;                                     // init mixing vars
+ s_chan[ch].s_2=0;
+ s_chan[ch].iSBPos=28;
+
+ s_chan[ch].bNew=0;                                    // init channel flags
+ s_chan[ch].bStop=0;                                   
+ s_chan[ch].bOn=1;
+
+ s_chan[ch].SB[29]=0;                                  // init our interpolation helpers
+ s_chan[ch].SB[30]=0;
+
+ if(iUseInterpolation>=2)                              // gauss interpolation?
+      {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;}  // -> start with more decoding
+ else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;}  // -> no/simple interpolation starts with one 44100 decoding
+}
+
+////////////////////////////////////////////////////////////////////////
+// MAIN SPU FUNCTION
+// here is the main job handler... thread, timer or direct func call
+// basically the whole sound processing is done in this fat func!
+////////////////////////////////////////////////////////////////////////
+
+static u32 sampcount;
+static u32 decaybegin;
+static u32 decayend;
+
+static u32 seektime;
+int psf2_seek(u32 t)
+{
+ seektime=t*441/10;
+ if(seektime>sampcount) return(1);
+ return(0);
+}
+
+// Counting to 65536 results in full volume offage.
+void setlength2(s32 stop, s32 fade)
+{
+ seektime = 0;
+ if(stop==~0)
+ {
+  decaybegin=~0;
+ }
+ else
+ {
+  stop=(stop*441)/10;
+  fade=(fade*441)/10;
+
+  decaybegin=stop;
+  decayend=stop+fade;
+ }
+}
+// 5 ms waiting phase, if buffer is full and no new sound has to get started
+// .. can be made smaller (smallest val: 1 ms), but bigger waits give
+// better performance
+
+#define PAUSE_W 5
+#define PAUSE_L 5000
+
+////////////////////////////////////////////////////////////////////////
+
+int iSpuAsyncWait=0;
+
+static void *MAINThread(int samp2run, void *data)
+{
+ int s_1,s_2,fa,voldiv=iVolume;
+ unsigned char * start;unsigned int nSample;
+ int ch,predict_nr,shift_factor,flags,d,d2,s;
+ int gpos,bIRQReturn=0;
+
+// while(!bEndThread)                                    // until we are shutting down
+  {
+   //--------------------------------------------------//
+   // ok, at the beginning we are looking if there is
+   // enuff free place in the dsound/oss buffer to
+   // fill in new data, or if there is a new channel to start.
+   // if not, we wait (thread) or return (timer/spuasync)
+   // until enuff free place is available/a new channel gets
+   // started
+
+   if(dwNewChannel2[0] || dwNewChannel2[1])            // new channel should start immedately?
+    {                                                  // (at least one bit 0 ... MAXCHANNEL is set?)
+     iSecureStart++;                                   // -> set iSecure
+     if(iSecureStart>5) iSecureStart=0;                //    (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance)
+    }
+   else iSecureStart=0;                                // 0: no new channel should start
+
+/*	if (!iSecureStart)
+	{
+	     iSecureStart=0;                                   // reset secure
+	     return;
+	}*/
+
+#if 0
+   while(!iSecureStart && !bEndThread) // &&               // no new start? no thread end?
+//         (SoundGetBytesBuffered()>TESTSIZE))           // and still enuff data in sound buffer?
+    {
+     iSecureStart=0;                                   // reset secure
+
+     if(iUseTimer) return 0;                           // linux no-thread mode? bye
+
+     if(dwNewChannel2[0] || dwNewChannel2[1]) 
+      iSecureStart=1;                                  // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop
+    }
+#endif
+
+   //--------------------------------------------------// continue from irq handling in timer mode? 
+
+   if(lastch>=0)                                       // will be -1 if no continue is pending
+    {
+     ch=lastch; lastch=-1;                  // -> setup all kind of vars to continue
+     goto GOON;                                        // -> directly jump to the continue point
+    }
+
+   //--------------------------------------------------//
+   //- main channel loop                              -// 
+   //--------------------------------------------------//
+    {
+     for(ch=0;ch<MAXCHAN;ch++)                         // loop em all... we will collect 1 ms of sound of each playing channel
+      {
+       if(s_chan[ch].bNew) StartSound(ch);             // start new sound
+       if(!s_chan[ch].bOn) continue;                   // channel not playing? next
+
+       if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq)   // new psx frequency?
+        {
+         s_chan[ch].iUsedFreq=s_chan[ch].iActFreq;     // -> take it and calc steps
+         s_chan[ch].sinc=s_chan[ch].iRawPitch<<4;
+         if(!s_chan[ch].sinc) s_chan[ch].sinc=1;
+         if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag
+        }
+//       ns=0;
+//       while(ns<NSSIZE)                                // loop until 1 ms of data is reached
+        {
+         while(s_chan[ch].spos>=0x10000L)
+          {
+           if(s_chan[ch].iSBPos==28)                   // 28 reached?
+            {
+             start=s_chan[ch].pCurr;                   // set up the current pos
+
+             if (start == (unsigned char*)-1)          // special "stop" sign
+              {
+               s_chan[ch].bOn=0;                       // -> turn everything off
+               s_chan[ch].ADSRX.lVolume=0;
+               s_chan[ch].ADSRX.EnvelopeVol=0;
+               goto ENDX;                              // -> and done for this channel
+              }
+
+             s_chan[ch].iSBPos=0;
+
+             //////////////////////////////////////////// spu irq handler here? mmm... do it later
+
+             s_1=s_chan[ch].s_1;
+             s_2=s_chan[ch].s_2;
+
+             predict_nr=(int)*start;start++;
+             shift_factor=predict_nr&0xf;
+             predict_nr >>= 4;
+             flags=(int)*start;start++;
+
+             // -------------------------------------- // 
+
+             for (nSample=0;nSample<28;start++)      
+              {
+               d=(int)*start;
+               s=((d&0xf)<<12);
+               if(s&0x8000) s|=0xffff0000;
+
+               fa=(s >> shift_factor);
+               fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+               s_2=s_1;s_1=fa;
+               s=((d & 0xf0) << 8);
+
+               s_chan[ch].SB[nSample++]=fa;
+
+               if(s&0x8000) s|=0xffff0000;
+               fa=(s>>shift_factor);              
+               fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
+               s_2=s_1;s_1=fa;
+
+               s_chan[ch].SB[nSample++]=fa;
+              }     
+
+             //////////////////////////////////////////// irq check
+
+             if(spuCtrl2[ch/24]&0x40)                  // some irq active?
+              {
+               if((pSpuIrq[ch/24] >  start-16 &&       // irq address reached?
+                   pSpuIrq[ch/24] <= start) ||
+                  ((flags&1) &&                        // special: irq on looping addr, when stop/loop flag is set 
+                   (pSpuIrq[ch/24] >  s_chan[ch].pLoop-16 &&
+                    pSpuIrq[ch/24] <= s_chan[ch].pLoop)))
+                {
+                 s_chan[ch].iIrqDone=1;                // -> debug flag
+
+                 if(irqCallback) irqCallback();        // -> call main emu (not supported in SPU2 right now)
+                 else
+                  {
+                   if(ch<24) InterruptDMA4();            // -> let's see what is happening if we call our irqs instead ;)
+                   else      InterruptDMA7();
+                  }
+                  
+                 if(iSPUIRQWait)                       // -> option: wait after irq for main emu
+                  {
+                   iSpuAsyncWait=1;
+                   bIRQReturn=1;
+                  }
+                }
+              }
+      
+             //////////////////////////////////////////// flag handler
+
+             if((flags&4) && (!s_chan[ch].bIgnoreLoop))
+              s_chan[ch].pLoop=start-16;               // loop adress
+
+             if(flags&1)                               // 1: stop/loop
+              {
+               dwEndChannel2[ch/24]|=(1<<(ch%24));
+              
+               // We play this block out first...
+               //if(!(flags&2)|| s_chan[ch].pLoop==NULL)   
+                                                       // 1+2: do loop... otherwise: stop
+               if(flags!=3 || s_chan[ch].pLoop==NULL)  // PETE: if we don't check exactly for 3, loop hang ups will happen (DQ4, for example)
+                {                                      // and checking if pLoop is set avoids crashes, yeah
+                 start = (unsigned char*)-1;
+                }
+               else
+                {
+                 start = s_chan[ch].pLoop;
+                }
+              }
+
+             s_chan[ch].pCurr=start;                   // store values for next cycle
+             s_chan[ch].s_1=s_1;
+             s_chan[ch].s_2=s_2;      
+
+             ////////////////////////////////////////////
+
+             if(bIRQReturn)                            // special return for "spu irq - wait for cpu action"
+              {
+               bIRQReturn=0;
+                {
+                 lastch=ch; 
+//                 lastns=ns;	// changemeback
+
+                 return;
+                }
+              }
+
+             ////////////////////////////////////////////
+
+GOON: ;
+
+            }
+
+           fa=s_chan[ch].SB[s_chan[ch].iSBPos++];      // get sample data
+
+//           if((spuCtrl2[ch/24]&0x4000)==0) fa=0;       // muted?
+//           else                                        // else adjust
+            {
+             if(fa>32767L)  fa=32767L;
+             if(fa<-32767L) fa=-32767L;              
+            }
+
+           if(iUseInterpolation>=2)                    // gauss/cubic interpolation
+            {
+             gpos = s_chan[ch].SB[28];
+             gval0 = fa;          
+             gpos = (gpos+1) & 3;
+             s_chan[ch].SB[28] = gpos;
+            }
+           else
+           if(iUseInterpolation==1)                    // simple interpolation
+            {
+             s_chan[ch].SB[28] = 0;                    
+             s_chan[ch].SB[29] = s_chan[ch].SB[30];    // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour'
+             s_chan[ch].SB[30] = s_chan[ch].SB[31];
+             s_chan[ch].SB[31] = fa;
+             s_chan[ch].SB[32] = 1;                    // -> flag: calc new interolation
+            }
+           else s_chan[ch].SB[29]=fa;                  // no interpolation
+
+           s_chan[ch].spos -= 0x10000L;
+          }
+
+         ////////////////////////////////////////////////
+         // noise handler... just produces some noise data
+         // surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used...
+         // and sometimes the noise will be used as fmod modulation... pfff
+
+         if(s_chan[ch].bNoise)
+          {
+           if((dwNoiseVal<<=1)&0x80000000L)
+            {
+             dwNoiseVal^=0x0040001L;
+             fa=((dwNoiseVal>>2)&0x7fff);
+             fa=-fa;
+            }
+           else fa=(dwNoiseVal>>2)&0x7fff;
+
+           // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val
+           fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl2[ch/24]&0x3f00)>>9))+1));
+           if(fa>32767L)  fa=32767L;
+           if(fa<-32767L) fa=-32767L;              
+           s_chan[ch].iOldNoise=fa;
+
+           if(iUseInterpolation<2)                     // no gauss/cubic interpolation?
+            s_chan[ch].SB[29] = fa;                    // -> store noise val in "current sample" slot
+          }                                            //----------------------------------------
+         else                                          // NO NOISE (NORMAL SAMPLE DATA) HERE 
+          {//------------------------------------------//
+           if(iUseInterpolation==3)                    // cubic interpolation
+            {
+             long xd;  
+             xd = ((s_chan[ch].spos) >> 1)+1;
+             gpos = s_chan[ch].SB[28];
+
+             fa  = gval(3) - 3*gval(2) + 3*gval(1) - gval0;
+             fa *= (xd - (2<<15)) / 6;
+             fa >>= 15;
+             fa += gval(2) - gval(1) - gval(1) + gval0;
+             fa *= (xd - (1<<15)) >> 1;
+             fa >>= 15;
+             fa += gval(1) - gval0;
+             fa *= xd;
+             fa >>= 15;
+             fa = fa + gval0;
+            }
+           //------------------------------------------//
+           else
+           if(iUseInterpolation==2)                    // gauss interpolation
+            {
+             int vl, vr;
+             vl = (s_chan[ch].spos >> 6) & ~3;
+             gpos = s_chan[ch].SB[28];
+             vr=(gauss[vl]*gval0)&~2047;
+             vr+=(gauss[vl+1]*gval(1))&~2047;
+             vr+=(gauss[vl+2]*gval(2))&~2047;
+             vr+=(gauss[vl+3]*gval(3))&~2047;
+             fa = vr>>11;
+/*
+             vr=(gauss[vl]*gval0)>>9;
+             vr+=(gauss[vl+1]*gval(1))>>9;
+             vr+=(gauss[vl+2]*gval(2))>>9;
+             vr+=(gauss[vl+3]*gval(3))>>9;
+             fa = vr>>2;
+*/
+            }
+           //------------------------------------------//
+           else
+           if(iUseInterpolation==1)                    // simple interpolation
+            {
+             if(s_chan[ch].sinc<0x10000L)              // -> upsampling?
+                  InterpolateUp(ch);                   // --> interpolate up
+             else InterpolateDown(ch);                 // --> else down
+             fa=s_chan[ch].SB[29];
+            }
+           //------------------------------------------//
+           else fa=s_chan[ch].SB[29];                  // no interpolation
+          }
+
+         s_chan[ch].sval = (MixADSR(ch) * fa) / 1023;  // add adsr
+
+         if(s_chan[ch].bFMod==2)                       // fmod freq channel
+          {
+           int NP=s_chan[ch+1].iRawPitch;
+           double intr;
+
+           NP=((32768L+s_chan[ch].sval)*NP)/32768L;    // mmm... I still need to adjust that to 1/48 khz... we will wait for the first game/demo using it to decide how to do it :)
+
+           if(NP>0x3fff) NP=0x3fff;
+           if(NP<0x1)    NP=0x1;
+           
+	   intr = (double)48000.0f / (double)44100.0f * (double)NP;
+           NP = (UINT32)intr;
+
+           NP=(44100L*NP)/(4096L);                     // calc frequency
+
+           s_chan[ch+1].iActFreq=NP;
+           s_chan[ch+1].iUsedFreq=NP;
+           s_chan[ch+1].sinc=(((NP/10)<<16)/4410);
+           if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1;
+           if(iUseInterpolation==1)                    // freq change in sipmle interpolation mode
+            s_chan[ch+1].SB[32]=1;
+
+// mmmm... set up freq decoding positions?
+//           s_chan[ch+1].iSBPos=28;
+//           s_chan[ch+1].spos=0x10000L;
+          }                    
+         else                   
+          {                                          
+           //////////////////////////////////////////////
+           // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff)
+
+           if(s_chan[ch].iMute) 
+            s_chan[ch].sval=0;                         // debug mute
+           else
+            {
+             if(s_chan[ch].bVolumeL)
+              SSumL[0]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000L;
+             if(s_chan[ch].bVolumeR)
+              SSumR[0]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000L;
+            }
+        
+           //////////////////////////////////////////////
+           // now let us store sound data for reverb    
+                                                          
+           if(s_chan[ch].bRVBActive) StoreREVERB(ch,0);
+          }
+
+         ////////////////////////////////////////////////
+         // ok, go on until 1 ms data of this channel is collected
+                                                       
+         s_chan[ch].spos += s_chan[ch].sinc;             
+                                                              
+        }        
+ENDX:   ;                                                      
+      }
+    }                                                         
+   
+  //---------------------------------------------------//
+  //- here we have another 1 ms of sound data
+  //---------------------------------------------------//
+
+  ///////////////////////////////////////////////////////
+  // mix all channels (including reverb) into one buffer
+
+    SSumL[0]+=MixREVERBLeft(0,0);
+    SSumL[0]+=MixREVERBLeft(0,1);
+    SSumR[0]+=MixREVERBRight(0);
+    SSumR[0]+=MixREVERBRight(1);
+                                              
+    d=SSumL[0]/voldiv;SSumL[0]=0;
+    d2=SSumR[0]/voldiv;SSumR[0]=0;
+
+    if(d<-32767) d=-32767;if(d>32767) d=32767;
+    if(d2<-32767) d2=-32767;if(d2>32767) d2=32767;
+
+    if(sampcount>=decaybegin)
+    {
+	s32 dmul;
+	if(decaybegin!=~0) // Is anyone REALLY going to be playing a song
+		      // for 13 hours?
+    	{
+		if(sampcount>=decayend)
+		{
+			psf2_update(NULL, 0, data);
+		        return(0);
+		}
+
+		dmul=256-(256*(sampcount-decaybegin)/(decayend-decaybegin));
+		d=(d*dmul)>>8;
+		d2=(d2*dmul)>>8;
+	}
+    }
+    sampcount++;
+
+    *pS++=d;
+    *pS++=d2;
+
+    InitREVERB();
+
+  //////////////////////////////////////////////////////                   
+  // feed the sound
+  // wanna have around 1/60 sec (16.666 ms) updates
+	if (seektime != 0 && sampcount < seektime)
+	{
+		pS=(short *)pSpuBuffer;
+	}
+	else if ((((unsigned char *)pS)-((unsigned char *)pSpuBuffer)) == (735*4))
+	{
+	    	psf2_update((u8*)pSpuBuffer,(u8*)pS-(u8*)pSpuBuffer, data);
+	        pS=(short *)pSpuBuffer;					  
+	}
+ }
+
+ // end of big main loop...
+
+ bThreadEnded=1;
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// SPU ASYNC... even newer epsxe func
+//  1 time every 'cycle' cycles... harhar
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC void CALLBACK SPU2async(unsigned long cycle, void *data)
+{
+ if(iSpuAsyncWait)
+  {
+   iSpuAsyncWait++;
+   if(iSpuAsyncWait<=64) return;
+   iSpuAsyncWait=0;
+  }
+
+ MAINThread(0, data);                                      // -> linux high-compat mode
+}
+
+////////////////////////////////////////////////////////////////////////
+// INIT/EXIT STUFF
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// SPUINIT: this func will be called first by the main emu
+////////////////////////////////////////////////////////////////////////
+
+              
+EXPORT_GCC long CALLBACK SPU2init(void)
+{
+ spuMemC=(unsigned char *)spuMem;                      // just small setup
+ memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN));
+ memset(rvb,0,2*sizeof(REVERBInfo));
+
+ sampcount = 0;
+
+ InitADSR();
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SETUPTIMER: init of certain buffers and threads/timers
+////////////////////////////////////////////////////////////////////////
+
+static void SetupTimer(void)
+{
+ memset(SSumR,0,NSSIZE*sizeof(int));                   // init some mixing buffers
+ memset(SSumL,0,NSSIZE*sizeof(int));
+ pS=(short *)pSpuBuffer;                               // setup soundbuffer pointer
+
+ bEndThread=0;                                         // init thread vars
+ bThreadEnded=0; 
+ bSpuInit=1;                                           // flag: we are inited
+}
+
+////////////////////////////////////////////////////////////////////////
+// REMOVETIMER: kill threads/timers
+////////////////////////////////////////////////////////////////////////
+
+static void RemoveTimer(void)
+{
+ bEndThread=1;                                         // raise flag to end thread
+ bThreadEnded=0;                                       // no more spu is running
+ bSpuInit=0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SETUPSTREAMS: init most of the spu buffers
+////////////////////////////////////////////////////////////////////////
+
+static void SetupStreams(void)
+{ 
+ int i;
+
+ pSpuBuffer=(unsigned char *)malloc(32768);            // alloc mixing buffer
+
+ i=NSSIZE*2;
+
+ sRVBStart[0] = (int *)malloc(i*4);                    // alloc reverb buffer
+ memset(sRVBStart[0],0,i*4);
+ sRVBEnd[0]  = sRVBStart[0] + i;
+ sRVBPlay[0] = sRVBStart[0];
+ sRVBStart[1] = (int *)malloc(i*4);                    // alloc reverb buffer
+ memset(sRVBStart[1],0,i*4);
+ sRVBEnd[1]  = sRVBStart[1] + i;
+ sRVBPlay[1] = sRVBStart[1];
+
+ for(i=0;i<MAXCHAN;i++)                                // loop sound channels
+  {
+// we don't use mutex sync... not needed, would only 
+// slow us down:
+//   s_chan[i].hMutex=CreateMutex(NULL,FALSE,NULL);
+   s_chan[i].ADSRX.SustainLevel = 1024;                // -> init sustain
+   s_chan[i].iMute=0;
+   s_chan[i].iIrqDone=0;
+   s_chan[i].pLoop=spuMemC;
+   s_chan[i].pStart=spuMemC;
+   s_chan[i].pCurr=spuMemC;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// REMOVESTREAMS: free most buffer
+////////////////////////////////////////////////////////////////////////
+
+static void RemoveStreams(void)
+{ 
+ free(pSpuBuffer);                                     // free mixing buffer
+ pSpuBuffer=NULL;
+ free(sRVBStart[0]);                                   // free reverb buffer
+ sRVBStart[0]=0;
+ free(sRVBStart[1]);                                   // free reverb buffer
+ sRVBStart[1]=0;
+
+/*
+ int i;
+ for(i=0;i<MAXCHAN;i++)
+  {
+   WaitForSingleObject(s_chan[i].hMutex,2000);
+   ReleaseMutex(s_chan[i].hMutex);
+   if(s_chan[i].hMutex)    
+    {CloseHandle(s_chan[i].hMutex);s_chan[i].hMutex=0;}
+  }
+*/
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// SPUOPEN: called by main emu after init
+////////////////////////////////////////////////////////////////////////
+   
+EXPORT_GCC long CALLBACK SPU2open(void *pDsp)                          
+{
+ if(bSPUIsOpen) return 0;                              // security for some stupid main emus
+
+ iUseXA=0;                                             // just small setup
+ iVolume=3;
+ bEndThread=0;
+ bThreadEnded=0;
+ spuMemC=(unsigned char *)spuMem;      
+ memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN));
+ pSpuIrq[0]=0;
+ pSpuIrq[1]=0;
+ iSPUIRQWait=1;
+ dwNewChannel2[0]=0;
+ dwNewChannel2[1]=0;
+ dwEndChannel2[0]=0;
+ dwEndChannel2[1]=0;
+ spuCtrl2[0]=0;              
+ spuCtrl2[1]=0;              
+ spuStat2[0]=0;
+ spuStat2[1]=0;
+ spuIrq2[0]=0;             
+ spuIrq2[1]=0;             
+ spuAddr2[0]=0xffffffff;   
+ spuAddr2[1]=0xffffffff;   
+ spuRvbAddr2[0]=0;
+ spuRvbAddr2[1]=0;
+ spuRvbAEnd2[0]=0;
+ spuRvbAEnd2[1]=0;
+ 
+// ReadConfig();                                         // read user stuff
+ 
+// SetupSound();                                         // setup midas (before init!)
+
+ SetupStreams();                                       // prepare streaming
+
+ SetupTimer();                                         // timer for feeding data
+
+ bSPUIsOpen=1;
+
+ return 0;        
+}
+
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// SPUCLOSE: called before shutdown
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC void CALLBACK SPU2close(void)
+{
+ if(!bSPUIsOpen) return;                               // some security
+
+ bSPUIsOpen=0;                                         // no more open
+
+ RemoveTimer();                                        // no more feeding
+
+// RemoveSound();                                        // no more sound handling
+
+ RemoveStreams();                                      // no more streaming
+}
+
+////////////////////////////////////////////////////////////////////////
+// SPUSHUTDOWN: called by main emu on final exit
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC void CALLBACK SPU2shutdown(void)
+{
+ return;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SPUTEST: we don't test, we are always fine ;)
+////////////////////////////////////////////////////////////////////////
+
+EXPORT_GCC long CALLBACK SPU2test(void)
+{
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SETUP CALLBACKS
+// this functions will be called once, 
+// passes a callback that should be called on SPU-IRQ/cdda volume change
+////////////////////////////////////////////////////////////////////////
+
+// not used yet
+EXPORT_GCC void CALLBACK SPU2irqCallback(void (CALLBACK *callback)(void))
+{
+ irqCallback = callback;
+}
+
+// not used yet
+EXPORT_GCC void CALLBACK SPU2registerCallback(void (CALLBACK *callback)(void))
+{
+ irqCallback = callback;
+}
+
+// not used yet
+EXPORT_GCC void CALLBACK SPU2registerCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short))
+{
+ cddavCallback = CDDAVcallback;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/spu.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,39 @@
+/***************************************************************************
+                            spu.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2004/04/04 - Pete
+// - changed plugin to emulate PS2 spu
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+
+void SetupTimer(void);
+void RemoveTimer(void);
+EXPORT_GCC void CALLBACK SPU2playADPCMchannel(xa_decode_t *xap);
+
+EXPORT_GCC long CALLBACK SPU2init(void);
+EXPORT_GCC long CALLBACK SPU2open(void *pDsp);
+EXPORT_GCC void CALLBACK SPU2async(unsigned long cycle, void *);
+EXPORT_GCC void CALLBACK SPU2close(void);
+EXPORT_GCC int  CALLBACK psf2_seek(u32 t);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/stdafx.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,41 @@
+/***************************************************************************
+                           StdAfx.h  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#define EXPORT_GCC
+
+#include <stdlib.h>
+#define RRand(range) (random()%range)
+#include <string.h>
+#include <math.h>
+
+#undef CALLBACK
+#define CALLBACK
+#define DWORD unsigned long
+#define LOWORD(l)           ((unsigned short)(l))
+#define HIWORD(l)           ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF))
+
+#include "psemuxa.h"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/peops2/xa.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,363 @@
+/***************************************************************************
+                            xa.c  -  description
+                             -------------------
+    begin                : Wed May 15 2002
+    copyright            : (C) 2002 by Pete Bernert
+    email                : BlackDove@addcom.de
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version. See also the license.txt file for *
+ *   additional informations.                                              *
+ *                                                                         *
+ ***************************************************************************/
+
+//*************************************************************************//
+// History of changes:
+//
+// 2003/02/18 - kode54
+// - added gaussian interpolation
+//
+// 2002/05/15 - Pete
+// - generic cleanup for the Peops release
+//
+//*************************************************************************//
+
+#include "stdafx.h"
+
+#define _IN_XA
+
+// will be included from spu.c
+#ifdef _IN_SPU
+
+////////////////////////////////////////////////////////////////////////
+// XA GLOBALS
+////////////////////////////////////////////////////////////////////////
+
+xa_decode_t   * xapGlobal=0;
+
+unsigned long * XAFeed  = NULL;
+unsigned long * XAPlay  = NULL;
+unsigned long * XAStart = NULL;
+unsigned long * XAEnd   = NULL;
+
+unsigned long   XARepeat  = 0;
+unsigned long   XALastVal = 0;
+
+int             iLeftXAVol  = 32767;
+int             iRightXAVol = 32767;
+
+static int gauss_ptr = 0;
+static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+#define gvall0 gauss_window[gauss_ptr]
+#define gvall(x) gauss_window[(gauss_ptr+x)&3]
+#define gvalr0 gauss_window[4+gauss_ptr]
+#define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)]
+
+////////////////////////////////////////////////////////////////////////
+// MIX XA 
+////////////////////////////////////////////////////////////////////////
+
+INLINE void MixXA(void)
+{
+ int ns;
+
+ for(ns=0;ns<NSSIZE && XAPlay!=XAFeed;ns++)
+  {
+   XALastVal=*XAPlay++;
+   if(XAPlay==XAEnd) XAPlay=XAStart;
+   SSumL[ns]+=(((short)(XALastVal&0xffff))       * iLeftXAVol)/32767;
+   SSumR[ns]+=(((short)((XALastVal>>16)&0xffff)) * iRightXAVol)/32767;
+  }
+
+ if(XAPlay==XAFeed && XARepeat)
+  {
+   XARepeat--;
+   for(;ns<NSSIZE;ns++)
+    {
+     SSumL[ns]+=(((short)(XALastVal&0xffff))       * iLeftXAVol)/32767;
+     SSumR[ns]+=(((short)((XALastVal>>16)&0xffff)) * iRightXAVol)/32767;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////
+// FEED XA 
+////////////////////////////////////////////////////////////////////////
+
+INLINE void FeedXA(xa_decode_t *xap)
+{
+ int sinc,spos,i,iSize,iPlace,vl,vr;
+
+ if(!bSPUIsOpen) return;
+
+ xapGlobal = xap;                                      // store info for save states
+ XARepeat  = 100;                                      // set up repeat
+
+ iSize=((44100*xap->nsamples)/xap->freq);              // get size
+ if(!iSize) return;                                    // none? bye
+
+ if(XAFeed<XAPlay) iPlace=XAPlay-XAFeed;               // how much space in my buf?
+ else              iPlace=(XAEnd-XAFeed) + (XAPlay-XAStart);
+
+ if(iPlace==0) return;                                 // no place at all
+
+ //----------------------------------------------------//
+ if(iXAPitch)                                          // pitch change option?
+  {
+   static DWORD dwLT=0;
+   static DWORD dwFPS=0;
+   static int   iFPSCnt=0;
+   static int   iLastSize=0;
+   static DWORD dwL1=0;
+   DWORD dw=timeGetTime(),dw1,dw2;
+
+   iPlace=iSize;
+
+   dwFPS+=dw-dwLT;iFPSCnt++;
+
+   dwLT=dw;
+                                       
+   if(iFPSCnt>=10)
+    {
+     if(!dwFPS) dwFPS=1;
+     dw1=1000000/dwFPS; 
+     if(dw1>=(dwL1-100) && dw1<=(dwL1+100)) dw1=dwL1;
+     else dwL1=dw1;
+     dw2=(xap->freq*100/xap->nsamples);
+     if((!dw1)||((dw2+100)>=dw1)) iLastSize=0;
+     else
+      {
+       iLastSize=iSize*dw2/dw1;
+       if(iLastSize>iPlace) iLastSize=iPlace;
+       iSize=iLastSize;
+      }
+     iFPSCnt=0;dwFPS=0;
+    }
+   else
+    {
+     if(iLastSize) iSize=iLastSize;
+    }
+  }
+ //----------------------------------------------------//
+
+ spos=0x10000L;
+ sinc = (xap->nsamples << 16) / iSize;                 // calc freq by num / size
+
+ if(xap->stereo)
+  {
+   unsigned long * pS=(unsigned long *)xap->pcm;
+   unsigned long l=0;
+
+   if(iXAPitch)
+    {
+     long l1,l2;short s;
+     for(i=0;i<iSize;i++)
+      {
+       if(iUseInterpolation==2) 
+        {
+         while(spos>=0x10000L)
+          {
+           l = *pS++;
+           gauss_window[gauss_ptr] = (short)LOWORD(l);
+           gauss_window[4+gauss_ptr] = (short)HIWORD(l);
+           gauss_ptr = (gauss_ptr+1) & 3;
+           spos -= 0x10000L;
+          }
+         vl = (spos >> 6) & ~3;
+         vr=(gauss[vl]*gvall0)&~2047;
+         vr+=(gauss[vl+1]*gvall(1))&~2047;
+         vr+=(gauss[vl+2]*gvall(2))&~2047;
+         vr+=(gauss[vl+3]*gvall(3))&~2047;
+         l= (vr >> 11) & 0xffff;
+         vr=(gauss[vl]*gvalr0)&~2047;
+         vr+=(gauss[vl+1]*gvalr(1))&~2047;
+         vr+=(gauss[vl+2]*gvalr(2))&~2047;
+         vr+=(gauss[vl+3]*gvalr(3))&~2047;
+         l |= vr << 5;
+        }
+       else
+        {
+         while(spos>=0x10000L)
+          {
+           l = *pS++;
+           spos -= 0x10000L;
+          }
+        }
+
+       s=(short)LOWORD(l);
+       l1=s;
+       l1=(l1*iPlace)/iSize;
+       if(l1<-32767) l1=-32767;
+       if(l1> 32767) l1=32767;
+       s=(short)HIWORD(l);
+       l2=s;
+       l2=(l2*iPlace)/iSize;
+       if(l2<-32767) l2=-32767;
+       if(l2> 32767) l2=32767;
+       l=(l1&0xffff)|(l2<<16);
+
+       *XAFeed++=l;
+
+       if(XAFeed==XAEnd) XAFeed=XAStart;
+       if(XAFeed==XAPlay) 
+        {
+         if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+         break;
+        }
+
+       spos += sinc;
+      }
+    }
+   else
+    {
+     for(i=0;i<iSize;i++)
+      {
+       if(iUseInterpolation==2) 
+        {
+         while(spos>=0x10000L)
+          {
+           l = *pS++;
+           gauss_window[gauss_ptr] = (short)LOWORD(l);
+           gauss_window[4+gauss_ptr] = (short)HIWORD(l);
+           gauss_ptr = (gauss_ptr+1) & 3;
+           spos -= 0x10000L;
+          }
+         vl = (spos >> 6) & ~3;
+         vr=(gauss[vl]*gvall0)&~2047;
+         vr+=(gauss[vl+1]*gvall(1))&~2047;
+         vr+=(gauss[vl+2]*gvall(2))&~2047;
+         vr+=(gauss[vl+3]*gvall(3))&~2047;
+         l= (vr >> 11) & 0xffff;
+         vr=(gauss[vl]*gvalr0)&~2047;
+         vr+=(gauss[vl+1]*gvalr(1))&~2047;
+         vr+=(gauss[vl+2]*gvalr(2))&~2047;
+         vr+=(gauss[vl+3]*gvalr(3))&~2047;
+         l |= vr << 5;
+        }
+       else
+        {
+         while(spos>=0x10000L)
+          {
+           l = *pS++;
+           spos -= 0x10000L;
+          }
+        }
+
+       *XAFeed++=l;
+
+       if(XAFeed==XAEnd) XAFeed=XAStart;
+       if(XAFeed==XAPlay) 
+        {
+         if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+         break;
+        }
+
+       spos += sinc;
+      }
+    }
+  }
+ else
+  {
+   unsigned short * pS=(unsigned short *)xap->pcm;
+   unsigned long l;short s=0;
+
+   if(iXAPitch)
+    {
+     long l1;
+     for(i=0;i<iSize;i++)
+      {
+       if(iUseInterpolation==2) 
+        {
+         while(spos>=0x10000L)
+          {
+           gauss_window[gauss_ptr] = (short)*pS++;
+           gauss_ptr = (gauss_ptr+1) & 3;
+           spos -= 0x10000L;
+          }
+         vl = (spos >> 6) & ~3;
+         vr=(gauss[vl]*gvall0)&~2047;
+         vr+=(gauss[vl+1]*gvall(1))&~2047;
+         vr+=(gauss[vl+2]*gvall(2))&~2047;
+         vr+=(gauss[vl+3]*gvall(3))&~2047;
+         l1=s= vr >> 11;
+         l1 &= 0xffff;
+        }
+       else
+        {
+         while(spos>=0x10000L)
+          {
+           s = *pS++;
+           spos -= 0x10000L;
+          }
+         l1=s;
+        }
+
+       l1=(l1*iPlace)/iSize;
+       if(l1<-32767) l1=-32767;
+       if(l1> 32767) l1=32767;
+       l=(l1&0xffff)|(l1<<16);
+       *XAFeed++=l;
+
+       if(XAFeed==XAEnd) XAFeed=XAStart;
+       if(XAFeed==XAPlay) 
+        {
+         if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+         break;
+        }
+
+       spos += sinc;
+      }
+    }
+   else
+    {
+     for(i=0;i<iSize;i++)
+      {
+       if(iUseInterpolation==2) 
+        {
+         while(spos>=0x10000L)
+          {
+           gauss_window[gauss_ptr] = (short)*pS++;
+           gauss_ptr = (gauss_ptr+1) & 3;
+           spos -= 0x10000L;
+          }
+         vl = (spos >> 6) & ~3;
+         vr=(gauss[vl]*gvall0)&~2047;
+         vr+=(gauss[vl+1]*gvall(1))&~2047;
+         vr+=(gauss[vl+2]*gvall(2))&~2047;
+         vr+=(gauss[vl+3]*gvall(3))&~2047;
+         l=s= vr >> 11;
+         l &= 0xffff;
+        }
+       else
+        {
+         while(spos>=0x10000L)
+          {
+           s = *pS++;
+           spos -= 0x10000L;
+          }
+         l=s;
+        }
+
+       *XAFeed++=(l|(l<<16));
+
+       if(XAFeed==XAEnd) XAFeed=XAStart;
+       if(XAFeed==XAPlay) 
+        {
+         if(XAPlay!=XAStart) XAFeed=XAPlay-1;
+         break;
+        }
+
+       spos += sinc;
+      }
+    }
+  }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/plugin.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,297 @@
+/*
+	Audio Overload SDK - main driver.  for demonstration only, not user friendly!
+
+	Copyright (c) 2007-2008 R. Belmont and Richard Bannister.
+
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+	* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <audacious/plugin.h>
+
+#include "ao.h"
+#include "corlett.h"
+#include "eng_protos.h"
+
+/* file types */
+static uint32 type;
+
+static struct 
+{ 
+	uint32 sig; 
+	char *name; 
+	int32 (*start)(uint8 *, uint32); 
+	int32 (*stop)(void); 
+	int32 (*command)(int32, int32); 
+	uint32 rate; 
+} types[] = {
+	{ 0x50534602, "Sony PlayStation 2 (.psf2)", psf2_start, psf2_stop, psf2_command, 60 },
+	{ 0xffffffff, "", NULL, NULL, NULL, 0 }
+};
+
+static char *path;
+
+/* ao_get_lib: called to load secondary files */
+int ao_get_lib(char *filename, uint8 **buffer, uint64 *length)
+{
+	guchar *filebuf;
+	gsize size;
+	char buf[PATH_MAX];
+
+	snprintf(buf, PATH_MAX, "%s/%s", dirname(path), filename);
+
+	aud_vfs_file_get_contents(buf, (gchar **) &filebuf, &size);
+
+	*buffer = filebuf;
+	*length = (uint64)size;
+
+	return AO_SUCCESS;
+}
+
+static gint seek = 0;
+
+Tuple *psf2_tuple(gchar *filename)
+{
+	Tuple *t;
+	corlett_t *c;
+	guchar *buf;
+	gsize sz;
+
+	aud_vfs_file_get_contents(filename, (gchar **) &buf, &sz);
+
+	if (!buf)
+		return NULL;
+
+	if (corlett_decode(buf, sz, NULL, NULL, &c) != AO_SUCCESS)
+		return NULL;	
+
+	t = aud_tuple_new_from_filename(filename);
+
+	aud_tuple_associate_int(t, FIELD_LENGTH, NULL, c->inf_length ? psfTimeToMS(c->inf_length) + psfTimeToMS(c->inf_fade) : -1);
+	aud_tuple_associate_string(t, FIELD_ARTIST, NULL, c->inf_artist);
+	aud_tuple_associate_string(t, FIELD_ALBUM, NULL, c->inf_game);
+	aud_tuple_associate_string(t, -1, "game", c->inf_game);
+	aud_tuple_associate_string(t, FIELD_TITLE, NULL, c->inf_title);
+	aud_tuple_associate_string(t, FIELD_COPYRIGHT, NULL, c->inf_copy);
+	aud_tuple_associate_string(t, FIELD_QUALITY, NULL, "sequenced");
+	aud_tuple_associate_string(t, FIELD_CODEC, NULL, "PlayStation2 Audio");
+	aud_tuple_associate_string(t, -1, "console", "PlayStation 2");
+
+	free(c);
+	g_free(buf);
+
+	return t;
+}
+
+gchar *psf2_title(gchar *filename, gint *length)
+{
+	gchar *title = NULL;
+	Tuple *tuple = psf2_tuple(filename);
+
+	if (tuple != NULL)
+	{
+		title = aud_tuple_formatter_make_title_string(tuple, aud_get_gentitle_format());
+		*length = aud_tuple_get_int(tuple, FIELD_LENGTH, NULL);
+		aud_tuple_free(tuple);
+	}
+	else
+	{
+		title = g_path_get_basename(filename);
+		*length = -1;
+	}
+
+	return title;
+}
+
+void psf2_play(InputPlayback *data)
+{
+	guchar *buffer;
+	gsize size;
+	uint32 filesig;
+	gint length;
+	gchar *title = psf2_title(data->filename, &length);
+
+	path = g_strdup(data->filename);
+	aud_vfs_file_get_contents(data->filename, (gchar **) &buffer, &size);
+
+	// now try to identify the file
+	type = 0;
+	filesig = buffer[0]<<24 | buffer[1]<<16 | buffer[2]<<8 | buffer[3];
+	while (types[type].sig != 0xffffffff)
+	{
+		if (filesig == types[type].sig)
+		{
+			break;
+		}
+		else
+		{
+			type++;
+		}
+	}
+
+	// now did we identify it above or just fall through?
+	if (types[type].sig == 0xffffffff)
+	{
+		printf("ERROR: File is unknown, signature bytes are %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3]);
+		free(buffer);
+		return;
+	}
+
+	if (psf2_start(buffer, size) != AO_SUCCESS)
+	{
+		free(buffer);
+		printf("ERROR: Engine rejected file!\n");
+		return;
+	}
+	
+	data->output->open_audio(FMT_S16_NE, 44100, 2);
+
+        data->set_params(data, title, length, 44100*2*2*8, 44100, 2);
+
+	data->playing = TRUE;
+	data->set_pb_ready(data);
+
+	for (;;)
+	{
+		psf2_execute(data);
+
+		if (seek)
+		{
+			data->eof = FALSE;
+			data->output->flush(seek);
+
+			psf2_command(COMMAND_RESTART, 0);
+			psf2_seek(seek);
+
+			seek = 0;
+			continue;
+		}
+
+		psf2_stop();
+
+		data->output->buffer_free();
+		data->output->buffer_free();
+
+		while (data->eof && data->output->buffer_playing())
+			g_usleep(10000);
+
+		data->output->close_audio();
+
+		break;
+	}	
+
+	g_free(buffer);
+	g_free(path);
+        g_free(title);
+
+	data->playing = FALSE;
+}
+
+void psf2_update(unsigned char *buffer, long count, InputPlayback *playback)
+{
+	const int mask = ~((((16 / 8) * 2)) - 1);
+
+	if (buffer == NULL)
+	{
+		playback->playing = FALSE;
+		playback->eof = TRUE;
+
+		return;
+	}
+
+	while (count > 0)
+	{
+		int t = playback->output->buffer_free() & mask;
+		if (t > count)
+			playback->pass_audio(playback, FMT_S16_NE, 2, count, buffer, NULL);
+		else
+		{
+			if (t)
+				playback->pass_audio(playback, FMT_S16_NE, 2, t, buffer, NULL);
+
+			g_usleep((count-t)*1000*5/441/2);
+		}
+		count -= t;
+		buffer += t;
+	}
+
+	if (seek)
+	{
+		if (psf2_seek(seek))
+		{
+			playback->output->flush(seek);
+			seek = 0;
+		}
+		else
+		{
+			playback->eof = TRUE;
+			return;
+		}
+	}
+}
+
+void psf2_Stop(InputPlayback *playback)
+{
+	playback->playing = FALSE;
+}
+
+void psf2_pause(InputPlayback *playback, short p)
+{
+	playback->output->pause(p);
+}
+
+int psf2_is_our_fd(gchar *filename, VFSFile *file)
+{
+	gchar magic[4];
+	aud_vfs_fread(magic, 1, 4, file);
+
+	if (!memcmp(magic, "PSF\x02", 4))
+		return 1;
+
+	return 0;
+}
+
+void psf2_Seek(InputPlayback *playback, int time)
+{
+	seek = time * 1000;
+}
+
+gchar *psf2_fmts[] = { "psf2", "minipsf2", NULL };
+
+InputPlugin psf2_ip =
+{
+	.description = "PSF2 Audio Plugin",
+	.play_file = psf2_play,
+	.stop = psf2_Stop,
+	.pause = psf2_pause,
+	.seek = psf2_Seek,
+	.get_song_tuple = psf2_tuple,
+	.is_our_file_from_vfs = psf2_is_our_fd,
+	.vfs_extensions = psf2_fmts,
+};
+
+InputPlugin *psf2_iplist[] = { &psf2_ip, NULL };
+
+DECLARE_PLUGIN(psf2, NULL, NULL, psf2_iplist, NULL, NULL, NULL, NULL, NULL);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/psx.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,3159 @@
+/*
+ * Sony CXD8530AQ/CXD8530BQ/CXD8530CQ/CXD8661R
+ *
+ * PSX CPU emulator for the MAME project written by smf
+ * Thanks to Farfetch'd for information on the delay slot bug
+ *
+ * The PSX CPU is a custom r3000a with a built in
+ * geometry transform engine, no mmu & no data cache.
+ *
+ * There is a stall circuit for load delays, but
+ * it doesn't work if the load occurs in a branch
+ * delay slot.
+ *
+ */
+
+#include <stdio.h>
+#include "ao.h"
+#include "cpuintrf.h"
+#include "psx.h"
+
+#define EXC_INT ( 0 )
+#define EXC_ADEL ( 4 )
+#define EXC_ADES ( 5 )
+#define EXC_SYS ( 8 )
+#define EXC_BP ( 9 )
+#define EXC_RI ( 10 )
+#define EXC_CPU ( 11 )
+#define EXC_OVF ( 12 )
+
+#define CP0_RANDOM ( 1 )
+#define CP0_BADVADDR ( 8 )
+#define CP0_SR ( 12 )
+#define CP0_CAUSE ( 13 )
+#define CP0_EPC ( 14 )
+#define CP0_PRID ( 15 )
+
+#define SR_IEC ( 1L << 0 )
+#define SR_KUC ( 1L << 1 )
+#define SR_ISC ( 1L << 16 )
+#define SR_SWC ( 1L << 17 )
+#define SR_TS  ( 1L << 21 )
+#define SR_BEV ( 1L << 22 )
+#define SR_RE ( 1L << 25 )
+#define SR_CU0 ( 1L << 28 )
+#define SR_CU1 ( 1L << 29 )
+#define SR_CU2 ( 1L << 30 )
+#define SR_CU3 ( 1L << 31 )
+
+#define CAUSE_EXC ( 31L << 2 )
+#define CAUSE_IP ( 255L << 8 )
+#define CAUSE_IP2 ( 1L << 10 )
+#define CAUSE_IP3 ( 1L << 11 )
+#define CAUSE_IP4 ( 1L << 12 )
+#define CAUSE_IP5 ( 1L << 13 )
+#define CAUSE_IP6 ( 1L << 14 )
+#define CAUSE_IP7 ( 1L << 15 )
+#define CAUSE_CE ( 3L << 28 )
+#define CAUSE_CE0 ( 0L << 28 )
+#define CAUSE_CE1 ( 1L << 28 )
+#define CAUSE_CE2 ( 2L << 28 )
+#define CAUSE_BD ( 1L << 31 )
+
+extern void psx_bios_hle(uint32 pc);
+extern void psx_iop_call(uint32 pc, uint32 callnum);
+extern uint8 program_read_byte_32le(offs_t address);
+extern uint16 program_read_word_32le(offs_t address);
+extern uint32 program_read_dword_32le(offs_t address);
+extern void program_write_byte_32le(offs_t address, uint8 data);
+extern void program_write_word_32le(offs_t address, uint16 data);
+extern void program_write_dword_32le(offs_t address, uint32 data);
+
+static UINT8 mips_reg_layout[] =
+{
+	MIPS_PC, -1,
+	MIPS_DELAYV, MIPS_DELAYR, -1,
+	MIPS_HI, MIPS_LO, -1,
+	-1,
+	MIPS_R0, MIPS_R1, -1,
+	MIPS_R2, MIPS_R3, -1,
+	MIPS_R4, MIPS_R5, -1,
+	MIPS_R6, MIPS_R7, -1,
+	MIPS_R8, MIPS_R9, -1,
+	MIPS_R10, MIPS_R11, -1,
+	MIPS_R12, MIPS_R13, -1,
+	MIPS_R14, MIPS_R15, -1,
+	MIPS_R16, MIPS_R17, -1,
+	MIPS_R18, MIPS_R19, -1,
+	MIPS_R20, MIPS_R21, -1,
+	MIPS_R22, MIPS_R23, -1,
+	MIPS_R24, MIPS_R25, -1,
+	MIPS_R26, MIPS_R27, -1,
+	MIPS_R28, MIPS_R29, -1,
+	MIPS_R30, MIPS_R31, -1,
+	-1,
+	MIPS_CP0R0, MIPS_CP0R1, -1,
+	MIPS_CP0R2, MIPS_CP0R3, -1,
+	MIPS_CP0R4, MIPS_CP0R5, -1,
+	MIPS_CP0R6, MIPS_CP0R7, -1,
+	MIPS_CP0R8, MIPS_CP0R9, -1,
+	MIPS_CP0R10, MIPS_CP0R11, -1,
+	MIPS_CP0R12, MIPS_CP0R13, -1,
+	MIPS_CP0R14, MIPS_CP0R15, -1,
+	MIPS_CP0R16, MIPS_CP0R17, -1,
+	MIPS_CP0R18, MIPS_CP0R19, -1,
+	MIPS_CP0R20, MIPS_CP0R21, -1,
+	MIPS_CP0R22, MIPS_CP0R23, -1,
+	MIPS_CP0R24, MIPS_CP0R25, -1,
+	MIPS_CP0R26, MIPS_CP0R27, -1,
+	MIPS_CP0R28, MIPS_CP0R29, -1,
+	MIPS_CP0R30, MIPS_CP0R31, -1,
+	-1,
+	MIPS_CP2DR0, MIPS_CP2DR1, -1,
+	MIPS_CP2DR2, MIPS_CP2DR3, -1,
+	MIPS_CP2DR4, MIPS_CP2DR5, -1,
+	MIPS_CP2DR6, MIPS_CP2DR7, -1,
+	MIPS_CP2DR8, MIPS_CP2DR9, -1,
+	MIPS_CP2DR10, MIPS_CP2DR11, -1,
+	MIPS_CP2DR12, MIPS_CP2DR13, -1,
+	MIPS_CP2DR14, MIPS_CP2DR15, -1,
+	MIPS_CP2DR16, MIPS_CP2DR17, -1,
+	MIPS_CP2DR18, MIPS_CP2DR19, -1,
+	MIPS_CP2DR20, MIPS_CP2DR21, -1,
+	MIPS_CP2DR22, MIPS_CP2DR23, -1,
+	MIPS_CP2DR24, MIPS_CP2DR25, -1,
+	MIPS_CP2DR26, MIPS_CP2DR27, -1,
+	MIPS_CP2DR28, MIPS_CP2DR29, -1,
+	MIPS_CP2DR30, MIPS_CP2DR31, -1,
+	-1,
+	MIPS_CP2CR0, MIPS_CP2CR1, -1,
+	MIPS_CP2CR2, MIPS_CP2CR3, -1,
+	MIPS_CP2CR4, MIPS_CP2CR5, -1,
+	MIPS_CP2CR6, MIPS_CP2CR7, -1,
+	MIPS_CP2CR8, MIPS_CP2CR9, -1,
+	MIPS_CP2CR10, MIPS_CP2CR11, -1,
+	MIPS_CP2CR12, MIPS_CP2CR13, -1,
+	MIPS_CP2CR14, MIPS_CP2CR15, -1,
+	MIPS_CP2CR16, MIPS_CP2CR17, -1,
+	MIPS_CP2CR18, MIPS_CP2CR19, -1,
+	MIPS_CP2CR20, MIPS_CP2CR21, -1,
+	MIPS_CP2CR22, MIPS_CP2CR23, -1,
+	MIPS_CP2CR24, MIPS_CP2CR25, -1,
+	MIPS_CP2CR26, MIPS_CP2CR27, -1,
+	MIPS_CP2CR28, MIPS_CP2CR29, -1,
+	MIPS_CP2CR30, MIPS_CP2CR31,
+	0
+};
+
+static UINT8 mips_win_layout[] = {
+	45, 0,35,13,	/* register window (top right) */
+	 0, 0,44,13,	/* disassembler window (left, upper) */
+	 0,14,44, 8,	/* memory #1 window (left, middle) */
+	45,14,35, 8,	/* memory #2 window (lower) */
+	 0,23,80, 1 	/* command line window (bottom rows) */
+};
+
+static const char *delayn[] =
+{
+	"pc", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+	"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+	"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+	"t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra",
+	"pc"
+};
+
+#define REGPC ( 32 )
+
+typedef struct
+{
+	UINT32 op;
+	UINT32 pc;
+	UINT32 prevpc;
+	UINT32 delayv;
+	UINT32 delayr;
+	UINT32 hi;
+	UINT32 lo;
+	UINT32 r[ 32 ];
+	UINT32 cp0r[ 32 ];
+	PAIR cp2cr[ 32 ];
+	PAIR cp2dr[ 32 ];
+	int (*irq_callback)(int irqline);
+} mips_cpu_context;
+
+static mips_cpu_context mipscpu;
+
+static int mips_ICount = 0;
+
+static UINT32 mips_mtc0_writemask[]=
+{
+	0xffffffff, /* INDEX */
+	0x00000000, /* RANDOM */
+	0xffffff00, /* ENTRYLO */
+	0x00000000,
+	0xffe00000, /* CONTEXT */
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000, /* BADVADDR */
+	0x00000000,
+	0xffffffc0, /* ENTRYHI */
+	0x00000000,
+	0xf27fff3f, /* SR */
+	0x00000300, /* CAUSE */
+	0x00000000, /* EPC */
+	0x00000000, /* PRID */
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000,
+	0x00000000
+};
+
+#if 0
+void GTELOG(const char *a,...)
+{
+	va_list va;
+	char s_text[ 1024 ];
+	va_start( va, a );
+	vsprintf( s_text, a, va );
+	va_end( va );
+	logerror( "%08x: GTE: %08x %s\n", mipscpu.pc, INS_COFUN( mipscpu.op ), s_text );
+}
+#else
+INLINE void GTELOG(const char *a, ...) {}
+#endif
+
+static UINT32 getcp2dr( int n_reg );
+static void setcp2dr( int n_reg, UINT32 n_value );
+static UINT32 getcp2cr( int n_reg );
+static void setcp2cr( int n_reg, UINT32 n_value );
+static void docop2( int gteop );
+static void mips_exception( int exception );
+
+void mips_stop( void )
+{
+#ifdef MAME_DEBUG
+	extern int debug_key_pressed;
+	debug_key_pressed = 1;
+	CALL_MAME_DEBUG;
+#endif
+}
+
+INLINE void mips_set_cp0r( int reg, UINT32 value )
+{
+	mipscpu.cp0r[ reg ] = value;
+	if( reg == CP0_SR || reg == CP0_CAUSE )
+	{
+		if( ( mipscpu.cp0r[ CP0_SR ] & SR_IEC ) != 0 && ( mipscpu.cp0r[ CP0_SR ] & mipscpu.cp0r[ CP0_CAUSE ] & CAUSE_IP ) != 0 )
+		{
+			mips_exception( EXC_INT );
+		}
+		else if( mipscpu.delayr != REGPC && ( mipscpu.pc & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 3 ) ) != 0 )
+		{
+			mips_exception( EXC_ADEL );
+			mips_set_cp0r( CP0_BADVADDR, mipscpu.pc );
+		}
+	}
+}
+
+INLINE void mips_commit_delayed_load( void )
+{
+	if( mipscpu.delayr != 0 )
+	{
+		mipscpu.r[ mipscpu.delayr ] = mipscpu.delayv;
+		mipscpu.delayr = 0;
+		mipscpu.delayv = 0;
+	}
+}
+
+INLINE void mips_delayed_branch( UINT32 n_adr )
+{
+	if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 3 ) ) != 0 )
+	{
+		mips_exception( EXC_ADEL );
+		mips_set_cp0r( CP0_BADVADDR, n_adr );
+	}
+	else
+	{
+		mips_commit_delayed_load();
+		mipscpu.delayr = REGPC;
+		mipscpu.delayv = n_adr;
+		mipscpu.pc += 4;
+	}
+}
+
+INLINE void mips_set_pc( unsigned val )
+{
+	mipscpu.pc = val;
+	change_pc( val );
+	mipscpu.delayr = 0;
+	mipscpu.delayv = 0;
+}
+
+INLINE void mips_advance_pc( void )
+{
+	if( mipscpu.delayr == REGPC )
+	{
+		mips_set_pc( mipscpu.delayv );
+	}
+	else
+	{
+		mips_commit_delayed_load();
+		mipscpu.pc += 4;
+	}
+}
+
+INLINE void mips_load( UINT32 n_r, UINT32 n_v )
+{
+	mips_advance_pc();
+	if( n_r != 0 )
+	{
+		mipscpu.r[ n_r ] = n_v;
+	}
+}
+
+INLINE void mips_delayed_load( UINT32 n_r, UINT32 n_v )
+{
+	if( mipscpu.delayr == REGPC )
+	{
+		mips_set_pc( mipscpu.delayv );
+		mipscpu.delayr = n_r;
+		mipscpu.delayv = n_v;
+	}
+	else
+	{
+		mips_commit_delayed_load();
+		mipscpu.pc += 4;
+		if( n_r != 0 )
+		{
+			mipscpu.r[ n_r ] = n_v;
+		}
+	}
+}
+
+static void mips_exception( int exception )
+{
+	mips_set_cp0r( CP0_SR, ( mipscpu.cp0r[ CP0_SR ] & ~0x3f ) | ( ( mipscpu.cp0r[ CP0_SR ] << 2 ) & 0x3f ) );
+	if( mipscpu.delayr == REGPC )
+	{
+		mips_set_cp0r( CP0_EPC, mipscpu.pc - 4 );
+		mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~CAUSE_EXC ) | CAUSE_BD | ( exception << 2 ) );
+	}
+	else
+	{
+		mips_commit_delayed_load();
+		mips_set_cp0r( CP0_EPC, mipscpu.pc );
+		mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~( CAUSE_EXC | CAUSE_BD ) ) | ( exception << 2 ) );
+	}
+	if( mipscpu.cp0r[ CP0_SR ] & SR_BEV )
+	{
+		mips_set_pc( 0xbfc00180 );
+	}
+	else
+	{
+		mips_set_pc( 0x80000080 );
+	}
+}
+
+void mips_init( void )
+{
+#if 0
+	int cpu = cpu_getactivecpu();
+
+	state_save_register_UINT32( "psxcpu", cpu, "op", &mipscpu.op, 1 );
+	state_save_register_UINT32( "psxcpu", cpu, "pc", &mipscpu.pc, 1 );
+	state_save_register_UINT32( "psxcpu", cpu, "delayv", &mipscpu.delayv, 1 );
+	state_save_register_UINT32( "psxcpu", cpu, "delayr", &mipscpu.delayr, 1 );
+	state_save_register_UINT32( "psxcpu", cpu, "hi", &mipscpu.hi, 1 );
+	state_save_register_UINT32( "psxcpu", cpu, "lo", &mipscpu.lo, 1 );
+	state_save_register_UINT32( "psxcpu", cpu, "r", &mipscpu.r[ 0 ], 32 );
+	state_save_register_UINT32( "psxcpu", cpu, "cp0r", &mipscpu.cp0r[ 0 ], 32 );
+	state_save_register_UINT32( "psxcpu", cpu, "cp2cr", &mipscpu.cp2cr[ 0 ].d, 32 );
+	state_save_register_UINT32( "psxcpu", cpu, "cp2dr", &mipscpu.cp2dr[ 0 ].d, 32 );
+#endif
+}
+
+void mips_reset( void *param )
+{
+	mips_set_cp0r( CP0_SR, ( mipscpu.cp0r[ CP0_SR ] & ~( SR_TS | SR_SWC | SR_KUC | SR_IEC ) ) | SR_BEV );
+	mips_set_cp0r( CP0_RANDOM, 63 ); /* todo: */
+	mips_set_cp0r( CP0_PRID, 0x00000200 ); /* todo: */
+	mips_set_pc( 0xbfc00000 );
+	mipscpu.prevpc = 0xffffffff;
+}
+
+static void mips_exit( void )
+{
+}
+
+void mips_shorten_frame(void)
+{
+	mips_ICount = 0;
+}
+
+void psx_hw_runcounters(void);
+
+int psxcpu_verbose = 0;
+
+int mips_execute( int cycles )
+{
+	UINT32 n_res;
+
+	mips_ICount = cycles;
+	do
+	{
+//		CALL_MAME_DEBUG;
+
+//		psx_hw_runcounters();
+	
+		mipscpu.op = cpu_readop32( mipscpu.pc );
+
+#if 0
+		while (mipscpu.prevpc == mipscpu.pc)
+		{
+			psx_hw_runcounters();
+			mips_ICount--;
+
+			if (mips_ICount == 0) return cycles;
+		}
+
+		// if we're not in a delay slot, update
+		// if we're in a delay slot and the delay instruction is not NOP, update
+		if (( mipscpu.delayr == 0 ) || ((mipscpu.delayr != 0) && (mipscpu.op != 0)))
+		{
+			mipscpu.prevpc = mipscpu.pc;
+		}	
+#endif
+#if 0
+		if (1) //psxcpu_verbose)
+		{
+			printf("[%08x: %08x] [SP %08x RA %08x V0 %08x V1 %08x A0 %08x S0 %08x S1 %08x]\n", mipscpu.pc, mipscpu.op, mipscpu.r[29], mipscpu.r[31], mipscpu.r[2], mipscpu.r[3], mipscpu.r[4], mipscpu.r[ 16 ], mipscpu.r[ 17 ]);
+//			psxcpu_verbose--;
+		}
+#endif
+		switch( INS_OP( mipscpu.op ) )
+		{
+		case OP_SPECIAL:
+			switch( INS_FUNCT( mipscpu.op ) )
+			{
+			case FUNCT_HLECALL:
+//				printf("HLECALL, PC = %08x\n", mipscpu.pc);
+				psx_bios_hle(mipscpu.pc);
+				break;
+			case FUNCT_SLL:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RT( mipscpu.op ) ] << INS_SHAMT( mipscpu.op ) );
+				break;
+			case FUNCT_SRL:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RT( mipscpu.op ) ] >> INS_SHAMT( mipscpu.op ) );
+				break;
+			case FUNCT_SRA:
+				mips_load( INS_RD( mipscpu.op ), (INT32)mipscpu.r[ INS_RT( mipscpu.op ) ] >> INS_SHAMT( mipscpu.op ) );
+				break;
+			case FUNCT_SLLV:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RT( mipscpu.op ) ] << ( mipscpu.r[ INS_RS( mipscpu.op ) ] & 31 ) );
+				break;
+			case FUNCT_SRLV:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RT( mipscpu.op ) ] >> ( mipscpu.r[ INS_RS( mipscpu.op ) ] & 31 ) );
+				break;
+			case FUNCT_SRAV:
+				mips_load( INS_RD( mipscpu.op ), (INT32)mipscpu.r[ INS_RT( mipscpu.op ) ] >> ( mipscpu.r[ INS_RS( mipscpu.op ) ] & 31 ) );
+				break;
+			case FUNCT_JR:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					mips_delayed_branch( mipscpu.r[ INS_RS( mipscpu.op ) ] );
+				}
+				break;
+			case FUNCT_JALR:
+				n_res = mipscpu.pc + 8;
+				mips_delayed_branch( mipscpu.r[ INS_RS( mipscpu.op ) ] );
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mipscpu.r[ INS_RD( mipscpu.op ) ] = n_res;
+				}
+				break;
+			case FUNCT_SYSCALL:
+				mips_exception( EXC_SYS );
+				break;
+			case FUNCT_BREAK:
+				printf("BREAK!\n");
+				exit(-1);
+//				mips_exception( EXC_BP );
+				mips_advance_pc();
+				break;
+			case FUNCT_MFHI:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.hi );
+				break;
+			case FUNCT_MTHI:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					mips_advance_pc();
+					mipscpu.hi = mipscpu.r[ INS_RS( mipscpu.op ) ];
+				}
+				break;
+			case FUNCT_MFLO:
+				mips_load( INS_RD( mipscpu.op ),  mipscpu.lo );
+				break;
+			case FUNCT_MTLO:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					mips_advance_pc();
+					mipscpu.lo = mipscpu.r[ INS_RS( mipscpu.op ) ];
+				}
+				break;
+			case FUNCT_MULT:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					INT64 n_res64;
+					n_res64 = MUL_64_32_32( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ], (INT32)mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+					mipscpu.lo = LO32_32_64( n_res64 );
+					mipscpu.hi = HI32_32_64( n_res64 );
+				}
+				break;
+			case FUNCT_MULTU:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					UINT64 n_res64;
+					n_res64 = MUL_U64_U32_U32( mipscpu.r[ INS_RS( mipscpu.op ) ], mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+					mipscpu.lo = LO32_U32_U64( n_res64 );
+					mipscpu.hi = HI32_U32_U64( n_res64 );
+				}
+				break;
+			case FUNCT_DIV:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					UINT32 n_div;
+					UINT32 n_mod;
+					if( mipscpu.r[ INS_RT( mipscpu.op ) ] != 0 )
+					{
+						n_div = (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] / (INT32)mipscpu.r[ INS_RT( mipscpu.op ) ];
+						n_mod = (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] % (INT32)mipscpu.r[ INS_RT( mipscpu.op ) ];
+						mips_advance_pc();
+						mipscpu.lo = n_div;
+						mipscpu.hi = n_mod;
+					}
+					else
+					{
+						mips_advance_pc();
+					}
+				}
+				break;
+			case FUNCT_DIVU:
+				if( INS_RD( mipscpu.op ) != 0 )
+				{
+					mips_exception( EXC_RI );
+				}
+				else
+				{
+					UINT32 n_div;
+					UINT32 n_mod;
+					if( mipscpu.r[ INS_RT( mipscpu.op ) ] != 0 )
+					{
+						n_div = mipscpu.r[ INS_RS( mipscpu.op ) ] / mipscpu.r[ INS_RT( mipscpu.op ) ];
+						n_mod = mipscpu.r[ INS_RS( mipscpu.op ) ] % mipscpu.r[ INS_RT( mipscpu.op ) ];
+						mips_advance_pc();
+						mipscpu.lo = n_div;
+						mipscpu.hi = n_mod;
+					}
+					else
+					{
+						mips_advance_pc();
+					}
+				}
+				break;
+			case FUNCT_ADD:
+				{
+					n_res = mipscpu.r[ INS_RS( mipscpu.op ) ] + mipscpu.r[ INS_RT( mipscpu.op ) ];
+					if( (INT32)( ~( mipscpu.r[ INS_RS( mipscpu.op ) ] ^ mipscpu.r[ INS_RT( mipscpu.op ) ] ) & ( mipscpu.r[ INS_RS( mipscpu.op ) ] ^ n_res ) ) < 0 )
+					{
+						mips_exception( EXC_OVF );
+					}
+					else
+					{
+						mips_load( INS_RD( mipscpu.op ), n_res );
+					}
+				}
+				break;
+			case FUNCT_ADDU:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] + mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			case FUNCT_SUB:
+				n_res = mipscpu.r[ INS_RS( mipscpu.op ) ] - mipscpu.r[ INS_RT( mipscpu.op ) ];
+				if( (INT32)( ( mipscpu.r[ INS_RS( mipscpu.op ) ] ^ mipscpu.r[ INS_RT( mipscpu.op ) ] ) & ( mipscpu.r[ INS_RS( mipscpu.op ) ] ^ n_res ) ) < 0 )
+				{
+					mips_exception( EXC_OVF );
+				}
+				else
+				{
+					mips_load( INS_RD( mipscpu.op ), n_res );
+				}
+				break;
+			case FUNCT_SUBU:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] - mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			case FUNCT_AND:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] & mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			case FUNCT_OR:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] | mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			case FUNCT_XOR:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] ^ mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			case FUNCT_NOR:
+				mips_load( INS_RD( mipscpu.op ), ~( mipscpu.r[ INS_RS( mipscpu.op ) ] | mipscpu.r[ INS_RT( mipscpu.op ) ] ) );
+				break;
+			case FUNCT_SLT:
+				mips_load( INS_RD( mipscpu.op ), (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] < (INT32)mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			case FUNCT_SLTU:
+				mips_load( INS_RD( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] < mipscpu.r[ INS_RT( mipscpu.op ) ] );
+				break;
+			default:
+				mips_exception( EXC_RI );
+				break;
+			}
+			break;
+		case OP_REGIMM:
+			switch( INS_RT( mipscpu.op ) )
+			{
+			case RT_BLTZ:
+				if( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] < 0 )
+				{
+					mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+				}
+				else
+				{
+					mips_advance_pc();
+				}
+				break;
+			case RT_BGEZ:
+				if( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] >= 0 )
+				{
+					mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+				}
+				else
+				{
+					mips_advance_pc();
+				}
+				break;
+			case RT_BLTZAL:
+				n_res = mipscpu.pc + 8;
+				if( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] < 0 )
+				{
+					mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+				}
+				else
+				{
+					mips_advance_pc();
+				}
+				mipscpu.r[ 31 ] = n_res;
+				break;
+			case RT_BGEZAL:
+				n_res = mipscpu.pc + 8;
+				if( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] >= 0 )
+				{
+					mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+				}
+				else
+				{
+					mips_advance_pc();
+				}
+				mipscpu.r[ 31 ] = n_res;
+				break;
+			}
+			break;
+		case OP_J:
+			mips_delayed_branch( ( ( mipscpu.pc + 4 ) & 0xf0000000 ) + ( INS_TARGET( mipscpu.op ) << 2 ) );
+			break;
+		case OP_JAL:
+			n_res = mipscpu.pc + 8;
+			mips_delayed_branch( ( ( mipscpu.pc + 4 ) & 0xf0000000 ) + ( INS_TARGET( mipscpu.op ) << 2 ) );
+			mipscpu.r[ 31 ] = n_res;
+			break;
+		case OP_BEQ:
+			if( mipscpu.r[ INS_RS( mipscpu.op ) ] == mipscpu.r[ INS_RT( mipscpu.op ) ] )
+			{
+				mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+			}
+			else
+			{
+				mips_advance_pc();
+			}
+			break;
+		case OP_BNE:
+			if( mipscpu.r[ INS_RS( mipscpu.op ) ] != mipscpu.r[ INS_RT( mipscpu.op ) ] )
+			{
+				mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+			}
+			else
+			{
+				mips_advance_pc();
+			}
+			break;
+		case OP_BLEZ:
+			if( INS_RT( mipscpu.op ) != 0 )
+			{
+				mips_exception( EXC_RI );
+			}
+			else if( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] <= 0 )
+			{
+				mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+			}
+			else
+			{
+				mips_advance_pc();
+			}
+			break;
+		case OP_BGTZ:
+			if( INS_RT( mipscpu.op ) != 0 )
+			{
+				mips_exception( EXC_RI );
+			}
+			else if( (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] > 0 )
+			{
+				mips_delayed_branch( mipscpu.pc + 4 + ( MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) << 2 ) );
+			}
+			else
+			{
+				mips_advance_pc();
+			}
+			break;
+		case OP_ADDI:
+			{
+				UINT32 n_imm;
+				n_imm = MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				n_res = mipscpu.r[ INS_RS( mipscpu.op ) ] + n_imm;
+				if( (INT32)( ~( mipscpu.r[ INS_RS( mipscpu.op ) ] ^ n_imm ) & ( mipscpu.r[ INS_RS( mipscpu.op ) ] ^ n_res ) ) < 0 )
+				{
+					mips_exception( EXC_OVF );
+				}
+				else
+				{
+					mips_load( INS_RT( mipscpu.op ), n_res );
+				}
+			}
+			break;
+		case OP_ADDIU:
+			if (INS_RT( mipscpu.op ) == 0)
+			{
+				psx_iop_call(mipscpu.pc, INS_IMMEDIATE(mipscpu.op));
+				mips_advance_pc();
+			}
+			else
+			{
+				mips_load( INS_RT( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) );
+			}
+			break;
+		case OP_SLTI:
+			mips_load( INS_RT( mipscpu.op ), (INT32)mipscpu.r[ INS_RS( mipscpu.op ) ] < MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) );
+			break;
+		case OP_SLTIU:
+			mips_load( INS_RT( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] < (UINT32)MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) ) );
+			break;
+		case OP_ANDI:
+			mips_load( INS_RT( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] & INS_IMMEDIATE( mipscpu.op ) );
+			break;
+		case OP_ORI:
+			mips_load( INS_RT( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] | INS_IMMEDIATE( mipscpu.op ) );
+			break;
+		case OP_XORI:
+			mips_load( INS_RT( mipscpu.op ), mipscpu.r[ INS_RS( mipscpu.op ) ] ^ INS_IMMEDIATE( mipscpu.op ) );
+			break;
+		case OP_LUI:
+			mips_load( INS_RT( mipscpu.op ), INS_IMMEDIATE( mipscpu.op ) << 16 );
+			break;
+		case OP_COP0:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) != 0 && ( mipscpu.cp0r[ CP0_SR ] & SR_CU0 ) == 0 )
+			{
+				mips_exception( EXC_CPU );
+				mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~CAUSE_CE ) | CAUSE_CE0 );
+			}
+			else
+			{
+				switch( INS_RS( mipscpu.op ) )
+				{
+				case RS_MFC:
+					mips_delayed_load( INS_RT( mipscpu.op ), mipscpu.cp0r[ INS_RD( mipscpu.op ) ] );
+					break;
+				case RS_CFC:
+					/* todo: */
+					logerror( "%08x: COP0 CFC not supported\n", mipscpu.pc );
+					mips_stop();
+					mips_advance_pc();
+					break;
+				case RS_MTC:
+					n_res = ( mipscpu.cp0r[ INS_RD( mipscpu.op ) ] & ~mips_mtc0_writemask[ INS_RD( mipscpu.op ) ] ) |
+						( mipscpu.r[ INS_RT( mipscpu.op ) ] & mips_mtc0_writemask[ INS_RD( mipscpu.op ) ] );
+					mips_advance_pc();
+					mips_set_cp0r( INS_RD( mipscpu.op ), n_res );
+					break;
+				case RS_CTC:
+					/* todo: */
+					logerror( "%08x: COP0 CTC not supported\n", mipscpu.pc );
+					mips_stop();
+					mips_advance_pc();
+					break;
+				case RS_BC:
+					switch( INS_RT( mipscpu.op ) )
+					{
+					case RT_BCF:
+						/* todo: */
+						logerror( "%08x: COP0 BCF not supported\n", mipscpu.pc );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					case RT_BCT:
+						/* todo: */
+						logerror( "%08x: COP0 BCT not supported\n", mipscpu.pc );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					default:
+						/* todo: */
+						logerror( "%08x: COP0 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					}
+					break;
+				default:
+					switch( INS_CO( mipscpu.op ) )
+					{
+					case 1:
+						switch( INS_CF( mipscpu.op ) )
+						{
+						case CF_RFE:
+							mips_advance_pc();
+							mips_set_cp0r( CP0_SR, ( mipscpu.cp0r[ CP0_SR ] & ~0xf ) | ( ( mipscpu.cp0r[ CP0_SR ] >> 2 ) & 0xf ) );
+							break;
+						default:
+							/* todo: */
+							logerror( "%08x: COP0 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+							mips_stop();
+							mips_advance_pc();
+							break;
+						}
+						break;
+					default:
+						/* todo: */
+						logerror( "%08x: COP0 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					}
+					break;
+				}
+			}
+			break;
+		case OP_COP1:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_CU1 ) == 0 )
+			{
+				mips_exception( EXC_CPU );
+				mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~CAUSE_CE ) | CAUSE_CE1 );
+			}
+			else
+			{
+				switch( INS_RS( mipscpu.op ) )
+				{
+				case RS_MFC:
+					/* todo: */
+					logerror( "%08x: COP1 BCT not supported\n", mipscpu.pc );
+					mips_stop();
+					mips_advance_pc();
+					break;
+				case RS_CFC:
+					/* todo: */
+					logerror( "%08x: COP1 CFC not supported\n", mipscpu.pc );
+					mips_stop();
+					mips_advance_pc();
+					break;
+				case RS_MTC:
+					/* todo: */
+					logerror( "%08x: COP1 MTC not supported\n", mipscpu.pc );
+					mips_stop();
+					mips_advance_pc();
+					break;
+				case RS_CTC:
+					/* todo: */
+					logerror( "%08x: COP1 CTC not supported\n", mipscpu.pc );
+					mips_stop();
+					mips_advance_pc();
+					break;
+				case RS_BC:
+					switch( INS_RT( mipscpu.op ) )
+					{
+					case RT_BCF:
+						/* todo: */
+						logerror( "%08x: COP1 BCF not supported\n", mipscpu.pc );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					case RT_BCT:
+						/* todo: */
+						logerror( "%08x: COP1 BCT not supported\n", mipscpu.pc );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					default:
+						/* todo: */
+						logerror( "%08x: COP1 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					}
+					break;
+				default:
+					switch( INS_CO( mipscpu.op ) )
+					{
+					case 1:
+						/* todo: */
+						logerror( "%08x: COP1 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					default:
+						/* todo: */
+						logerror( "%08x: COP1 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					}
+					break;
+				}
+			}
+			break;
+		case OP_COP2:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_CU2 ) == 0 )
+			{
+				mips_exception( EXC_CPU );
+				mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~CAUSE_CE ) | CAUSE_CE2 );
+			}
+			else
+			{
+				switch( INS_RS( mipscpu.op ) )
+				{
+				case RS_MFC:
+					mips_delayed_load( INS_RT( mipscpu.op ), getcp2dr( INS_RD( mipscpu.op ) ) );
+					break;
+				case RS_CFC:
+					mips_delayed_load( INS_RT( mipscpu.op ), getcp2cr( INS_RD( mipscpu.op ) ) );
+					break;
+				case RS_MTC:
+					setcp2dr( INS_RD( mipscpu.op ), mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+					break;
+				case RS_CTC:
+					setcp2cr( INS_RD( mipscpu.op ), mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+					break;
+				case RS_BC:
+					switch( INS_RT( mipscpu.op ) )
+					{
+					case RT_BCF:
+						/* todo: */
+						logerror( "%08x: COP2 BCF not supported\n", mipscpu.pc );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					case RT_BCT:
+						/* todo: */
+						logerror( "%08x: COP2 BCT not supported\n", mipscpu.pc );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					default:
+						/* todo: */
+						logerror( "%08x: COP2 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					}
+					break;
+				default:
+					switch( INS_CO( mipscpu.op ) )
+					{
+					case 1:
+						docop2( INS_COFUN( mipscpu.op ) );
+						mips_advance_pc();
+						break;
+					default:
+						/* todo: */
+						logerror( "%08x: COP2 unknown command %08x\n", mipscpu.pc, mipscpu.op );
+						mips_stop();
+						mips_advance_pc();
+						break;
+					}
+					break;
+				}
+			}
+			break;
+		case OP_LB:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LB SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), MIPS_BYTE_EXTEND( program_read_byte_32le( n_adr ^ 3 ) ) );
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), MIPS_BYTE_EXTEND( program_read_byte_32le( n_adr ) ) );
+				}
+			}
+			break;
+		case OP_LH:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LH SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 1 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), MIPS_WORD_EXTEND( program_read_word_32le( n_adr ^ 2 ) ) );
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 1 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), MIPS_WORD_EXTEND( program_read_word_32le( n_adr ) ) );
+				}
+			}
+			break;
+		case OP_LWL:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LWL SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 0:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0x00ffffff ) | ( (UINT32)program_read_byte_32le( n_adr + 3 ) << 24 );
+						break;
+					case 1:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0x0000ffff ) | ( (UINT32)program_read_word_32le( n_adr + 1 ) << 16 );
+						break;
+					case 2:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0x000000ff ) | ( (UINT32)program_read_byte_32le( n_adr - 1 ) << 8 ) | ( (UINT32)program_read_word_32le( n_adr ) << 16 );
+						break;
+					default:
+						n_res = program_read_dword_32le( n_adr - 3 );
+						break;
+					}
+					mips_delayed_load( INS_RT( mipscpu.op ), n_res );
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 0:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0x00ffffff ) | ( (UINT32)program_read_byte_32le( n_adr ) << 24 );
+						break;
+					case 1:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0x0000ffff ) | ( (UINT32)program_read_word_32le( n_adr - 1 ) << 16 );
+						break;
+					case 2:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0x000000ff ) | ( (UINT32)program_read_word_32le( n_adr - 2 ) << 8 ) | ( (UINT32)program_read_byte_32le( n_adr ) << 24 );
+						break;
+					default:
+						n_res = program_read_dword_32le( n_adr - 3 );
+						break;
+					}
+					mips_delayed_load( INS_RT( mipscpu.op ), n_res );
+				}
+			}
+			break;
+		case OP_LW:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LW SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+#if 0
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 3 ) ) != 0 )
+				{
+					printf("ADEL\n");
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+#endif
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), program_read_dword_32le( n_adr ) );
+				}
+			}
+			break;
+		case OP_LBU:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LBU SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), program_read_byte_32le( n_adr ^ 3 ) );
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), program_read_byte_32le( n_adr ) );
+				}
+			}
+			break;
+		case OP_LHU:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LHU SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 1 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), program_read_word_32le( n_adr ^ 2 ) );
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 1 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					mips_delayed_load( INS_RT( mipscpu.op ), program_read_word_32le( n_adr ) );
+				}
+			}
+			break;
+		case OP_LWR:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LWR SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 3:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0xffffff00 ) | program_read_byte_32le( n_adr - 3 );
+						break;
+					case 2:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0xffff0000 ) | program_read_word_32le( n_adr - 2 );
+						break;
+					case 1:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0xff000000 ) | program_read_word_32le( n_adr - 1 ) | ( (UINT32)program_read_byte_32le( n_adr + 1 ) << 16 );
+						break;
+					default:
+						n_res = program_read_dword_32le( n_adr );
+						break;
+					}
+					mips_delayed_load( INS_RT( mipscpu.op ), n_res );
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 3:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0xffffff00 ) | program_read_byte_32le( n_adr );
+						break;
+					case 2:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0xffff0000 ) | program_read_word_32le( n_adr );
+						break;
+					case 1:
+						n_res = ( mipscpu.r[ INS_RT( mipscpu.op ) ] & 0xff000000 ) | program_read_byte_32le( n_adr ) | ( (UINT32)program_read_word_32le( n_adr + 1 ) << 8 );
+						break;
+					default:
+						n_res = program_read_dword_32le( n_adr );
+						break;
+					}
+					mips_delayed_load( INS_RT( mipscpu.op ), n_res );
+				}
+			}
+			break;
+		case OP_SB:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: SB SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					program_write_byte_32le( n_adr ^ 3, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					program_write_byte_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+				}
+			}
+			break;
+		case OP_SH:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: SH SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 1 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					program_write_word_32le( n_adr ^ 2, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 1 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					program_write_word_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+				}
+			}
+			break;
+		case OP_SWL:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				printf("SR_ISC not supported\n");
+				logerror( "%08x: SWL SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					printf("permission violation?\n");
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 0:
+						program_write_byte_32le( n_adr + 3, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 24 );
+						break;
+					case 1:
+						program_write_word_32le( n_adr + 1, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 16 );
+						break;
+					case 2:
+						program_write_byte_32le( n_adr - 1, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 8 );
+						program_write_word_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 16 );
+						break;
+					case 3:
+						program_write_dword_32le( n_adr - 3, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					}
+					mips_advance_pc();
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					printf("permission violation 2\n");
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 0:
+						program_write_byte_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 24 );
+						break;
+					case 1:
+						program_write_word_32le( n_adr - 1, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 16 );
+						break;
+					case 2:
+						program_write_word_32le( n_adr - 2, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 8 );
+						program_write_byte_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 24 );
+						break;
+					case 3:
+						program_write_dword_32le( n_adr - 3, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					}
+					mips_advance_pc();
+				}
+			}
+			break;
+		case OP_SW:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+/* used by bootstrap
+				logerror( "%08x: SW SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+*/
+				mips_advance_pc();
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if(0) // ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 3 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					program_write_dword_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+					mips_advance_pc();
+				}
+			}
+			break;
+		case OP_SWR:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: SWR SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & ( SR_RE | SR_KUC ) ) == ( SR_RE | SR_KUC ) )
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 0:
+						program_write_dword_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					case 1:
+						program_write_word_32le( n_adr - 1, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						program_write_byte_32le( n_adr + 1, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 16 );
+						break;
+					case 2:
+						program_write_word_32le( n_adr - 2, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					case 3:
+						program_write_byte_32le( n_adr - 3, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					}
+					mips_advance_pc();
+				}
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					switch( n_adr & 3 )
+					{
+					case 0:
+						program_write_dword_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					case 1:
+						program_write_byte_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						program_write_word_32le( n_adr + 1, mipscpu.r[ INS_RT( mipscpu.op ) ] >> 8 );
+						break;
+					case 2:
+						program_write_word_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					case 3:
+						program_write_byte_32le( n_adr, mipscpu.r[ INS_RT( mipscpu.op ) ] );
+						break;
+					}
+					mips_advance_pc();
+				}
+			}
+			break;
+		case OP_LWC1:
+			/* todo: */
+			logerror( "%08x: COP1 LWC not supported\n", mipscpu.pc );
+			mips_stop();
+			mips_advance_pc();
+			break;
+		case OP_LWC2:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_CU2 ) == 0 )
+			{
+				mips_exception( EXC_CPU );
+				mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~CAUSE_CE ) | CAUSE_CE2 );
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: LWC2 SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 3 ) ) != 0 )
+				{
+					mips_exception( EXC_ADEL );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					/* todo: delay? */
+					setcp2dr( INS_RT( mipscpu.op ), program_read_dword_32le( n_adr ) );
+					mips_advance_pc();
+				}
+			}
+			break;
+		case OP_SWC1:
+			/* todo: */
+			logerror( "%08x: COP1 SWC not supported\n", mipscpu.pc );
+			mips_stop();
+			mips_advance_pc();
+			break;
+		case OP_SWC2:
+			if( ( mipscpu.cp0r[ CP0_SR ] & SR_CU2 ) == 0 )
+			{
+				mips_exception( EXC_CPU );
+				mips_set_cp0r( CP0_CAUSE, ( mipscpu.cp0r[ CP0_CAUSE ] & ~CAUSE_CE ) | CAUSE_CE2 );
+			}
+			else if( ( mipscpu.cp0r[ CP0_SR ] & SR_ISC ) != 0 )
+			{
+				/* todo: */
+				logerror( "%08x: SWC2 SR_ISC not supported\n", mipscpu.pc );
+				mips_stop();
+				mips_advance_pc();
+			}
+			else
+			{
+				UINT32 n_adr;
+				n_adr = mipscpu.r[ INS_RS( mipscpu.op ) ] + MIPS_WORD_EXTEND( INS_IMMEDIATE( mipscpu.op ) );
+				if( ( n_adr & ( ( ( mipscpu.cp0r[ CP0_SR ] & SR_KUC ) << 30 ) | 3 ) ) != 0 )
+				{
+					mips_exception( EXC_ADES );
+					mips_set_cp0r( CP0_BADVADDR, n_adr );
+				}
+				else
+				{
+					program_write_dword_32le( n_adr, getcp2dr( INS_RT( mipscpu.op ) ) );
+					mips_advance_pc();
+				}
+			}
+			break;
+		default:
+//			printf( "%08x: unknown opcode %08x (prev %08x, RA %08x)\n", mipscpu.pc, mipscpu.op, mipscpu.prevpc,  mipscpu.r[31] );
+//			mips_stop();
+//			mips_exception( EXC_RI );
+  			break;
+		}
+skipinterp:
+		mips_ICount--;
+	} while( mips_ICount > 0 );
+
+	return cycles - mips_ICount;
+}
+
+static void mips_get_context( void *dst )
+{
+	if( dst )
+	{
+		*(mips_cpu_context *)dst = mipscpu;
+	}
+}
+
+static void mips_set_context( void *src )
+{
+	if( src )
+	{
+		mipscpu = *(mips_cpu_context *)src;
+		change_pc( mipscpu.pc );
+	}
+}
+
+static void set_irq_line( int irqline, int state )
+{
+	UINT32 ip;
+
+	switch( irqline )
+	{
+	case MIPS_IRQ0:
+		ip = CAUSE_IP2;
+		break;
+	case MIPS_IRQ1:
+		ip = CAUSE_IP3;
+		break;
+	case MIPS_IRQ2:
+		ip = CAUSE_IP4;
+		break;
+	case MIPS_IRQ3:
+		ip = CAUSE_IP5;
+		break;
+	case MIPS_IRQ4:
+		ip = CAUSE_IP6;
+		break;
+	case MIPS_IRQ5:
+		ip = CAUSE_IP7;
+		break;
+	default:
+		return;
+	}
+
+	switch( state )
+	{
+	case CLEAR_LINE:
+		mips_set_cp0r( CP0_CAUSE, mipscpu.cp0r[ CP0_CAUSE ] & ~ip );
+		break;
+	case ASSERT_LINE:
+		mips_set_cp0r( CP0_CAUSE, mipscpu.cp0r[ CP0_CAUSE ] |= ip );
+		if( mipscpu.irq_callback )
+		{
+			/* HOLD_LINE interrupts are not supported by the architecture.
+			By acknowledging the interupt here they are treated like PULSE_LINE
+			interrupts, so if the interrupt isn't enabled it will be ignored.
+			There is also a problem with PULSE_LINE interrupts as the interrupt
+			pending bits aren't latched the emulated code won't know what caused
+			the interrupt. */
+			(*mipscpu.irq_callback)( irqline );
+		}
+		break;
+	}
+}
+
+/****************************************************************************
+ * Return a formatted string for a register
+ ****************************************************************************/
+
+offs_t mips_dasm( char *buffer, offs_t pc )
+{
+	offs_t ret;
+	change_pc( pc );
+#ifdef MAME_DEBUG
+	ret = DasmMIPS( buffer, pc );
+#else
+	sprintf( buffer, "$%08x", cpu_readop32( pc ) );
+	ret = 4;
+#endif
+	change_pc( mipscpu.pc );
+	return ret;
+}
+
+/* preliminary gte code */
+
+#define VXY0 ( mipscpu.cp2dr[ 0 ].d )
+#define VX0  ( mipscpu.cp2dr[ 0 ].w.l )
+#define VY0  ( mipscpu.cp2dr[ 0 ].w.h )
+#define VZ0  ( mipscpu.cp2dr[ 1 ].w.l )
+#define VXY1 ( mipscpu.cp2dr[ 2 ].d )
+#define VX1  ( mipscpu.cp2dr[ 2 ].w.l )
+#define VY1  ( mipscpu.cp2dr[ 2 ].w.h )
+#define VZ1  ( mipscpu.cp2dr[ 3 ].w.l )
+#define VXY2 ( mipscpu.cp2dr[ 4 ].d )
+#define VX2  ( mipscpu.cp2dr[ 4 ].w.l )
+#define VY2  ( mipscpu.cp2dr[ 4 ].w.h )
+#define VZ2  ( mipscpu.cp2dr[ 5 ].w.l )
+#define RGB  ( mipscpu.cp2dr[ 6 ].d )
+#define R    ( mipscpu.cp2dr[ 6 ].b.l )
+#define G    ( mipscpu.cp2dr[ 6 ].b.h )
+#define B    ( mipscpu.cp2dr[ 6 ].b.h2 )
+#define CODE ( mipscpu.cp2dr[ 6 ].b.h3 )
+#define OTZ  ( mipscpu.cp2dr[ 7 ].w.l )
+#define IR0  ( mipscpu.cp2dr[ 8 ].d )
+#define IR1  ( mipscpu.cp2dr[ 9 ].d )
+#define IR2  ( mipscpu.cp2dr[ 10 ].d )
+#define IR3  ( mipscpu.cp2dr[ 11 ].d )
+#define SXY0 ( mipscpu.cp2dr[ 12 ].d )
+#define SX0  ( mipscpu.cp2dr[ 12 ].w.l )
+#define SY0  ( mipscpu.cp2dr[ 12 ].w.h )
+#define SXY1 ( mipscpu.cp2dr[ 13 ].d )
+#define SX1  ( mipscpu.cp2dr[ 13 ].w.l )
+#define SY1  ( mipscpu.cp2dr[ 13 ].w.h )
+#define SXY2 ( mipscpu.cp2dr[ 14 ].d )
+#define SX2  ( mipscpu.cp2dr[ 14 ].w.l )
+#define SY2  ( mipscpu.cp2dr[ 14 ].w.h )
+#define SXYP ( mipscpu.cp2dr[ 15 ].d )
+#define SXP  ( mipscpu.cp2dr[ 15 ].w.l )
+#define SYP  ( mipscpu.cp2dr[ 15 ].w.h )
+#define SZ0  ( mipscpu.cp2dr[ 16 ].w.l )
+#define SZ1  ( mipscpu.cp2dr[ 17 ].w.l )
+#define SZ2  ( mipscpu.cp2dr[ 18 ].w.l )
+#define SZ3  ( mipscpu.cp2dr[ 19 ].w.l )
+#define RGB0 ( mipscpu.cp2dr[ 20 ].d )
+#define R0   ( mipscpu.cp2dr[ 20 ].b.l )
+#define G0   ( mipscpu.cp2dr[ 20 ].b.h )
+#define B0   ( mipscpu.cp2dr[ 20 ].b.h2 )
+#define CD0  ( mipscpu.cp2dr[ 20 ].b.h3 )
+#define RGB1 ( mipscpu.cp2dr[ 21 ].d )
+#define R1   ( mipscpu.cp2dr[ 21 ].b.l )
+#define G1   ( mipscpu.cp2dr[ 21 ].b.h )
+#define B1   ( mipscpu.cp2dr[ 21 ].b.h2 )
+#define CD1  ( mipscpu.cp2dr[ 21 ].b.h3 )
+#define RGB2 ( mipscpu.cp2dr[ 22 ].d )
+#define R2   ( mipscpu.cp2dr[ 22 ].b.l )
+#define G2   ( mipscpu.cp2dr[ 22 ].b.h )
+#define B2   ( mipscpu.cp2dr[ 22 ].b.h2 )
+#define CD2  ( mipscpu.cp2dr[ 22 ].b.h3 )
+#define RES1 ( mipscpu.cp2dr[ 23 ].d )
+#define MAC0 ( mipscpu.cp2dr[ 24 ].d )
+#define MAC1 ( mipscpu.cp2dr[ 25 ].d )
+#define MAC2 ( mipscpu.cp2dr[ 26 ].d )
+#define MAC3 ( mipscpu.cp2dr[ 27 ].d )
+#define IRGB ( mipscpu.cp2dr[ 28 ].d )
+#define ORGB ( mipscpu.cp2dr[ 29 ].d )
+#define LZCS ( mipscpu.cp2dr[ 30 ].d )
+#define LZCR ( mipscpu.cp2dr[ 31 ].d )
+
+#define D1  ( mipscpu.cp2cr[ 0 ].d )
+#define R11 ( mipscpu.cp2cr[ 0 ].w.l )
+#define R12 ( mipscpu.cp2cr[ 0 ].w.h )
+#define R13 ( mipscpu.cp2cr[ 1 ].w.l )
+#define R21 ( mipscpu.cp2cr[ 1 ].w.h )
+#define D2  ( mipscpu.cp2cr[ 2 ].d )
+#define R22 ( mipscpu.cp2cr[ 2 ].w.l )
+#define R23 ( mipscpu.cp2cr[ 2 ].w.h )
+#define R31 ( mipscpu.cp2cr[ 3 ].w.l )
+#define R32 ( mipscpu.cp2cr[ 3 ].w.h )
+#define D3  ( mipscpu.cp2cr[ 4 ].d )
+#define R33 ( mipscpu.cp2cr[ 4 ].w.l )
+#define TRX ( mipscpu.cp2cr[ 5 ].d )
+#define TRY ( mipscpu.cp2cr[ 6 ].d )
+#define TRZ ( mipscpu.cp2cr[ 7 ].d )
+#define L11 ( mipscpu.cp2cr[ 8 ].w.l )
+#define L12 ( mipscpu.cp2cr[ 8 ].w.h )
+#define L13 ( mipscpu.cp2cr[ 9 ].w.l )
+#define L21 ( mipscpu.cp2cr[ 9 ].w.h )
+#define L22 ( mipscpu.cp2cr[ 10 ].w.l )
+#define L23 ( mipscpu.cp2cr[ 10 ].w.h )
+#define L31 ( mipscpu.cp2cr[ 11 ].w.l )
+#define L32 ( mipscpu.cp2cr[ 11 ].w.h )
+#define L33 ( mipscpu.cp2cr[ 12 ].w.l )
+#define RBK ( mipscpu.cp2cr[ 13 ].d )
+#define GBK ( mipscpu.cp2cr[ 14 ].d )
+#define BBK ( mipscpu.cp2cr[ 15 ].d )
+#define LR1 ( mipscpu.cp2cr[ 16 ].w.l )
+#define LR2 ( mipscpu.cp2cr[ 16 ].w.h )
+#define LR3 ( mipscpu.cp2cr[ 17 ].w.l )
+#define LG1 ( mipscpu.cp2cr[ 17 ].w.h )
+#define LG2 ( mipscpu.cp2cr[ 18 ].w.l )
+#define LG3 ( mipscpu.cp2cr[ 18 ].w.h )
+#define LB1 ( mipscpu.cp2cr[ 19 ].w.l )
+#define LB2 ( mipscpu.cp2cr[ 19 ].w.h )
+#define LB3 ( mipscpu.cp2cr[ 20 ].w.l )
+#define RFC ( mipscpu.cp2cr[ 21 ].d )
+#define GFC ( mipscpu.cp2cr[ 22 ].d )
+#define BFC ( mipscpu.cp2cr[ 23 ].d )
+#define OFX ( mipscpu.cp2cr[ 24 ].d )
+#define OFY ( mipscpu.cp2cr[ 25 ].d )
+#define H   ( mipscpu.cp2cr[ 26 ].w.l )
+#define DQA ( mipscpu.cp2cr[ 27 ].w.l )
+#define DQB ( mipscpu.cp2cr[ 28 ].d )
+#define ZSF3 ( mipscpu.cp2cr[ 29 ].w.l )
+#define ZSF4 ( mipscpu.cp2cr[ 30 ].w.l )
+#define FLAG ( mipscpu.cp2cr[ 31 ].d )
+
+static UINT32 getcp2dr( int n_reg )
+{
+	if( n_reg == 1 || n_reg == 3 || n_reg == 5 || n_reg == 8 || n_reg == 9 || n_reg == 10 || n_reg == 11 )
+	{
+		mipscpu.cp2dr[ n_reg ].d = (INT32)(INT16)mipscpu.cp2dr[ n_reg ].d;
+	}
+	else if( n_reg == 17 || n_reg == 18 || n_reg == 19 )
+	{
+		mipscpu.cp2dr[ n_reg ].d = (UINT32)(UINT16)mipscpu.cp2dr[ n_reg ].d;
+	}
+	else if( n_reg == 29 )
+	{
+		ORGB = ( ( IR1 >> 7 ) & 0x1f ) | ( ( IR2 >> 2 ) & 0x3e0 ) | ( ( IR3 << 3 ) & 0x7c00 );
+	}
+	GTELOG( "get CP2DR%u=%08x", n_reg, mipscpu.cp2dr[ n_reg ].d );
+	return mipscpu.cp2dr[ n_reg ].d;
+}
+
+static void setcp2dr( int n_reg, UINT32 n_value )
+{
+	GTELOG( "set CP2DR%u=%08x", n_reg, n_value );
+	mipscpu.cp2dr[ n_reg ].d = n_value;
+
+	if( n_reg == 15 )
+	{
+		SXY0 = SXY1;
+		SXY1 = SXY2;
+		SXY2 = SXYP;
+	}
+	else if( n_reg == 28 )
+	{
+		IR1 = ( IRGB & 0x1f ) << 4;
+		IR2 = ( IRGB & 0x3e0 ) >> 1;
+		IR3 = ( IRGB & 0x7c00 ) >> 6;
+	}
+	else if( n_reg == 30 )
+	{
+		UINT32 n_lzcs = LZCS;
+		UINT32 n_lzcr = 0;
+
+		if( ( n_lzcs & 0x80000000 ) == 0 )
+		{
+			n_lzcs = ~n_lzcs;
+		}
+		while( ( n_lzcs & 0x80000000 ) != 0 )
+		{
+			n_lzcr++;
+			n_lzcs <<= 1;
+		}
+		LZCR = n_lzcr;
+	}
+}
+
+static UINT32 getcp2cr( int n_reg )
+{
+	GTELOG( "get CP2CR%u=%08x", n_reg, mipscpu.cp2cr[ n_reg ].d );
+	return mipscpu.cp2cr[ n_reg ].d;
+}
+
+static void setcp2cr( int n_reg, UINT32 n_value )
+{
+	GTELOG( "set CP2CR%u=%08x", n_reg, n_value );
+	mipscpu.cp2cr[ n_reg ].d = n_value;
+}
+
+INLINE INT32 LIM( INT32 n_value, INT32 n_max, INT32 n_min, UINT32 n_flag )
+{
+	if( n_value > n_max )
+	{
+		FLAG |= n_flag;
+		return n_max;
+	}
+	else if( n_value < n_min )
+	{
+		FLAG |= n_flag;
+		return n_min;
+	}
+	return n_value;
+}
+
+INLINE INT64 BOUNDS( INT64 n_value, INT64 n_max, int n_maxflag, INT64 n_min, int n_minflag )
+{
+	if( n_value > n_max )
+	{
+		FLAG |= n_maxflag;
+	}
+	else if( n_value < n_min )
+	{
+		FLAG |= n_minflag;
+	}
+	return n_value;
+}
+
+#define A1( a ) BOUNDS( ( a ), 0x7fffffff, 30, -(INT64)0x80000000, ( 1 << 27 ) )
+#define A2( a ) BOUNDS( ( a ), 0x7fffffff, 29, -(INT64)0x80000000, ( 1 << 26 ) )
+#define A3( a ) BOUNDS( ( a ), 0x7fffffff, 28, -(INT64)0x80000000, ( 1 << 25 ) )
+#define Lm_B1( a, l ) LIM( ( a ), 0x7fff, -0x8000 * !l, ( 1 << 31 ) | ( 1 << 24 ) )
+#define Lm_B2( a, l ) LIM( ( a ), 0x7fff, -0x8000 * !l, ( 1 << 31 ) | ( 1 << 23 ) )
+#define Lm_B3( a, l ) LIM( ( a ), 0x7fff, -0x8000 * !l, ( 1 << 22 ) )
+#define Lm_C1( a ) LIM( ( a ), 0x00ff, 0x0000, ( 1 << 21 ) )
+#define Lm_C2( a ) LIM( ( a ), 0x00ff, 0x0000, ( 1 << 20 ) )
+#define Lm_C3( a ) LIM( ( a ), 0x00ff, 0x0000, ( 1 << 19 ) )
+#define Lm_D( a ) LIM( ( a ), 0xffff, 0x0000, ( 1 << 31 ) | ( 1 << 18 ) )
+
+INLINE UINT32 Lm_E( UINT32 n_z )
+{
+	if( n_z <= H / 2 )
+	{
+		n_z = H / 2;
+		FLAG |= ( 1 << 31 ) | ( 1 << 17 );
+	}
+	if( n_z == 0 )
+	{
+		n_z = 1;
+	}
+	return n_z;
+}
+
+#define F( a ) BOUNDS( ( a ), 0x7fffffff, ( 1 << 31 ) | ( 1 << 16 ), -(INT64)0x80000000, ( 1 << 31 ) | ( 1 << 15 ) )
+#define Lm_G1( a ) LIM( ( a ), 0x3ff, -0x400, ( 1 << 31 ) | ( 1 << 14 ) )
+#define Lm_G2( a ) LIM( ( a ), 0x3ff, -0x400, ( 1 << 31 ) | ( 1 << 13 ) )
+#define Lm_H( a ) LIM( ( a ), 0xfff, 0x000, ( 1 << 12 ) )
+
+static void docop2( int gteop )
+{
+	int n_sf;
+	int n_v;
+	int n_lm;
+	int n_pass;
+	UINT16 n_v1;
+	UINT16 n_v2;
+	UINT16 n_v3;
+	const UINT16 **p_n_mx;
+	const UINT32 **p_n_cv;
+	static const UINT16 n_zm = 0;
+	static const UINT32 n_zc = 0;
+	static const UINT16 *p_n_vx[] = { &VX0, &VX1, &VX2 };
+	static const UINT16 *p_n_vy[] = { &VY0, &VY1, &VY2 };
+	static const UINT16 *p_n_vz[] = { &VZ0, &VZ1, &VZ2 };
+	static const UINT16 *p_n_rm[] = { &R11, &R12, &R13, &R21, &R22, &R23, &R31, &R32, &R33 };
+	static const UINT16 *p_n_lm[] = { &L11, &L12, &L13, &L21, &L22, &L23, &L31, &L32, &L33 };
+	static const UINT16 *p_n_cm[] = { &LR1, &LR2, &LR3, &LG1, &LG2, &LG3, &LB1, &LB2, &LB3 };
+	static const UINT16 *p_n_zm[] = { &n_zm, &n_zm, &n_zm, &n_zm, &n_zm, &n_zm, &n_zm, &n_zm, &n_zm };
+	static const UINT16 **p_p_n_mx[] = { p_n_rm, p_n_lm, p_n_cm, p_n_zm };
+	static const UINT32 *p_n_tr[] = { &TRX, &TRY, &TRZ };
+	static const UINT32 *p_n_bk[] = { &RBK, &GBK, &BBK };
+	static const UINT32 *p_n_fc[] = { &RFC, &GFC, &BFC };
+	static const UINT32 *p_n_zc[] = { &n_zc, &n_zc, &n_zc };
+	static const UINT32 **p_p_n_cv[] = { p_n_tr, p_n_bk, p_n_fc, p_n_zc };
+
+	switch( GTE_FUNCT( gteop ) )
+	{
+	case 0x01:
+		if( gteop == 0x0180001 )
+		{
+			GTELOG( "RTPS" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT32)TRX << 12 ) + ( (INT16)R11 * (INT16)VX0 ) + ( (INT16)R12 * (INT16)VY0 ) + ( (INT16)R13 * (INT16)VZ0 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)(INT32)TRY << 12 ) + ( (INT16)R21 * (INT16)VX0 ) + ( (INT16)R22 * (INT16)VY0 ) + ( (INT16)R23 * (INT16)VZ0 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)(INT32)TRZ << 12 ) + ( (INT16)R31 * (INT16)VX0 ) + ( (INT16)R32 * (INT16)VY0 ) + ( (INT16)R33 * (INT16)VZ0 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 0 );
+			IR2 = Lm_B2( (INT32)MAC2, 0 );
+			IR3 = Lm_B3( (INT32)MAC3, 0 );
+			SZ0 = SZ1;
+			SZ1 = SZ2;
+			SZ2 = SZ3;
+			SZ3 = Lm_D( (INT32)MAC3 );
+			SXY0 = SXY1;
+			SXY1 = SXY2;
+			SX2 = Lm_G1( F( (INT64)(INT32)OFX + ( (INT64)(INT16)IR1 * ( ( (UINT32)H << 16 ) / Lm_E( SZ3 ) ) ) ) >> 16 );
+			SY2 = Lm_G2( F( (INT64)(INT32)OFY + ( (INT64)(INT16)IR2 * ( ( (UINT32)H << 16 ) / Lm_E( SZ3 ) ) ) ) >> 16 );
+			MAC0 = F( (INT64)(INT32)DQB + ( (INT64)(INT16)DQA * ( ( (UINT32)H << 16 ) / Lm_E( SZ3 ) ) ) );
+			IR0 = Lm_H( (INT32)MAC0 >> 12 );
+			return;
+		}
+		break;
+	case 0x06:
+		if( gteop == 0x0400006 ||
+			gteop == 0x1400006 ||
+			gteop == 0x0155cc6 )
+		{
+			GTELOG( "NCLIP" );
+			FLAG = 0;
+
+			MAC0 = F( ( (INT64)(INT16)SX0 * (INT16)SY1 ) + ( (INT16)SX1 * (INT16)SY2 ) + ( (INT16)SX2 * (INT16)SY0 ) - ( (INT16)SX0 * (INT16)SY2 ) - ( (INT16)SX1 * (INT16)SY0 ) - ( (INT16)SX2 * (INT16)SY1 ) );
+			return;
+		}
+		break;
+	case 0x0c:
+		if( GTE_OP( gteop ) == 0x17 )
+		{
+			GTELOG( "OP" );
+			n_sf = 12 * GTE_SF( gteop );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT32)D2 * (INT16)IR3 ) - ( (INT64)(INT32)D3 * (INT16)IR2 ) ) >> n_sf );
+			MAC2 = A2( ( ( (INT64)(INT32)D3 * (INT16)IR1 ) - ( (INT64)(INT32)D1 * (INT16)IR3 ) ) >> n_sf );
+			MAC3 = A3( ( ( (INT64)(INT32)D1 * (INT16)IR2 ) - ( (INT64)(INT32)D2 * (INT16)IR1 ) ) >> n_sf );
+			IR1 = Lm_B1( (INT32)MAC1, 0 );
+			IR2 = Lm_B2( (INT32)MAC2, 0 );
+			IR3 = Lm_B3( (INT32)MAC3, 0 );
+			return;
+		}
+		break;
+	case 0x10:
+		if( gteop == 0x0780010 )
+		{
+			GTELOG( "DPCS" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)R << 16 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)RFC - ( R << 4 ), 0 ) ) ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)G << 16 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)GFC - ( G << 4 ), 0 ) ) ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)B << 16 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)BFC - ( B << 4 ), 0 ) ) ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 0 );
+			IR2 = Lm_B2( (INT32)MAC2, 0 );
+			IR3 = Lm_B3( (INT32)MAC3, 0 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x11:
+		if( gteop == 0x0980011 )
+		{
+			GTELOG( "INTPL" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT16)IR1 << 12 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)RFC - (INT16)IR1, 0 ) ) ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)(INT16)IR2 << 12 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)GFC - (INT16)IR2, 0 ) ) ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)(INT16)IR3 << 12 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)BFC - (INT16)IR3, 0 ) ) ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 0 );
+			IR2 = Lm_B2( (INT32)MAC2, 0 );
+			IR3 = Lm_B3( (INT32)MAC3, 0 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 );
+			return;
+		}
+		break;
+	case 0x12:
+		if( GTE_OP( gteop ) == 0x04 )
+		{
+			GTELOG( "MVMVA" );
+			n_sf = 12 * GTE_SF( gteop );
+			p_n_mx = p_p_n_mx[ GTE_MX( gteop ) ];
+			n_v = GTE_V( gteop );
+			if( n_v < 3 )
+			{
+				n_v1 = *p_n_vx[ n_v ];
+				n_v2 = *p_n_vy[ n_v ];
+				n_v3 = *p_n_vz[ n_v ];
+			}
+			else
+			{
+				n_v1 = IR1;
+				n_v2 = IR2;
+				n_v3 = IR3;
+			}
+			p_n_cv = p_p_n_cv[ GTE_CV( gteop ) ];
+			n_lm = GTE_LM( gteop );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT32)*p_n_cv[ 0 ] << 12 ) + ( (INT16)*p_n_mx[ 0 ] * (INT16)n_v1 ) + ( (INT16)*p_n_mx[ 1 ] * (INT16)n_v2 ) + ( (INT16)*p_n_mx[ 2 ] * (INT16)n_v3 ) ) >> n_sf );
+			MAC2 = A2( ( ( (INT64)(INT32)*p_n_cv[ 1 ] << 12 ) + ( (INT16)*p_n_mx[ 3 ] * (INT16)n_v1 ) + ( (INT16)*p_n_mx[ 4 ] * (INT16)n_v2 ) + ( (INT16)*p_n_mx[ 5 ] * (INT16)n_v3 ) ) >> n_sf );
+			MAC3 = A3( ( ( (INT64)(INT32)*p_n_cv[ 2 ] << 12 ) + ( (INT16)*p_n_mx[ 6 ] * (INT16)n_v1 ) + ( (INT16)*p_n_mx[ 7 ] * (INT16)n_v2 ) + ( (INT16)*p_n_mx[ 8 ] * (INT16)n_v3 ) ) >> n_sf );
+
+			IR1 = Lm_B1( (INT32)MAC1, n_lm );
+			IR2 = Lm_B2( (INT32)MAC2, n_lm );
+			IR3 = Lm_B3( (INT32)MAC3, n_lm );
+			return;
+		}
+		break;
+	case 0x13:
+		if( gteop == 0x0e80413 )
+		{
+			GTELOG( "NCDS" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT16)L11 * (INT16)VX0 ) + ( (INT16)L12 * (INT16)VY0 ) + ( (INT16)L13 * (INT16)VZ0 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)(INT16)L21 * (INT16)VX0 ) + ( (INT16)L22 * (INT16)VY0 ) + ( (INT16)L23 * (INT16)VZ0 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)(INT16)L31 * (INT16)VX0 ) + ( (INT16)L32 * (INT16)VY0 ) + ( (INT16)L33 * (INT16)VZ0 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			MAC1 = A1( ( ( ( (INT64)R << 4 ) * (INT16)IR1 ) + ( (INT16)IR0 * Lm_B1( (INT32)RFC - ( ( R * (INT16)IR1 ) >> 8 ), 0 ) ) ) >> 12 );
+			MAC2 = A2( ( ( ( (INT64)G << 4 ) * (INT16)IR2 ) + ( (INT16)IR0 * Lm_B2( (INT32)GFC - ( ( G * (INT16)IR2 ) >> 8 ), 0 ) ) ) >> 12 );
+			MAC3 = A3( ( ( ( (INT64)B << 4 ) * (INT16)IR3 ) + ( (INT16)IR0 * Lm_B3( (INT32)BFC - ( ( B * (INT16)IR3 ) >> 8 ), 0 ) ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x14:
+		if( gteop == 0x1280414 )
+		{
+			GTELOG( "CDP" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+			IR1 = Lm_B1( MAC1, 1 );
+			IR2 = Lm_B2( MAC2, 1 );
+			IR3 = Lm_B3( MAC3, 1 );
+			MAC1 = A1( ( ( ( (INT64)R << 4 ) * (INT16)IR1 ) + ( (INT16)IR0 * Lm_B1( (INT32)RFC - ( ( R * (INT16)IR1 ) >> 8 ), 0 ) ) ) >> 12 );
+			MAC2 = A2( ( ( ( (INT64)G << 4 ) * (INT16)IR2 ) + ( (INT16)IR0 * Lm_B2( (INT32)GFC - ( ( G * (INT16)IR2 ) >> 8 ), 0 ) ) ) >> 12 );
+			MAC3 = A3( ( ( ( (INT64)B << 4 ) * (INT16)IR3 ) + ( (INT16)IR0 * Lm_B3( (INT32)BFC - ( ( B * (INT16)IR3 ) >> 8 ), 0 ) ) ) >> 12 );
+			IR1 = Lm_B1( MAC1, 1 );
+			IR2 = Lm_B2( MAC2, 1 );
+			IR3 = Lm_B3( MAC3, 1 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x16:
+		if( gteop == 0x0f80416 )
+		{
+			GTELOG( "NCDT" );
+			FLAG = 0;
+
+			for( n_v = 0; n_v < 3; n_v++ )
+			{
+				MAC1 = A1( ( ( (INT64)(INT16)L11 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L12 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L13 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)(INT16)L21 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L22 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L23 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)(INT16)L31 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L32 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L33 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				MAC1 = A1( ( ( ( (INT64)R << 4 ) * (INT16)IR1 ) + ( (INT16)IR0 * Lm_B1( (INT32)RFC - ( ( R * (INT16)IR1 ) >> 8 ), 0 ) ) ) >> 12 );
+				MAC2 = A2( ( ( ( (INT64)G << 4 ) * (INT16)IR2 ) + ( (INT16)IR0 * Lm_B2( (INT32)GFC - ( ( G * (INT16)IR2 ) >> 8 ), 0 ) ) ) >> 12 );
+				MAC3 = A3( ( ( ( (INT64)B << 4 ) * (INT16)IR3 ) + ( (INT16)IR0 * Lm_B3( (INT32)BFC - ( ( B * (INT16)IR3 ) >> 8 ), 0 ) ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				CD0 = CD1;
+				CD1 = CD2;
+				CD2 = CODE;
+				R0 = R1;
+				R1 = R2;
+				R2 = Lm_C1( (INT32)MAC1 >> 4 );
+				G0 = G1;
+				G1 = G2;
+				G2 = Lm_C2( (INT32)MAC2 >> 4 );
+				B0 = B1;
+				B1 = B2;
+				B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			}
+			return;
+		}
+		break;
+	case 0x1b:
+		if( gteop == 0x108041b )
+		{
+			GTELOG( "NCCS" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT16)L11 * (INT16)VX0 ) + ( (INT16)L12 * (INT16)VY0 ) + ( (INT16)L13 * (INT16)VZ0 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)(INT16)L21 * (INT16)VX0 ) + ( (INT16)L22 * (INT16)VY0 ) + ( (INT16)L23 * (INT16)VZ0 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)(INT16)L31 * (INT16)VX0 ) + ( (INT16)L32 * (INT16)VY0 ) + ( (INT16)L33 * (INT16)VZ0 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			MAC1 = A1( ( (INT64)R * (INT16)IR1 ) >> 8 );
+			MAC2 = A2( ( (INT64)G * (INT16)IR2 ) >> 8 );
+			MAC3 = A3( ( (INT64)B * (INT16)IR3 ) >> 8 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x1c:
+		if( gteop == 0x138041c )
+		{
+			GTELOG( "CC" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+			IR1 = Lm_B1( MAC1, 1 );
+			IR2 = Lm_B2( MAC2, 1 );
+			IR3 = Lm_B3( MAC3, 1 );
+			MAC1 = A1( ( (INT64)R * (INT16)IR1 ) >> 8 );
+			MAC2 = A2( ( (INT64)G * (INT16)IR2 ) >> 8 );
+			MAC3 = A3( ( (INT64)B * (INT16)IR3 ) >> 8 );
+			IR1 = Lm_B1( MAC1, 1 );
+			IR2 = Lm_B2( MAC2, 1 );
+			IR3 = Lm_B3( MAC3, 1 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x1e:
+		if( gteop == 0x0c8041e )
+		{
+			GTELOG( "NCS" );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT16)L11 * (INT16)VX0 ) + ( (INT16)L12 * (INT16)VY0 ) + ( (INT16)L13 * (INT16)VZ0 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)(INT16)L21 * (INT16)VX0 ) + ( (INT16)L22 * (INT16)VY0 ) + ( (INT16)L23 * (INT16)VZ0 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)(INT16)L31 * (INT16)VX0 ) + ( (INT16)L32 * (INT16)VY0 ) + ( (INT16)L33 * (INT16)VZ0 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+			MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+			MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+			IR1 = Lm_B1( (INT32)MAC1, 1 );
+			IR2 = Lm_B2( (INT32)MAC2, 1 );
+			IR3 = Lm_B3( (INT32)MAC3, 1 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x20:
+		if( gteop == 0x0d80420 )
+		{
+			GTELOG( "NCT" );
+			FLAG = 0;
+
+			for( n_v = 0; n_v < 3; n_v++ )
+			{
+				MAC1 = A1( ( ( (INT64)(INT16)L11 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L12 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L13 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)(INT16)L21 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L22 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L23 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)(INT16)L31 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L32 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L33 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				CD0 = CD1;
+				CD1 = CD2;
+				CD2 = CODE;
+				R0 = R1;
+				R1 = R2;
+				R2 = Lm_C1( (INT32)MAC1 >> 4 );
+				G0 = G1;
+				G1 = G2;
+				G2 = Lm_C2( (INT32)MAC2 >> 4 );
+				B0 = B1;
+				B1 = B2;
+				B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			}
+			return;
+		}
+		break;
+	case 0x28:
+		if( GTE_OP( gteop ) == 0x0a && GTE_LM( gteop ) == 1 )
+		{
+			GTELOG( "SQR" );
+			n_sf = 12 * GTE_SF( gteop );
+			FLAG = 0;
+
+			MAC1 = A1( ( (INT64)(INT16)IR1 * (INT16)IR1 ) >> n_sf );
+			MAC2 = A2( ( (INT64)(INT16)IR2 * (INT16)IR2 ) >> n_sf );
+			MAC3 = A3( ( (INT64)(INT16)IR3 * (INT16)IR3 ) >> n_sf );
+			IR1 = Lm_B1( MAC1, 1 );
+			IR2 = Lm_B2( MAC2, 1 );
+			IR3 = Lm_B3( MAC3, 1 );
+			return;
+		}
+		break;
+	// DCPL 0x29
+	case 0x2a:
+		if( gteop == 0x0f8002a )
+		{
+			GTELOG( "DPCT" );
+			FLAG = 0;
+
+			for( n_pass = 0; n_pass < 3; n_pass++ )
+			{
+				MAC1 = A1( ( ( (INT64)R0 << 16 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)RFC - ( R0 << 4 ), 0 ) ) ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)G0 << 16 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)GFC - ( G0 << 4 ), 0 ) ) ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)B0 << 16 ) + ( (INT64)(INT16)IR0 * ( Lm_B1( (INT32)BFC - ( B0 << 4 ), 0 ) ) ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 0 );
+				IR2 = Lm_B2( (INT32)MAC2, 0 );
+				IR3 = Lm_B3( (INT32)MAC3, 0 );
+				CD0 = CD1;
+				CD1 = CD2;
+				CD2 = CODE;
+				R0 = R1;
+				R1 = R2;
+				R2 = Lm_C1( (INT32)MAC1 >> 4 );
+				G0 = G1;
+				G1 = G2;
+				G2 = Lm_C2( (INT32)MAC2 >> 4 );
+				B0 = B1;
+				B1 = B2;
+				B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			}
+			return;
+		}
+		break;
+	case 0x2d:
+		if( gteop == 0x158002d )
+		{
+			GTELOG( "AVSZ3" );
+			FLAG = 0;
+
+			MAC0 = F( ( (INT64)(INT16)ZSF3 * SZ1 ) + ( (INT16)ZSF3 * SZ2 ) + ( (INT16)ZSF3 * SZ3 ) );
+			OTZ = Lm_D( (INT32)MAC0 >> 12 );
+			return;
+		}
+		break;
+	case 0x2e:
+		if( gteop == 0x168002e )
+		{
+			GTELOG( "AVSZ4" );
+			FLAG = 0;
+
+			MAC0 = F( ( (INT64)(INT16)ZSF4 * SZ0 ) + ( (INT16)ZSF4 * SZ1 ) + ( (INT16)ZSF4 * SZ2 ) + ( (INT16)ZSF4 * SZ3 ) );
+			OTZ = Lm_D( (INT32)MAC0 >> 12 );
+			return;
+		}
+		break;
+	case 0x30:
+		if( gteop == 0x0280030 )
+		{
+			GTELOG( "RTPT" );
+			FLAG = 0;
+
+			for( n_v = 0; n_v < 3; n_v++ )
+			{
+				MAC1 = A1( ( ( (INT64)(INT32)TRX << 12 ) + ( (INT16)R11 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)R12 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)R13 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)(INT32)TRY << 12 ) + ( (INT16)R21 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)R22 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)R23 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)(INT32)TRZ << 12 ) + ( (INT16)R31 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)R32 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)R33 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 0 );
+				IR2 = Lm_B2( (INT32)MAC2, 0 );
+				IR3 = Lm_B3( (INT32)MAC3, 0 );
+				SZ0 = SZ1;
+				SZ1 = SZ2;
+				SZ2 = SZ3;
+				SZ3 = Lm_D( (INT32)MAC3 );
+				SXY0 = SXY1;
+				SXY1 = SXY2;
+				SX2 = Lm_G1( F( ( (INT64)(INT32)OFX + ( (INT64)(INT16)IR1 * ( ( (UINT32)H << 16 ) / Lm_E( SZ3 ) ) ) ) >> 16 ) );
+				SY2 = Lm_G2( F( ( (INT64)(INT32)OFY + ( (INT64)(INT16)IR2 * ( ( (UINT32)H << 16 ) / Lm_E( SZ3 ) ) ) ) >> 16 ) );
+				MAC0 = F( (INT64)(INT32)DQB + ( (INT64)(INT16)DQA * ( ( (UINT32)H << 16 ) / Lm_E( SZ3 ) ) ) );
+				IR0 = Lm_H( (INT32)MAC0 >> 12 );
+			}
+			return;
+		}
+		break;
+	case 0x3d:
+		if( GTE_OP( gteop ) == 0x09 ||
+			GTE_OP( gteop ) == 0x19 )
+		{
+			GTELOG( "GPF" );
+			n_sf = 12 * GTE_SF( gteop );
+			FLAG = 0;
+
+			MAC1 = A1( ( (INT64)(INT16)IR0 * (INT16)IR1 ) >> n_sf );
+			MAC2 = A2( ( (INT64)(INT16)IR0 * (INT16)IR2 ) >> n_sf );
+			MAC3 = A3( ( (INT64)(INT16)IR0 * (INT16)IR3 ) >> n_sf );
+			IR1 = Lm_B1( (INT32)MAC1, 0 );
+			IR2 = Lm_B2( (INT32)MAC2, 0 );
+			IR3 = Lm_B3( (INT32)MAC3, 0 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x3e:
+		if( GTE_OP( gteop ) == 0x1a )
+		{
+			GTELOG( "GPL" );
+			n_sf = 12 * GTE_SF( gteop );
+			FLAG = 0;
+
+			MAC1 = A1( ( ( (INT64)(INT32)MAC1 << n_sf ) + ( (INT16)IR0 * (INT16)IR1 ) ) >> n_sf );
+			MAC2 = A2( ( ( (INT64)(INT32)MAC2 << n_sf ) + ( (INT16)IR0 * (INT16)IR2 ) ) >> n_sf );
+			MAC3 = A3( ( ( (INT64)(INT32)MAC3 << n_sf ) + ( (INT16)IR0 * (INT16)IR3 ) ) >> n_sf );
+			IR1 = Lm_B1( (INT32)MAC1, 0 );
+			IR2 = Lm_B2( (INT32)MAC2, 0 );
+			IR3 = Lm_B3( (INT32)MAC3, 0 );
+			CD0 = CD1;
+			CD1 = CD2;
+			CD2 = CODE;
+			R0 = R1;
+			R1 = R2;
+			R2 = Lm_C1( (INT32)MAC1 >> 4 );
+			G0 = G1;
+			G1 = G2;
+			G2 = Lm_C2( (INT32)MAC2 >> 4 );
+			B0 = B1;
+			B1 = B2;
+			B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			return;
+		}
+		break;
+	case 0x3f:
+		if( gteop == 0x108043f ||
+			gteop == 0x118043f )
+		{
+			GTELOG( "NCCT" );
+			FLAG = 0;
+
+			for( n_v = 0; n_v < 3; n_v++ )
+			{
+				MAC1 = A1( ( ( (INT64)(INT16)L11 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L12 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L13 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)(INT16)L21 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L22 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L23 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)(INT16)L31 * (INT16)*p_n_vx[ n_v ] ) + ( (INT16)L32 * (INT16)*p_n_vy[ n_v ] ) + ( (INT16)L33 * (INT16)*p_n_vz[ n_v ] ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				MAC1 = A1( ( ( (INT64)RBK << 12 ) + ( (INT16)LR1 * (INT16)IR1 ) + ( (INT16)LR2 * (INT16)IR2 ) + ( (INT16)LR3 * (INT16)IR3 ) ) >> 12 );
+				MAC2 = A2( ( ( (INT64)GBK << 12 ) + ( (INT16)LG1 * (INT16)IR1 ) + ( (INT16)LG2 * (INT16)IR2 ) + ( (INT16)LG3 * (INT16)IR3 ) ) >> 12 );
+				MAC3 = A3( ( ( (INT64)BBK << 12 ) + ( (INT16)LB1 * (INT16)IR1 ) + ( (INT16)LB2 * (INT16)IR2 ) + ( (INT16)LB3 * (INT16)IR3 ) ) >> 12 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				MAC1 = A1( ( (INT64)R * (INT16)IR1 ) >> 8 );
+				MAC2 = A2( ( (INT64)G * (INT16)IR2 ) >> 8 );
+				MAC3 = A3( ( (INT64)B * (INT16)IR3 ) >> 8 );
+				IR1 = Lm_B1( (INT32)MAC1, 1 );
+				IR2 = Lm_B2( (INT32)MAC2, 1 );
+				IR3 = Lm_B3( (INT32)MAC3, 1 );
+				CD0 = CD1;
+				CD1 = CD2;
+				CD2 = CODE;
+				R0 = R1;
+				R1 = R2;
+				R2 = Lm_C1( (INT32)MAC1 >> 4 );
+				G0 = G1;
+				G1 = G2;
+				G2 = Lm_C2( (INT32)MAC2 >> 4 );
+				B0 = B1;
+				B1 = B2;
+				B2 = Lm_C3( (INT32)MAC3 >> 4 );
+			}
+			return;
+		}
+		break;
+	}
+//	usrintf_showmessage_secs( 1, "unknown GTE op %08x", gteop );
+	logerror( "%08x: unknown GTE op %08x\n", mipscpu.pc, gteop );
+	mips_stop();
+}
+
+/**************************************************************************
+ * Generic set_info
+ **************************************************************************/
+
+void mips_set_info(UINT32 state, union cpuinfo *info)
+{
+	switch (state)
+	{
+		/* --- the following bits of info are set as 64-bit signed integers --- */
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ0:		set_irq_line(MIPS_IRQ0, info->i);		break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ1:		set_irq_line(MIPS_IRQ1, info->i);		break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ2:		set_irq_line(MIPS_IRQ2, info->i);		break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ3:		set_irq_line(MIPS_IRQ3, info->i);		break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ4:		set_irq_line(MIPS_IRQ4, info->i);		break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ5:		set_irq_line(MIPS_IRQ5, info->i);		break;
+
+		case CPUINFO_INT_PC:							mips_set_pc( info->i );					break;
+		case CPUINFO_INT_REGISTER + MIPS_PC:			mips_set_pc( info->i );					break;
+		case CPUINFO_INT_SP:							/* no stack */							break;
+		case CPUINFO_INT_REGISTER + MIPS_DELAYV:		mipscpu.delayv = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_DELAYR:		if( info->i <= REGPC ) mipscpu.delayr = info->i; break;
+		case CPUINFO_INT_REGISTER + MIPS_HI:			mipscpu.hi = info->i;					break;
+		case CPUINFO_INT_REGISTER + MIPS_LO:			mipscpu.lo = info->i;					break;
+		case CPUINFO_INT_REGISTER + MIPS_R0:			mipscpu.r[ 0 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R1:			mipscpu.r[ 1 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R2:			mipscpu.r[ 2 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R3:			mipscpu.r[ 3 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R4:			mipscpu.r[ 4 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R5:			mipscpu.r[ 5 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R6:			mipscpu.r[ 6 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R7:			mipscpu.r[ 7 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R8:			mipscpu.r[ 8 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R9:			mipscpu.r[ 9 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R10:			mipscpu.r[ 10 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R11:			mipscpu.r[ 11 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R12:			mipscpu.r[ 12 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R13:			mipscpu.r[ 13 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R14:			mipscpu.r[ 14 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R15:			mipscpu.r[ 15 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R16:			mipscpu.r[ 16 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R17:			mipscpu.r[ 17 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R18:			mipscpu.r[ 18 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R19:			mipscpu.r[ 19 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R20:			mipscpu.r[ 20 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R21:			mipscpu.r[ 21 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R22:			mipscpu.r[ 22 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R23:			mipscpu.r[ 23 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R24:			mipscpu.r[ 24 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R25:			mipscpu.r[ 25 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R26:			mipscpu.r[ 26 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R27:			mipscpu.r[ 27 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R28:			mipscpu.r[ 28 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R29:			mipscpu.r[ 29 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R30:			mipscpu.r[ 30 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_R31:			mipscpu.r[ 31 ] = info->i;				break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R0:			mips_set_cp0r( 0, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R1:			mips_set_cp0r( 1, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R2:			mips_set_cp0r( 2, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R3:			mips_set_cp0r( 3, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R4:			mips_set_cp0r( 4, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R5:			mips_set_cp0r( 5, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R6:			mips_set_cp0r( 6, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R7:			mips_set_cp0r( 7, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R8:			mips_set_cp0r( 8, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R9:			mips_set_cp0r( 9, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R10:		mips_set_cp0r( 10, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R11:		mips_set_cp0r( 11, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R12:		mips_set_cp0r( 12, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R13:		mips_set_cp0r( 13, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R14:		mips_set_cp0r( 14, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R15:		mips_set_cp0r( 15, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R16:		mips_set_cp0r( 16, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R17:		mips_set_cp0r( 17, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R18:		mips_set_cp0r( 18, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R19:		mips_set_cp0r( 19, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R20:		mips_set_cp0r( 20, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R21:		mips_set_cp0r( 21, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R22:		mips_set_cp0r( 22, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R23:		mips_set_cp0r( 23, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R24:		mips_set_cp0r( 24, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R25:		mips_set_cp0r( 25, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R26:		mips_set_cp0r( 26, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R27:		mips_set_cp0r( 27, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R28:		mips_set_cp0r( 28, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R29:		mips_set_cp0r( 29, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R30:		mips_set_cp0r( 30, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R31:		mips_set_cp0r( 31, info->i );			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR0:		mipscpu.cp2dr[ 0 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR1:		mipscpu.cp2dr[ 1 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR2:		mipscpu.cp2dr[ 2 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR3:		mipscpu.cp2dr[ 3 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR4:		mipscpu.cp2dr[ 4 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR5:		mipscpu.cp2dr[ 5 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR6:		mipscpu.cp2dr[ 6 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR7:		mipscpu.cp2dr[ 7 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR8:		mipscpu.cp2dr[ 8 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR9:		mipscpu.cp2dr[ 9 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR10:		mipscpu.cp2dr[ 10 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR11:		mipscpu.cp2dr[ 11 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR12:		mipscpu.cp2dr[ 12 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR13:		mipscpu.cp2dr[ 13 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR14:		mipscpu.cp2dr[ 14 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR15:		mipscpu.cp2dr[ 15 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR16:		mipscpu.cp2dr[ 16 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR17:		mipscpu.cp2dr[ 17 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR18:		mipscpu.cp2dr[ 18 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR19:		mipscpu.cp2dr[ 19 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR20:		mipscpu.cp2dr[ 20 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR21:		mipscpu.cp2dr[ 21 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR22:		mipscpu.cp2dr[ 22 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR23:		mipscpu.cp2dr[ 23 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR24:		mipscpu.cp2dr[ 24 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR25:		mipscpu.cp2dr[ 25 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR26:		mipscpu.cp2dr[ 26 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR27:		mipscpu.cp2dr[ 27 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR28:		mipscpu.cp2dr[ 28 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR29:		mipscpu.cp2dr[ 29 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR30:		mipscpu.cp2dr[ 30 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR31:		mipscpu.cp2dr[ 31 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR0:		mipscpu.cp2cr[ 0 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR1:		mipscpu.cp2cr[ 1 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR2:		mipscpu.cp2cr[ 2 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR3:		mipscpu.cp2cr[ 3 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR4:		mipscpu.cp2cr[ 4 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR5:		mipscpu.cp2cr[ 5 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR6:		mipscpu.cp2cr[ 6 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR7:		mipscpu.cp2cr[ 7 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR8:		mipscpu.cp2cr[ 8 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR9:		mipscpu.cp2cr[ 9 ].d = info->i;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR10:		mipscpu.cp2cr[ 10 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR11:		mipscpu.cp2cr[ 11 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR12:		mipscpu.cp2cr[ 12 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR13:		mipscpu.cp2cr[ 13 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR14:		mipscpu.cp2cr[ 14 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR15:		mipscpu.cp2cr[ 15 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR16:		mipscpu.cp2cr[ 16 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR17:		mipscpu.cp2cr[ 17 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR18:		mipscpu.cp2cr[ 18 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR19:		mipscpu.cp2cr[ 19 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR20:		mipscpu.cp2cr[ 20 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR21:		mipscpu.cp2cr[ 21 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR22:		mipscpu.cp2cr[ 22 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR23:		mipscpu.cp2cr[ 23 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR24:		mipscpu.cp2cr[ 24 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR25:		mipscpu.cp2cr[ 25 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR26:		mipscpu.cp2cr[ 26 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR27:		mipscpu.cp2cr[ 27 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR28:		mipscpu.cp2cr[ 28 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR29:		mipscpu.cp2cr[ 29 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR30:		mipscpu.cp2cr[ 30 ].d = info->i;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR31:		mipscpu.cp2cr[ 31 ].d = info->i;		break;
+		
+		/* --- the following bits of info are set as pointers to data or functions --- */
+		case CPUINFO_PTR_IRQ_CALLBACK:					mipscpu.irq_callback = info->irqcallback;			break;
+	}
+}
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+void mips_get_info(UINT32 state, union cpuinfo *info)
+{
+	switch (state)
+	{
+		/* --- the following bits of info are returned as 64-bit signed integers --- */
+		case CPUINFO_INT_CONTEXT_SIZE:					info->i = sizeof(mipscpu);				break;
+		case CPUINFO_INT_INPUT_LINES:					info->i = 6;							break;
+		case CPUINFO_INT_DEFAULT_IRQ_VECTOR:			info->i = 0;							break;
+		case CPUINFO_INT_ENDIANNESS:					info->i = CPU_IS_LE;					break;
+		case CPUINFO_INT_CLOCK_DIVIDER:					info->i = 1;							break;
+		case CPUINFO_INT_MIN_INSTRUCTION_BYTES:			info->i = 4;							break;
+		case CPUINFO_INT_MAX_INSTRUCTION_BYTES:			info->i = 4;							break;
+		case CPUINFO_INT_MIN_CYCLES:					info->i = 1;							break;
+		case CPUINFO_INT_MAX_CYCLES:					info->i = 40;							break;
+		
+		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_PROGRAM:	info->i = 32;					break;
+		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32;					break;
+		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = 0;					break;
+		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_DATA:	info->i = 0;					break;
+		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_DATA: 	info->i = 0;					break;
+		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_DATA: 	info->i = 0;					break;
+		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_IO:		info->i = 0;					break;
+		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_IO: 		info->i = 0;					break;
+		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_IO: 		info->i = 0;					break;
+
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ0:		info->i = (mipscpu.cp0r[ CP0_CAUSE ] & 0x400) ? ASSERT_LINE : CLEAR_LINE; break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ1:		info->i = (mipscpu.cp0r[ CP0_CAUSE ] & 0x800) ? ASSERT_LINE : CLEAR_LINE; break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ2:		info->i = (mipscpu.cp0r[ CP0_CAUSE ] & 0x1000) ? ASSERT_LINE : CLEAR_LINE; break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ3:		info->i = (mipscpu.cp0r[ CP0_CAUSE ] & 0x2000) ? ASSERT_LINE : CLEAR_LINE; break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ4:		info->i = (mipscpu.cp0r[ CP0_CAUSE ] & 0x4000) ? ASSERT_LINE : CLEAR_LINE; break;
+		case CPUINFO_INT_INPUT_STATE + MIPS_IRQ5:		info->i = (mipscpu.cp0r[ CP0_CAUSE ] & 0x8000) ? ASSERT_LINE : CLEAR_LINE; break;
+
+		case CPUINFO_INT_PREVIOUSPC:					/* not implemented */					break;
+
+		case CPUINFO_INT_PC:							info->i = mipscpu.pc;					break;
+		case CPUINFO_INT_REGISTER + MIPS_PC:			info->i = mipscpu.pc;					break;
+		case CPUINFO_INT_SP:
+			/* because there is no hardware stack and the pipeline causes the cpu to execute the
+			instruction after a subroutine call before the subroutine is executed there is little
+			chance of cmd_step_over() in mamedbg.c working. */
+								info->i = 0;													break;
+		case CPUINFO_INT_REGISTER + MIPS_DELAYV:		info->i = mipscpu.delayv;				break;
+		case CPUINFO_INT_REGISTER + MIPS_DELAYR:		info->i = mipscpu.delayr;				break;
+		case CPUINFO_INT_REGISTER + MIPS_HI:			info->i = mipscpu.hi;					break;
+		case CPUINFO_INT_REGISTER + MIPS_LO:			info->i = mipscpu.lo;					break;
+		case CPUINFO_INT_REGISTER + MIPS_R0:			info->i = mipscpu.r[ 0 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R1:			info->i = mipscpu.r[ 1 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R2:			info->i = mipscpu.r[ 2 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R3:			info->i = mipscpu.r[ 3 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R4:			info->i = mipscpu.r[ 4 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R5:			info->i = mipscpu.r[ 5 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R6:			info->i = mipscpu.r[ 6 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R7:			info->i = mipscpu.r[ 7 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R8:			info->i = mipscpu.r[ 8 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R9:			info->i = mipscpu.r[ 9 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R10:			info->i = mipscpu.r[ 10 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R11:			info->i = mipscpu.r[ 11 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R12:			info->i = mipscpu.r[ 12 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R13:			info->i = mipscpu.r[ 13 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R14:			info->i = mipscpu.r[ 14 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R15:			info->i = mipscpu.r[ 15 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R16:			info->i = mipscpu.r[ 16 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R17:			info->i = mipscpu.r[ 17 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R18:			info->i = mipscpu.r[ 18 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R19:			info->i = mipscpu.r[ 19 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R20:			info->i = mipscpu.r[ 20 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R21:			info->i = mipscpu.r[ 21 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R22:			info->i = mipscpu.r[ 22 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R23:			info->i = mipscpu.r[ 23 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R24:			info->i = mipscpu.r[ 24 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R25:			info->i = mipscpu.r[ 25 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R26:			info->i = mipscpu.r[ 26 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R27:			info->i = mipscpu.r[ 27 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R28:			info->i = mipscpu.r[ 28 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R29:			info->i = mipscpu.r[ 29 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R30:			info->i = mipscpu.r[ 30 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_R31:			info->i = mipscpu.r[ 31 ];				break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R0:			info->i = mipscpu.cp0r[ 0 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R1:			info->i = mipscpu.cp0r[ 1 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R2:			info->i = mipscpu.cp0r[ 2 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R3:			info->i = mipscpu.cp0r[ 3 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R4:			info->i = mipscpu.cp0r[ 4 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R5:			info->i = mipscpu.cp0r[ 5 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R6:			info->i = mipscpu.cp0r[ 6 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R7:			info->i = mipscpu.cp0r[ 7 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R8:			info->i = mipscpu.cp0r[ 8 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R9:			info->i = mipscpu.cp0r[ 9 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R10:		info->i = mipscpu.cp0r[ 10 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R11:		info->i = mipscpu.cp0r[ 11 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R12:		info->i = mipscpu.cp0r[ 12 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R13:		info->i = mipscpu.cp0r[ 13 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R14:		info->i = mipscpu.cp0r[ 14 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R15:		info->i = mipscpu.cp0r[ 15 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R16:		info->i = mipscpu.cp0r[ 16 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R17:		info->i = mipscpu.cp0r[ 17 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R18:		info->i = mipscpu.cp0r[ 18 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R19:		info->i = mipscpu.cp0r[ 19 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R20:		info->i = mipscpu.cp0r[ 20 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R21:		info->i = mipscpu.cp0r[ 21 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R22:		info->i = mipscpu.cp0r[ 22 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R23:		info->i = mipscpu.cp0r[ 23 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R24:		info->i = mipscpu.cp0r[ 24 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R25:		info->i = mipscpu.cp0r[ 25 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R26:		info->i = mipscpu.cp0r[ 26 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R27:		info->i = mipscpu.cp0r[ 27 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R28:		info->i = mipscpu.cp0r[ 28 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R29:		info->i = mipscpu.cp0r[ 29 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R30:		info->i = mipscpu.cp0r[ 30 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP0R31:		info->i = mipscpu.cp0r[ 31 ];			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR0:		info->i = mipscpu.cp2dr[ 0 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR1:		info->i = mipscpu.cp2dr[ 1 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR2:		info->i = mipscpu.cp2dr[ 2 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR3:		info->i = mipscpu.cp2dr[ 3 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR4:		info->i = mipscpu.cp2dr[ 4 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR5:		info->i = mipscpu.cp2dr[ 5 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR6:		info->i = mipscpu.cp2dr[ 6 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR7:		info->i = mipscpu.cp2dr[ 7 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR8:		info->i = mipscpu.cp2dr[ 8 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR9:		info->i = mipscpu.cp2dr[ 9 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR10:		info->i = mipscpu.cp2dr[ 10 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR11:		info->i = mipscpu.cp2dr[ 11 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR12:		info->i = mipscpu.cp2dr[ 12 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR13:		info->i = mipscpu.cp2dr[ 13 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR14:		info->i = mipscpu.cp2dr[ 14 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR15:		info->i = mipscpu.cp2dr[ 15 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR16:		info->i = mipscpu.cp2dr[ 16 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR17:		info->i = mipscpu.cp2dr[ 17 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR18:		info->i = mipscpu.cp2dr[ 18 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR19:		info->i = mipscpu.cp2dr[ 19 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR20:		info->i = mipscpu.cp2dr[ 20 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR21:		info->i = mipscpu.cp2dr[ 21 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR22:		info->i = mipscpu.cp2dr[ 22 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR23:		info->i = mipscpu.cp2dr[ 23 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR24:		info->i = mipscpu.cp2dr[ 24 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR25:		info->i = mipscpu.cp2dr[ 25 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR26:		info->i = mipscpu.cp2dr[ 26 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR27:		info->i = mipscpu.cp2dr[ 27 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR28:		info->i = mipscpu.cp2dr[ 28 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR29:		info->i = mipscpu.cp2dr[ 29 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR30:		info->i = mipscpu.cp2dr[ 30 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2DR31:		info->i = mipscpu.cp2dr[ 31 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR0:		info->i = mipscpu.cp2cr[ 0 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR1:		info->i = mipscpu.cp2cr[ 1 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR2:		info->i = mipscpu.cp2cr[ 2 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR3:		info->i = mipscpu.cp2cr[ 3 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR4:		info->i = mipscpu.cp2cr[ 4 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR5:		info->i = mipscpu.cp2cr[ 5 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR6:		info->i = mipscpu.cp2cr[ 6 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR7:		info->i = mipscpu.cp2cr[ 7 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR8:		info->i = mipscpu.cp2cr[ 8 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR9:		info->i = mipscpu.cp2cr[ 9 ].d;			break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR10:		info->i = mipscpu.cp2cr[ 10 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR11:		info->i = mipscpu.cp2cr[ 11 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR12:		info->i = mipscpu.cp2cr[ 12 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR13:		info->i = mipscpu.cp2cr[ 13 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR14:		info->i = mipscpu.cp2cr[ 14 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR15:		info->i = mipscpu.cp2cr[ 15 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR16:		info->i = mipscpu.cp2cr[ 16 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR17:		info->i = mipscpu.cp2cr[ 17 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR18:		info->i = mipscpu.cp2cr[ 18 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR19:		info->i = mipscpu.cp2cr[ 19 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR20:		info->i = mipscpu.cp2cr[ 20 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR21:		info->i = mipscpu.cp2cr[ 21 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR22:		info->i = mipscpu.cp2cr[ 22 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR23:		info->i = mipscpu.cp2cr[ 23 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR24:		info->i = mipscpu.cp2cr[ 24 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR25:		info->i = mipscpu.cp2cr[ 25 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR26:		info->i = mipscpu.cp2cr[ 26 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR27:		info->i = mipscpu.cp2cr[ 27 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR28:		info->i = mipscpu.cp2cr[ 28 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR29:		info->i = mipscpu.cp2cr[ 29 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR30:		info->i = mipscpu.cp2cr[ 30 ].d;		break;
+		case CPUINFO_INT_REGISTER + MIPS_CP2CR31:		info->i = mipscpu.cp2cr[ 31 ].d;		break;
+
+		/* --- the following bits of info are returned as pointers to data or functions --- */
+		case CPUINFO_PTR_SET_INFO:						info->setinfo = mips_set_info;			break;
+		case CPUINFO_PTR_GET_CONTEXT:					info->getcontext = mips_get_context;	break;
+		case CPUINFO_PTR_SET_CONTEXT:					info->setcontext = mips_set_context;	break;
+		case CPUINFO_PTR_INIT:							info->init = mips_init;					break;
+		case CPUINFO_PTR_RESET:							info->reset = mips_reset;				break;
+		case CPUINFO_PTR_EXIT:							info->exit = mips_exit;					break;
+		case CPUINFO_PTR_EXECUTE:						info->execute = mips_execute;			break;
+		case CPUINFO_PTR_BURN:							info->burn = NULL;						break;
+		case CPUINFO_PTR_DISASSEMBLE:					info->disassemble = mips_dasm;			break;
+		case CPUINFO_PTR_IRQ_CALLBACK:					info->irqcallback = mipscpu.irq_callback; break;
+		case CPUINFO_PTR_INSTRUCTION_COUNTER:			info->icount = &mips_ICount;			break;
+		case CPUINFO_PTR_REGISTER_LAYOUT:				info->p = mips_reg_layout;				break;
+		case CPUINFO_PTR_WINDOW_LAYOUT:					info->p = mips_win_layout;				break;
+
+		/* --- the following bits of info are returned as NULL-terminated strings --- */
+#if 0
+		case CPUINFO_STR_NAME:							strcpy(info->s = cpuintrf_temp_str(), "PSX CPU"); break;
+		case CPUINFO_STR_CORE_FAMILY:					strcpy(info->s = cpuintrf_temp_str(), "mipscpu"); break;
+		case CPUINFO_STR_CORE_VERSION:					strcpy(info->s = cpuintrf_temp_str(), "1.4"); break;
+		case CPUINFO_STR_CORE_FILE:						strcpy(info->s = cpuintrf_temp_str(), __FILE__); break;
+		case CPUINFO_STR_CORE_CREDITS:					strcpy(info->s = cpuintrf_temp_str(), "Copyright 2003 smf"); break;
+
+		case CPUINFO_STR_FLAGS:							strcpy(info->s = cpuintrf_temp_str(), " "); break;
+
+		case CPUINFO_STR_REGISTER + MIPS_PC:			sprintf( info->s = cpuintrf_temp_str(), "pc      :%08x", mipscpu.pc ); break;
+		case CPUINFO_STR_REGISTER + MIPS_DELAYV:		sprintf( info->s = cpuintrf_temp_str(), "delay   :%08x", mipscpu.delayv ); break;
+		case CPUINFO_STR_REGISTER + MIPS_DELAYR:		sprintf( info->s = cpuintrf_temp_str(), "delay %s:%02x", delayn[ mipscpu.delayr ], mipscpu.delayr ); break;
+		case CPUINFO_STR_REGISTER + MIPS_HI:			sprintf( info->s = cpuintrf_temp_str(), "hi      :%08x", mipscpu.hi ); break;
+		case CPUINFO_STR_REGISTER + MIPS_LO:			sprintf( info->s = cpuintrf_temp_str(), "lo      :%08x", mipscpu.lo ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R0:			sprintf( info->s = cpuintrf_temp_str(), "zero    :%08x", mipscpu.r[ 0 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R1:			sprintf( info->s = cpuintrf_temp_str(), "at      :%08x", mipscpu.r[ 1 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R2:			sprintf( info->s = cpuintrf_temp_str(), "v0      :%08x", mipscpu.r[ 2 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R3:			sprintf( info->s = cpuintrf_temp_str(), "v1      :%08x", mipscpu.r[ 3 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R4:			sprintf( info->s = cpuintrf_temp_str(), "a0      :%08x", mipscpu.r[ 4 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R5:			sprintf( info->s = cpuintrf_temp_str(), "a1      :%08x", mipscpu.r[ 5 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R6:			sprintf( info->s = cpuintrf_temp_str(), "a2      :%08x", mipscpu.r[ 6 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R7:			sprintf( info->s = cpuintrf_temp_str(), "a3      :%08x", mipscpu.r[ 7 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R8:			sprintf( info->s = cpuintrf_temp_str(), "t0      :%08x", mipscpu.r[ 8 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R9:			sprintf( info->s = cpuintrf_temp_str(), "t1      :%08x", mipscpu.r[ 9 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R10:			sprintf( info->s = cpuintrf_temp_str(), "t2      :%08x", mipscpu.r[ 10 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R11:			sprintf( info->s = cpuintrf_temp_str(), "t3      :%08x", mipscpu.r[ 11 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R12:			sprintf( info->s = cpuintrf_temp_str(), "t4      :%08x", mipscpu.r[ 12 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R13:			sprintf( info->s = cpuintrf_temp_str(), "t5      :%08x", mipscpu.r[ 13 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R14:			sprintf( info->s = cpuintrf_temp_str(), "t6      :%08x", mipscpu.r[ 14 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R15:			sprintf( info->s = cpuintrf_temp_str(), "t7      :%08x", mipscpu.r[ 15 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R16:			sprintf( info->s = cpuintrf_temp_str(), "s0      :%08x", mipscpu.r[ 16 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R17:			sprintf( info->s = cpuintrf_temp_str(), "s1      :%08x", mipscpu.r[ 17 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R18:			sprintf( info->s = cpuintrf_temp_str(), "s2      :%08x", mipscpu.r[ 18 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R19:			sprintf( info->s = cpuintrf_temp_str(), "s3      :%08x", mipscpu.r[ 19 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R20:			sprintf( info->s = cpuintrf_temp_str(), "s4      :%08x", mipscpu.r[ 20 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R21:			sprintf( info->s = cpuintrf_temp_str(), "s5      :%08x", mipscpu.r[ 21 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R22:			sprintf( info->s = cpuintrf_temp_str(), "s6      :%08x", mipscpu.r[ 22 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R23:			sprintf( info->s = cpuintrf_temp_str(), "s7      :%08x", mipscpu.r[ 23 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R24:			sprintf( info->s = cpuintrf_temp_str(), "t8      :%08x", mipscpu.r[ 24 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R25:			sprintf( info->s = cpuintrf_temp_str(), "t9      :%08x", mipscpu.r[ 25 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R26:			sprintf( info->s = cpuintrf_temp_str(), "k0      :%08x", mipscpu.r[ 26 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R27:			sprintf( info->s = cpuintrf_temp_str(), "k1      :%08x", mipscpu.r[ 27 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R28:			sprintf( info->s = cpuintrf_temp_str(), "gp      :%08x", mipscpu.r[ 28 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R29:			sprintf( info->s = cpuintrf_temp_str(), "sp      :%08x", mipscpu.r[ 29 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R30:			sprintf( info->s = cpuintrf_temp_str(), "fp      :%08x", mipscpu.r[ 30 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_R31:			sprintf( info->s = cpuintrf_temp_str(), "ra      :%08x", mipscpu.r[ 31 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R0:			sprintf( info->s = cpuintrf_temp_str(), "Index   :%08x", mipscpu.cp0r[ 0 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R1:			sprintf( info->s = cpuintrf_temp_str(), "Random  :%08x", mipscpu.cp0r[ 1 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R2:			sprintf( info->s = cpuintrf_temp_str(), "EntryLo :%08x", mipscpu.cp0r[ 2 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R3:			sprintf( info->s = cpuintrf_temp_str(), "cp0r3   :%08x", mipscpu.cp0r[ 3 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R4:			sprintf( info->s = cpuintrf_temp_str(), "Context :%08x", mipscpu.cp0r[ 4 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R5:			sprintf( info->s = cpuintrf_temp_str(), "cp0r5   :%08x", mipscpu.cp0r[ 5 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R6:			sprintf( info->s = cpuintrf_temp_str(), "cp0r6   :%08x", mipscpu.cp0r[ 6 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R7:			sprintf( info->s = cpuintrf_temp_str(), "cp0r7   :%08x", mipscpu.cp0r[ 7 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R8:			sprintf( info->s = cpuintrf_temp_str(), "BadVAddr:%08x", mipscpu.cp0r[ 8 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R9:			sprintf( info->s = cpuintrf_temp_str(), "cp0r9   :%08x", mipscpu.cp0r[ 9 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R10:		sprintf( info->s = cpuintrf_temp_str(), "EntryHi :%08x", mipscpu.cp0r[ 10 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R11:		sprintf( info->s = cpuintrf_temp_str(), "cp0r11  :%08x", mipscpu.cp0r[ 11 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R12:		sprintf( info->s = cpuintrf_temp_str(), "SR      :%08x", mipscpu.cp0r[ 12 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R13:		sprintf( info->s = cpuintrf_temp_str(), "Cause   :%08x", mipscpu.cp0r[ 13 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R14:		sprintf( info->s = cpuintrf_temp_str(), "EPC     :%08x", mipscpu.cp0r[ 14 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R15:		sprintf( info->s = cpuintrf_temp_str(), "PRId    :%08x", mipscpu.cp0r[ 15 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R16:		sprintf( info->s = cpuintrf_temp_str(), "cp0r16  :%08x", mipscpu.cp0r[ 16 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R17:		sprintf( info->s = cpuintrf_temp_str(), "cp0r17  :%08x", mipscpu.cp0r[ 17 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R18:		sprintf( info->s = cpuintrf_temp_str(), "cp0r18  :%08x", mipscpu.cp0r[ 18 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R19:		sprintf( info->s = cpuintrf_temp_str(), "cp0r19  :%08x", mipscpu.cp0r[ 19 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R20:		sprintf( info->s = cpuintrf_temp_str(), "cp0r20  :%08x", mipscpu.cp0r[ 20 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R21:		sprintf( info->s = cpuintrf_temp_str(), "cp0r21  :%08x", mipscpu.cp0r[ 21 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R22:		sprintf( info->s = cpuintrf_temp_str(), "cp0r22  :%08x", mipscpu.cp0r[ 22 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R23:		sprintf( info->s = cpuintrf_temp_str(), "cp0r23  :%08x", mipscpu.cp0r[ 23 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R24:		sprintf( info->s = cpuintrf_temp_str(), "cp0r24  :%08x", mipscpu.cp0r[ 24 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R25:		sprintf( info->s = cpuintrf_temp_str(), "cp0r25  :%08x", mipscpu.cp0r[ 25 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R26:		sprintf( info->s = cpuintrf_temp_str(), "cp0r26  :%08x", mipscpu.cp0r[ 26 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R27:		sprintf( info->s = cpuintrf_temp_str(), "cp0r27  :%08x", mipscpu.cp0r[ 27 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R28:		sprintf( info->s = cpuintrf_temp_str(), "cp0r28  :%08x", mipscpu.cp0r[ 28 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R29:		sprintf( info->s = cpuintrf_temp_str(), "cp0r29  :%08x", mipscpu.cp0r[ 29 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R30:		sprintf( info->s = cpuintrf_temp_str(), "cp0r30  :%08x", mipscpu.cp0r[ 30 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP0R31:		sprintf( info->s = cpuintrf_temp_str(), "cp0r31  :%08x", mipscpu.cp0r[ 31 ] ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR0:		sprintf( info->s = cpuintrf_temp_str(), "vxy0    :%08x", mipscpu.cp2dr[ 0 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR1:		sprintf( info->s = cpuintrf_temp_str(), "vz0     :%08x", mipscpu.cp2dr[ 1 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR2:		sprintf( info->s = cpuintrf_temp_str(), "vxy1    :%08x", mipscpu.cp2dr[ 2 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR3:		sprintf( info->s = cpuintrf_temp_str(), "vz1     :%08x", mipscpu.cp2dr[ 3 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR4:		sprintf( info->s = cpuintrf_temp_str(), "vxy2    :%08x", mipscpu.cp2dr[ 4 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR5:		sprintf( info->s = cpuintrf_temp_str(), "vz2     :%08x", mipscpu.cp2dr[ 5 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR6:		sprintf( info->s = cpuintrf_temp_str(), "rgb     :%08x", mipscpu.cp2dr[ 6 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR7:		sprintf( info->s = cpuintrf_temp_str(), "otz     :%08x", mipscpu.cp2dr[ 7 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR8:		sprintf( info->s = cpuintrf_temp_str(), "ir0     :%08x", mipscpu.cp2dr[ 8 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR9:		sprintf( info->s = cpuintrf_temp_str(), "ir1     :%08x", mipscpu.cp2dr[ 9 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR10:		sprintf( info->s = cpuintrf_temp_str(), "ir2     :%08x", mipscpu.cp2dr[ 10 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR11:		sprintf( info->s = cpuintrf_temp_str(), "ir3     :%08x", mipscpu.cp2dr[ 11 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR12:		sprintf( info->s = cpuintrf_temp_str(), "sxy0    :%08x", mipscpu.cp2dr[ 12 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR13:		sprintf( info->s = cpuintrf_temp_str(), "sxy1    :%08x", mipscpu.cp2dr[ 13 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR14:		sprintf( info->s = cpuintrf_temp_str(), "sxy2    :%08x", mipscpu.cp2dr[ 14 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR15:		sprintf( info->s = cpuintrf_temp_str(), "sxyp    :%08x", mipscpu.cp2dr[ 15 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR16:		sprintf( info->s = cpuintrf_temp_str(), "sz0     :%08x", mipscpu.cp2dr[ 16 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR17:		sprintf( info->s = cpuintrf_temp_str(), "sz1     :%08x", mipscpu.cp2dr[ 17 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR18:		sprintf( info->s = cpuintrf_temp_str(), "sz2     :%08x", mipscpu.cp2dr[ 18 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR19:		sprintf( info->s = cpuintrf_temp_str(), "sz3     :%08x", mipscpu.cp2dr[ 19 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR20:		sprintf( info->s = cpuintrf_temp_str(), "rgb0    :%08x", mipscpu.cp2dr[ 20 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR21:		sprintf( info->s = cpuintrf_temp_str(), "rgb1    :%08x", mipscpu.cp2dr[ 21 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR22:		sprintf( info->s = cpuintrf_temp_str(), "rgb2    :%08x", mipscpu.cp2dr[ 22 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR23:		sprintf( info->s = cpuintrf_temp_str(), "res1    :%08x", mipscpu.cp2dr[ 23 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR24:		sprintf( info->s = cpuintrf_temp_str(), "mac0    :%08x", mipscpu.cp2dr[ 24 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR25:		sprintf( info->s = cpuintrf_temp_str(), "mac1    :%08x", mipscpu.cp2dr[ 25 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR26:		sprintf( info->s = cpuintrf_temp_str(), "mac2    :%08x", mipscpu.cp2dr[ 26 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR27:		sprintf( info->s = cpuintrf_temp_str(), "mac3    :%08x", mipscpu.cp2dr[ 27 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR28:		sprintf( info->s = cpuintrf_temp_str(), "irgb    :%08x", mipscpu.cp2dr[ 28 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR29:		sprintf( info->s = cpuintrf_temp_str(), "orgb    :%08x", mipscpu.cp2dr[ 29 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR30:		sprintf( info->s = cpuintrf_temp_str(), "lzcs    :%08x", mipscpu.cp2dr[ 30 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2DR31:		sprintf( info->s = cpuintrf_temp_str(), "lzcr    :%08x", mipscpu.cp2dr[ 31 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR0:		sprintf( info->s = cpuintrf_temp_str(), "r11r12  :%08x", mipscpu.cp2cr[ 0 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR1:		sprintf( info->s = cpuintrf_temp_str(), "r13r21  :%08x", mipscpu.cp2cr[ 1 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR2:		sprintf( info->s = cpuintrf_temp_str(), "r22r23  :%08x", mipscpu.cp2cr[ 2 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR3:		sprintf( info->s = cpuintrf_temp_str(), "r31r32  :%08x", mipscpu.cp2cr[ 3 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR4:		sprintf( info->s = cpuintrf_temp_str(), "r33     :%08x", mipscpu.cp2cr[ 4 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR5:		sprintf( info->s = cpuintrf_temp_str(), "trx     :%08x", mipscpu.cp2cr[ 5 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR6:		sprintf( info->s = cpuintrf_temp_str(), "try     :%08x", mipscpu.cp2cr[ 6 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR7:		sprintf( info->s = cpuintrf_temp_str(), "trz     :%08x", mipscpu.cp2cr[ 7 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR8:		sprintf( info->s = cpuintrf_temp_str(), "l11l12  :%08x", mipscpu.cp2cr[ 8 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR9:		sprintf( info->s = cpuintrf_temp_str(), "l13l21  :%08x", mipscpu.cp2cr[ 9 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR10:		sprintf( info->s = cpuintrf_temp_str(), "l22l23  :%08x", mipscpu.cp2cr[ 10 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR11:		sprintf( info->s = cpuintrf_temp_str(), "l31l32  :%08x", mipscpu.cp2cr[ 11 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR12:		sprintf( info->s = cpuintrf_temp_str(), "l33     :%08x", mipscpu.cp2cr[ 12 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR13:		sprintf( info->s = cpuintrf_temp_str(), "rbk     :%08x", mipscpu.cp2cr[ 13 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR14:		sprintf( info->s = cpuintrf_temp_str(), "gbk     :%08x", mipscpu.cp2cr[ 14 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR15:		sprintf( info->s = cpuintrf_temp_str(), "bbk     :%08x", mipscpu.cp2cr[ 15 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR16:		sprintf( info->s = cpuintrf_temp_str(), "lr1lr2  :%08x", mipscpu.cp2cr[ 16 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR17:		sprintf( info->s = cpuintrf_temp_str(), "lr31g1  :%08x", mipscpu.cp2cr[ 17 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR18:		sprintf( info->s = cpuintrf_temp_str(), "lg2lg3  :%08x", mipscpu.cp2cr[ 18 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR19:		sprintf( info->s = cpuintrf_temp_str(), "lb1lb2  :%08x", mipscpu.cp2cr[ 19 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR20:		sprintf( info->s = cpuintrf_temp_str(), "lb3     :%08x", mipscpu.cp2cr[ 20 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR21:		sprintf( info->s = cpuintrf_temp_str(), "rfc     :%08x", mipscpu.cp2cr[ 21 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR22:		sprintf( info->s = cpuintrf_temp_str(), "gfc     :%08x", mipscpu.cp2cr[ 22 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR23:		sprintf( info->s = cpuintrf_temp_str(), "bfc     :%08x", mipscpu.cp2cr[ 23 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR24:		sprintf( info->s = cpuintrf_temp_str(), "ofx     :%08x", mipscpu.cp2cr[ 24 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR25:		sprintf( info->s = cpuintrf_temp_str(), "ofy     :%08x", mipscpu.cp2cr[ 25 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR26:		sprintf( info->s = cpuintrf_temp_str(), "h       :%08x", mipscpu.cp2cr[ 26 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR27:		sprintf( info->s = cpuintrf_temp_str(), "dqa     :%08x", mipscpu.cp2cr[ 27 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR28:		sprintf( info->s = cpuintrf_temp_str(), "dqb     :%08x", mipscpu.cp2cr[ 28 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR29:		sprintf( info->s = cpuintrf_temp_str(), "zsf3    :%08x", mipscpu.cp2cr[ 29 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR30:		sprintf( info->s = cpuintrf_temp_str(), "zsf4    :%08x", mipscpu.cp2cr[ 30 ].d ); break;
+		case CPUINFO_STR_REGISTER + MIPS_CP2CR31:		sprintf( info->s = cpuintrf_temp_str(), "flag    :%08x", mipscpu.cp2cr[ 31 ].d ); break;
+#endif
+	}
+}
+
+uint32 mips_get_cause(void)
+{
+	return mipscpu.cp0r[ CP0_CAUSE ];
+}
+
+uint32 mips_get_status(void)
+{
+	return mipscpu.cp0r[ CP0_SR ];
+}
+
+void mips_set_status(uint32 status)
+{
+	mipscpu.cp0r[ CP0_SR ] = status;
+}
+
+uint32 mips_get_ePC(void)
+{
+	return mipscpu.cp0r[ CP0_EPC ];
+}
+
+int mips_get_icount(void)
+{
+	return mips_ICount;
+}
+
+void mips_set_icount(int count)
+{
+	mips_ICount = count;
+}
+
+
+#if (HAS_PSXCPU)
+/**************************************************************************
+ * CPU-specific set_info
+ **************************************************************************/
+
+void psxcpu_get_info(UINT32 state, union cpuinfo *info)
+{
+	switch (state)
+	{
+		/* --- the following bits of info are returned as NULL-terminated strings --- */
+//		case CPUINFO_STR_NAME:							strcpy(info->s = cpuintrf_temp_str(), "PSX CPU"); break;
+
+		default:
+			mips_get_info(state, info);
+			break;
+	}
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/psx.h	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,288 @@
+#ifndef _MIPS_H
+#define _MIPS_H
+
+#include "ao.h"
+//#include "driver.h"
+
+typedef void genf(void);
+typedef int offs_t;
+
+#define cpu_readop32(pc) program_read_dword_32le(pc)
+#define change_pc(pc)																	\
+
+
+#ifdef __GNUC__
+#if (__GNUC__ < 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ <= 7))
+#define UNUSEDARG
+#else
+#define UNUSEDARG __attribute__((__unused__))
+#endif
+#else
+#define UNUSEDARG
+#endif
+
+typedef int8		(*read8_handler)  (UNUSEDARG offs_t offset);
+typedef void		(*write8_handler) (UNUSEDARG offs_t offset, UNUSEDARG int8 data);
+typedef int16		(*read16_handler) (UNUSEDARG offs_t offset, UNUSEDARG int16 mem_mask);
+typedef void		(*write16_handler)(UNUSEDARG offs_t offset, UNUSEDARG int16 data, UNUSEDARG int16 mem_mask);
+typedef int32		(*read32_handler) (UNUSEDARG offs_t offset, UNUSEDARG int32 mem_mask);
+typedef void		(*write32_handler)(UNUSEDARG offs_t offset, UNUSEDARG int32 data, UNUSEDARG int32 mem_mask);
+typedef int64		(*read64_handler) (UNUSEDARG offs_t offset, UNUSEDARG int64 mem_mask);
+typedef void		(*write64_handler)(UNUSEDARG offs_t offset, UNUSEDARG int64 data, UNUSEDARG int64 mem_mask);
+
+union read_handlers_t
+{
+	genf *				handler;
+	read8_handler		handler8;
+	read16_handler		handler16;
+	read32_handler		handler32;
+	read64_handler		handler64;
+};
+
+union write_handlers_t
+{
+	genf *				handler;
+	write8_handler		handler8;
+	write16_handler		handler16;
+	write32_handler		handler32;
+	write64_handler		handler64;
+};
+
+struct address_map_t
+{
+	uint32				flags;				/* flags and additional info about this entry */
+	offs_t				start, end;			/* start/end (or mask/match) values */
+	offs_t				mirror;				/* mirror bits */
+	offs_t				mask;				/* mask bits */
+	union read_handlers_t read;				/* read handler callback */
+	union write_handlers_t write;			/* write handler callback */
+	void *				memory;				/* pointer to memory backing this entry */
+	uint32				share;				/* index of a shared memory block */
+	void **				base;				/* receives pointer to memory (optional) */
+	size_t *			size;				/* receives size of area in bytes (optional) */
+};
+typedef struct address_map_t *(*construct_map_t)(struct address_map_t *map);
+
+union cpuinfo
+{
+	int64	i;											/* generic integers */
+	void *	p;											/* generic pointers */
+	genf *  f;											/* generic function pointers */
+	char *	s;											/* generic strings */
+
+	void	(*setinfo)(UINT32 state, union cpuinfo *info);/* CPUINFO_PTR_SET_INFO */
+	void	(*getcontext)(void *context);				/* CPUINFO_PTR_GET_CONTEXT */
+	void	(*setcontext)(void *context);				/* CPUINFO_PTR_SET_CONTEXT */
+	void	(*init)(void);								/* CPUINFO_PTR_INIT */
+	void	(*reset)(void *param);						/* CPUINFO_PTR_RESET */
+	void	(*exit)(void);								/* CPUINFO_PTR_EXIT */
+	int		(*execute)(int cycles);						/* CPUINFO_PTR_EXECUTE */
+	void	(*burn)(int cycles);						/* CPUINFO_PTR_BURN */
+	offs_t	(*disassemble)(char *buffer, offs_t pc);	/* CPUINFO_PTR_DISASSEMBLE */
+	int		(*irqcallback)(int state);					/* CPUINFO_PTR_IRQCALLBACK */
+	int *	icount;										/* CPUINFO_PTR_INSTRUCTION_COUNTER */
+	construct_map_t internal_map;						/* CPUINFO_PTR_INTERNAL_MEMORY_MAP */
+};
+
+enum
+{
+	MIPS_PC = 1,
+	MIPS_DELAYV, MIPS_DELAYR,
+	MIPS_HI, MIPS_LO,
+	MIPS_R0, MIPS_R1,
+	MIPS_R2, MIPS_R3,
+	MIPS_R4, MIPS_R5,
+	MIPS_R6, MIPS_R7,
+	MIPS_R8, MIPS_R9,
+	MIPS_R10, MIPS_R11,
+	MIPS_R12, MIPS_R13,
+	MIPS_R14, MIPS_R15,
+	MIPS_R16, MIPS_R17,
+	MIPS_R18, MIPS_R19,
+	MIPS_R20, MIPS_R21,
+	MIPS_R22, MIPS_R23,
+	MIPS_R24, MIPS_R25,
+	MIPS_R26, MIPS_R27,
+	MIPS_R28, MIPS_R29,
+	MIPS_R30, MIPS_R31,
+	MIPS_CP0R0, MIPS_CP0R1,
+	MIPS_CP0R2, MIPS_CP0R3,
+	MIPS_CP0R4, MIPS_CP0R5,
+	MIPS_CP0R6, MIPS_CP0R7,
+	MIPS_CP0R8, MIPS_CP0R9,
+	MIPS_CP0R10, MIPS_CP0R11,
+	MIPS_CP0R12, MIPS_CP0R13,
+	MIPS_CP0R14, MIPS_CP0R15,
+	MIPS_CP0R16, MIPS_CP0R17,
+	MIPS_CP0R18, MIPS_CP0R19,
+	MIPS_CP0R20, MIPS_CP0R21,
+	MIPS_CP0R22, MIPS_CP0R23,
+	MIPS_CP0R24, MIPS_CP0R25,
+	MIPS_CP0R26, MIPS_CP0R27,
+	MIPS_CP0R28, MIPS_CP0R29,
+	MIPS_CP0R30, MIPS_CP0R31,
+	MIPS_CP2DR0, MIPS_CP2DR1,
+	MIPS_CP2DR2, MIPS_CP2DR3,
+	MIPS_CP2DR4, MIPS_CP2DR5,
+	MIPS_CP2DR6, MIPS_CP2DR7,
+	MIPS_CP2DR8, MIPS_CP2DR9,
+	MIPS_CP2DR10, MIPS_CP2DR11,
+	MIPS_CP2DR12, MIPS_CP2DR13,
+	MIPS_CP2DR14, MIPS_CP2DR15,
+	MIPS_CP2DR16, MIPS_CP2DR17,
+	MIPS_CP2DR18, MIPS_CP2DR19,
+	MIPS_CP2DR20, MIPS_CP2DR21,
+	MIPS_CP2DR22, MIPS_CP2DR23,
+	MIPS_CP2DR24, MIPS_CP2DR25,
+	MIPS_CP2DR26, MIPS_CP2DR27,
+	MIPS_CP2DR28, MIPS_CP2DR29,
+	MIPS_CP2DR30, MIPS_CP2DR31,
+	MIPS_CP2CR0, MIPS_CP2CR1,
+	MIPS_CP2CR2, MIPS_CP2CR3,
+	MIPS_CP2CR4, MIPS_CP2CR5,
+	MIPS_CP2CR6, MIPS_CP2CR7,
+	MIPS_CP2CR8, MIPS_CP2CR9,
+	MIPS_CP2CR10, MIPS_CP2CR11,
+	MIPS_CP2CR12, MIPS_CP2CR13,
+	MIPS_CP2CR14, MIPS_CP2CR15,
+	MIPS_CP2CR16, MIPS_CP2CR17,
+	MIPS_CP2CR18, MIPS_CP2CR19,
+	MIPS_CP2CR20, MIPS_CP2CR21,
+	MIPS_CP2CR22, MIPS_CP2CR23,
+	MIPS_CP2CR24, MIPS_CP2CR25,
+	MIPS_CP2CR26, MIPS_CP2CR27,
+	MIPS_CP2CR28, MIPS_CP2CR29,
+	MIPS_CP2CR30, MIPS_CP2CR31
+};
+
+#define MIPS_INT_NONE	( -1 )
+
+#define MIPS_IRQ0	( 0 )
+#define MIPS_IRQ1	( 1 )
+#define MIPS_IRQ2	( 2 )
+#define MIPS_IRQ3	( 3 )
+#define MIPS_IRQ4	( 4 )
+#define MIPS_IRQ5	( 5 )
+
+#define MIPS_BYTE_EXTEND( a ) ( (INT32)(INT8)a )
+#define MIPS_WORD_EXTEND( a ) ( (INT32)(INT16)a )
+
+#define INS_OP( op ) ( ( op >> 26 ) & 63 )
+#define INS_RS( op ) ( ( op >> 21 ) & 31 )
+#define INS_RT( op ) ( ( op >> 16 ) & 31 )
+#define INS_IMMEDIATE( op ) ( op & 0xffff )
+#define INS_TARGET( op ) ( op & 0x3ffffff )
+#define INS_RD( op ) ( ( op >> 11 ) & 31 )
+#define INS_SHAMT( op ) ( ( op >> 6 ) & 31 )
+#define INS_FUNCT( op ) ( op & 63 )
+#define INS_CODE( op ) ( ( op >> 6 ) & 0xfffff )
+#define INS_CO( op ) ( ( op >> 25 ) & 1 )
+#define INS_COFUN( op ) ( op & 0x1ffffff )
+#define INS_CF( op ) ( op & 63 )
+
+#define GTE_OP( op ) ( ( op >> 20 ) & 31 )
+#define GTE_SF( op ) ( ( op >> 19 ) & 1 )
+#define GTE_MX( op ) ( ( op >> 17 ) & 3 )
+#define GTE_V( op ) ( ( op >> 15 ) & 3 )
+#define GTE_CV( op ) ( ( op >> 13 ) & 3 )
+#define GTE_CD( op ) ( ( op >> 11 ) & 3 ) /* not used */
+#define GTE_LM( op ) ( ( op >> 10 ) & 1 )
+#define GTE_CT( op ) ( ( op >> 6 ) & 15 ) /* not used */
+#define GTE_FUNCT( op ) ( op & 63 )
+
+#define OP_SPECIAL ( 0 )
+#define OP_REGIMM ( 1 )
+#define OP_J ( 2 )
+#define OP_JAL ( 3 )
+#define OP_BEQ ( 4 )
+#define OP_BNE ( 5 )
+#define OP_BLEZ ( 6 )
+#define OP_BGTZ ( 7 )
+#define OP_ADDI ( 8 )
+#define OP_ADDIU ( 9 )
+#define OP_SLTI ( 10 )
+#define OP_SLTIU ( 11 )
+#define OP_ANDI ( 12 )
+#define OP_ORI ( 13 )
+#define OP_XORI ( 14 )
+#define OP_LUI ( 15 )
+#define OP_COP0 ( 16 )
+#define OP_COP1 ( 17 )
+#define OP_COP2 ( 18 )
+#define OP_LB ( 32 )
+#define OP_LH ( 33 )
+#define OP_LWL ( 34 )
+#define OP_LW ( 35 )
+#define OP_LBU ( 36 )
+#define OP_LHU ( 37 )
+#define OP_LWR ( 38 )
+#define OP_SB ( 40 )
+#define OP_SH ( 41 )
+#define OP_SWL ( 42 )
+#define OP_SW ( 43 )
+#define OP_SWR ( 46 )
+#define OP_LWC1 ( 49 )
+#define OP_LWC2 ( 50 )
+#define OP_SWC1 ( 57 )
+#define OP_SWC2 ( 58 )
+
+/* OP_SPECIAL */
+#define FUNCT_SLL ( 0 )
+#define FUNCT_SRL ( 2 )
+#define FUNCT_SRA ( 3 )
+#define FUNCT_SLLV ( 4 )
+#define FUNCT_SRLV ( 6 )
+#define FUNCT_SRAV ( 7 )
+#define FUNCT_JR ( 8 )
+#define FUNCT_JALR ( 9 )	   
+#define FUNCT_HLECALL ( 11 )
+#define FUNCT_SYSCALL ( 12 )
+#define FUNCT_BREAK ( 13 )
+#define FUNCT_MFHI ( 16 )
+#define FUNCT_MTHI ( 17 )
+#define FUNCT_MFLO ( 18 )
+#define FUNCT_MTLO ( 19 )
+#define FUNCT_MULT ( 24 )
+#define FUNCT_MULTU ( 25 )
+#define FUNCT_DIV ( 26 )
+#define FUNCT_DIVU ( 27 )
+#define FUNCT_ADD ( 32 )
+#define FUNCT_ADDU ( 33 )
+#define FUNCT_SUB ( 34 )
+#define FUNCT_SUBU ( 35 )
+#define FUNCT_AND ( 36 )
+#define FUNCT_OR ( 37 )
+#define FUNCT_XOR ( 38 )
+#define FUNCT_NOR ( 39 )
+#define FUNCT_SLT ( 42 )
+#define FUNCT_SLTU ( 43 )
+
+/* OP_REGIMM */
+#define RT_BLTZ ( 0 )
+#define RT_BGEZ ( 1 )
+#define RT_BLTZAL ( 16 )
+#define RT_BGEZAL ( 17 )
+
+/* OP_COP0/OP_COP1/OP_COP2 */
+#define RS_MFC ( 0 )
+#define RS_CFC ( 2 )
+#define RS_MTC ( 4 )
+#define RS_CTC ( 6 )
+#define RS_BC ( 8 )
+
+/* RS_BC */
+#define RT_BCF ( 0 )
+#define RT_BCT ( 1 )
+
+/* OP_COP0 */
+#define CF_RFE ( 16 )
+
+#ifdef MAME_DEBUG
+extern unsigned DasmMIPS(char *buff, unsigned _pc);
+#endif
+
+#if (HAS_PSXCPU)
+extern void psxcpu_get_info(UINT32 state, union cpuinfo *info);
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/psf2/psx_hw.c	Tue Jul 01 21:13:48 2008 +0300
@@ -0,0 +1,3540 @@
+/*
+	Audio Overload SDK - PSX and IOP hardware emulation
+
+	Copyright (c) 2007 R. Belmont and Richard Bannister.
+
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+	* Neither the names of R. Belmont and Richard Bannister nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+    psx_hw.c - Minimal PSX/IOP hardware glue/emulation/whatever
+
+    supported: main RAM (2 MB, mirrored to fill an 8 MB space like on real HW)
+               DMA channel 4 (SPURAM) in both directions (including completion IRQ)
+	       VBL IRQ
+	       Root counters 2 and 3 including completion events and IRQs
+	       Some BIOS services including exception handling (via HLE)
+	       HLE emulation of IOP operating system, including multithreading
+	       SPU(2), SPU(2)RAM (via PEOpS)
+
+
+
+    Special notes:
+    PSF1
+    	- Chocobo's Dungeon 2 contains an illegal code sequence (patched)
+
+    PSF2
+	- Shadow Hearts assumes that the wave buffer alloc will go to 0x80060000 and the sequence buffer to 0x80170000.
+	  Our memory management doesn't work out that way, so we have to (wait for it) cheese it.
+*/
+
+#include <stdio.h>
+#include "ao.h"
+#include "cpuintrf.h"
+#include "psx.h"
+	
+#define DEBUG_HLE_BIOS	(0)		// debug PS1 HLE BIOS
+#define DEBUG_HLE_IOP	(0)		// debug PS2 IOP OS calls
+#define DEBUG_UNK_RW	(0)		// debug unknown reads/writes
+#define DEBUG_THREADING (0)		// debug PS2 IOP threading
+
+extern void mips_get_info(UINT32 state, union cpuinfo *info);
+extern void mips_set_info(UINT32 state, union cpuinfo *info);
+extern int psxcpu_verbose;
+extern uint16 SPUreadRegister(uint32 reg);
+extern void SPUwriteRegister(uint32 reg, uint16 val);
+extern void SPUwriteDMAMem(uint32 usPSXMem,int iSize);
+extern void SPUreadDMAMem(uint32 usPSXMem,int iSize);
+extern void mips_shorten_frame(void);
+extern int mips_execute( int cycles );
+extern uint32 psf2_load_file(char *file, uint8 *buf, uint32 buflen);
+extern uint32 psf2_load_elf(uint8 *start, uint32 len);
+void psx_hw_runcounters(void);
+int mips_get_icount(void);
+void mips_set_icount(int count);
+
+extern int psf_refresh;
+
+static int skipyet = 0;
+
+// SPU2
+extern void SPU2write(unsigned long reg, unsigned short val);
+extern unsigned short SPU2read(unsigned long reg);
+extern void SPU2readDMA4Mem(uint32 usPSXMem,int iSize);
+extern void SPU2writeDMA4Mem(uint32 usPSXMem,int iSize);
+extern void SPU2readDMA7Mem(uint32 usPSXMem,int iSize);
+extern void SPU2writeDMA7Mem(uint32 usPSXMem,int iSize);
+extern void SPU2interruptDMA4(void);
+extern void SPU2interruptDMA7(void);
+
+#define MAX_FILE_SLOTS	(32)
+
+static volatile int softcall_target = 0;
+static int filestat[MAX_FILE_SLOTS];
+static uint8 *filedata[MAX_FILE_SLOTS];
+static uint32 filesize[MAX_FILE_SLOTS], filepos[MAX_FILE_SLOTS];
+uint32 psf2_get_loadaddr(void);
+void psf2_set_loadaddr(uint32 new);
+static void call_irq_routine(uint32 routine, uint32 parameter);
+static int intr_susp = 0;
+
+static uint64 sys_time;
+static int timerexp = 0;
+
+typedef struct
+{
+	char name[10];
+	uint32 dispatch;
+} ExternLibEntries;
+
+static int32 iNumLibs;
+static ExternLibEntries	reglibs[32];
+
+typedef struct
+{
+	uint32 type;
+	uint32 value;
+	uint32 param;
+	int    inUse;
+} EventFlag;
+
+static int32 iNumFlags;
+static EventFlag evflags[32];
+
+typedef struct
+{
+	uint32 attr;
+	uint32 option;
+	int32 init;
+	int32 current;
+	int32 max;
+	int32 threadsWaiting;
+	int32 inuse;
+} Semaphore;
+
+#define SEMA_MAX	(64)
+
+static int32 iNumSema;
+static Semaphore semaphores[SEMA_MAX];
+
+// thread states
+enum
+{
+	TS_RUNNING = 0,		// now running
+	TS_READY,		// ready to run
+	TS_WAITEVFLAG,		// waiting on an event flag
+	TS_WAITSEMA,		// waiting on a semaphore
+	TS_WAITDELAY,		// waiting on a time delay
+	TS_SLEEPING,		// sleeping
+	TS_CREATED,		// newly created, hasn't run yet
+
+	TS_MAXSTATE
+};
+
+typedef struct
+{
+	int32  iState;		// state of thread
+
+	uint32 flags;		// flags
+	uint32 routine;		// start of code for the thread
+	uint32 stackloc;	// stack location in IOP RAM
+	uint32 stacksize;	// stack size
+	uint32 refCon;		// user value passed in at CreateThread time
+
+	uint32 waitparm;	// what we're waiting on if in one the TS_WAIT* states
+	
+	uint32 save_regs[37];	// CPU registers belonging to this thread
+} Thread;
+
+static int32 iNumThreads, iCurThread;
+static Thread threads[32];
+
+#if DEBUG_THREADING
+static char *_ThreadStateNames[TS_MAXSTATE] = { "RUNNING", "READY", "WAITEVFLAG", "WAITSEMA", "WAITDELAY", "SLEEPING", "CREATED" };
+#endif
+
+#if DEBUG_HLE_IOP
+static char *seek_types[3] = { "SEEK_SET", "SEEK_CUR", "SEEK_END" };
+#endif
+
+typedef struct
+{
+	int32  iActive;
+	uint32 count;
+	uint32 target;
+	uint32 source;
+	uint32 prescale;
+	uint32 handler;
+	uint32 hparam;
+	uint32 mode;
+} IOPTimer;
+
+static IOPTimer iop_timers[8];
+static int32 iNumTimers;
+
+typedef struct
+{
+	uint32 count;
+	uint32 mode;
+	uint32 target;
+	uint32 sysclock;
+} Counter;
+
+static Counter root_cnts[3];	// 3 of the bastards
+
+#define CLOCK_DIV	(8)	// 33 MHz / this = what we run the R3000 at to keep the CPU usage not insane
+
+// counter modes
+#define RC_EN		(0x0001)	// halt
+#define RC_RESET	(0x0008)	// automatically wrap
+#define RC_IQ1		(0x0010)	// IRQ when target reached
+#define RC_IQ2		(0x0040)	// IRQ when target reached (pSX treats same as IQ1?)
+#define RC_CLC		(0x0100)	// counter uses direct system clock
+#define RC_DIV8		(0x0200)	// (counter 2 only) system clock/8
+
+typedef struct
+{
+	uint32 desc;
+	int32 status;
+	int32 mode;
+	uint32 fhandler;
+} EvtCtrlBlk[32];
+
+static EvtCtrlBlk *Event;
+static EvtCtrlBlk *CounterEvent;
+
+// Sony event states
+#define EvStUNUSED	0x0000
+#define EvStWAIT	0x1000
+#define EvStACTIVE	0x2000
+#define EvStALREADY 	0x4000
+
+// Sony event modes
+#define EvMdINTR	0x1000
+#define EvMdNOINTR	0x2000
+
+// PSX main RAM
+uint32 psx_ram[(2*1024*1024)/4];
+uint32 psx_scratch[0x400];
+// backup image to restart songs
+uint32 initial_ram[(2*1024*1024)/4];
+uint32 initial_scratch[0x400];
+
+static uint32 spu_delay, dma_icr, irq_data, irq_mask, dma_timer, WAI;
+static uint32 dma4_madr, dma4_bcr, dma4_chcr, dma4_delay;
+static uint32 dma7_madr, dma7_bcr, dma7_chcr, dma7_delay;
+static uint32 dma4_cb, dma7_cb, dma4_fval, dma4_flag, dma7_fval, dma7_flag;
+static uint32 irq9_cb, irq9_fval, irq9_flag;
+
+// take a snapshot of the CPU state for a thread
+static void FreezeThread(int32 iThread, int flag)
+{
+	int i;
+	union cpuinfo mipsinfo;
+
+	#if DEBUG_THREADING
+//	printf("IOP: FreezeThread(%d)\n", iThread);
+	#endif
+
+	for (i = 0; i < 32; i++)
+	{
+		mips_get_info(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
+		threads[iThread].save_regs[i] = mipsinfo.i;
+	}
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+	threads[iThread].save_regs[32] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+	threads[iThread].save_regs[33] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
+	threads[iThread].save_regs[35] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
+	threads[iThread].save_regs[36] = mipsinfo.i;
+
+
+	// if a thread is freezing itself due to a IOP syscall, we must save the RA as the PC
+	// to come back to or else the syscall will recurse	
+	if (flag)
+	{
+		mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+	}
+	else
+	{
+		mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+	}
+	threads[iThread].save_regs[34] = mipsinfo.i;
+
+	#if DEBUG_THREADING
+	{
+		char buffer[256];
+
+		DasmMIPS(buffer, mipsinfo.i, &psx_ram[(mipsinfo.i & 0x7fffffff)/4]);
+
+		printf("IOP: FreezeThread(%d) => %08x  [%s]\n", iThread, threads[iThread].save_regs[34], buffer);
+	}
+	#endif
+
+	// if thread was running, now it's ready
+	if (threads[iThread].iState == TS_RUNNING)
+	{
+		threads[iThread].iState = TS_READY;
+	}
+}
+
+// restore the CPU state from a thread's snapshot
+static void ThawThread(int32 iThread)
+{
+	int i;
+	union cpuinfo mipsinfo;
+
+	// the first time a thread is put on the CPU,
+	// some special setup is required
+	if (threads[iThread].iState == TS_CREATED)
+	{
+		// PC = starting routine
+		threads[iThread].save_regs[34] = threads[iThread].routine-4;	// compensate for weird delay slot effects
+		// SP = thread's stack area
+		threads[iThread].save_regs[29] = (threads[iThread].stackloc + threads[iThread].stacksize) - 16;
+		threads[iThread].save_regs[29] |= 0x80000000;
+
+		threads[iThread].save_regs[35] = threads[iThread].save_regs[36] = 0;
+
+		#if DEBUG_THREADING
+//		printf("IOP: Initial setup for thread %d => PC %x SP %x\n", iThread, threads[iThread].save_regs[34]+4, threads[iThread].save_regs[29]);
+		#endif
+	}
+											     
+	#if DEBUG_THREADING
+	{
+		char buffer[256];
+
+		mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+		DasmMIPS(buffer, mipsinfo.i, &psx_ram[(mipsinfo.i & 0x7fffffff)/4]);
+
+		printf("IOP: ThawThread(%d) => %08x  [%s] (wake %d)\n", iThread, threads[iThread].save_regs[34], buffer, wakecount);
+	}
+	#endif
+
+	for (i = 0; i < 32; i++)
+	{
+		mipsinfo.i = threads[iThread].save_regs[i];
+		mips_set_info(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
+	}
+
+	mipsinfo.i = threads[iThread].save_regs[32];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+	mipsinfo.i = threads[iThread].save_regs[33];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+	mipsinfo.i = threads[iThread].save_regs[34];
+	mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+	mipsinfo.i = threads[iThread].save_regs[35];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
+	mipsinfo.i = threads[iThread].save_regs[36];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
+
+	threads[iThread].iState = TS_RUNNING;
+}
+
+// find a new thread to run
+static void ps2_reschedule(void)
+{
+	int i, starti, iNextThread;
+
+	iNextThread = -1;
+
+	// see if any thread other than the current one is ready to run
+	i = iCurThread+1;
+	if (i >= iNumThreads)
+	{
+		i = 0;
+	}
+
+	starti = i;
+
+	// starting with the next thread after this one,
+	// see who wants to run
+	while (i < iNumThreads)
+	{
+		if (i != iCurThread)
+		{
+			if (threads[i].iState == TS_READY)
+			{
+			  	iNextThread = i;
+				break;
+			}
+		}
+
+		i++;
+	}
+
+	// if we started above thread 0 and didn't pick one,
+	// go around and try from zero
+	if ((starti > 0) && (iNextThread == -1))
+	{
+		for (i = 0; i < iNumThreads; i++)
+		{
+			if (i != iCurThread)
+			{
+				if (threads[i].iState == TS_READY)
+				{
+				  	iNextThread = i;
+					break;
+				}
+			}
+		}
+	}
+
+	if (iNextThread != -1)
+	{
+		#if DEBUG_THREADING
+		for (i = 0; i < iNumThreads; i++)
+		{
+			printf("Thread %02d: %s\n", i, _ThreadStateNames[threads[i].iState]);
+		}
+		#endif
+
+		if (iCurThread != -1)
+		{
+			FreezeThread(iCurThread, 0);
+		}
+		ThawThread(iNextThread);
+		iCurThread = iNextThread;
+		threads[iCurThread].iState = TS_RUNNING;
+	}
+	else
+	{
+		// no thread to switch to, is the current one still running?
+		if (iCurThread != -1)
+		{
+			if (threads[iCurThread].iState != TS_RUNNING)
+			{
+				#if DEBUG_THREADING
+				printf("IOP: no threads to run\n");
+
+				for (i = 0; i < iNumThreads; i++)
+				{
+					printf("Thread %02d: %s\n", i, _ThreadStateNames[threads[i].iState]);
+				}
+				#endif
+
+				mips_shorten_frame();	// kill the CPU
+				iCurThread = -1;	// no threads are active
+			}
+		}
+		else
+		{
+			mips_shorten_frame();	// kill the CPU
+			iCurThread = -1;	// no threads are active
+		}
+	}
+}
+
+static void psx_irq_update(void)
+{
+	union cpuinfo mipsinfo;
+
+	if ((irq_data & irq_mask) != 0)
+	{	// assert the line
+		WAI = 0;
+		mipsinfo.i = ASSERT_LINE;
+		mips_set_info( CPUINFO_INT_INPUT_STATE + MIPS_IRQ0, &mipsinfo );
+	}
+	else
+	{
+		// clear the line
+		mipsinfo.i = CLEAR_LINE;
+		mips_set_info( CPUINFO_INT_INPUT_STATE + MIPS_IRQ0, &mipsinfo );
+	}
+}
+
+void psx_irq_set(uint32 irq)
+{
+	irq_data |= irq;
+
+	psx_irq_update();
+}
+
+static uint32 gpu_stat = 0;
+
+uint32 psx_hw_read(offs_t offset, uint32 mem_mask)
+{
+	if (offset >= 0x00000000 && offset <= 0x007fffff)
+	{
+		offset &= 0x1fffff;
+		return LE32(psx_ram[offset>>2]);
+	}
+
+	if (offset >= 0x80000000 && offset <= 0x807fffff)
+	{
+		offset &= 0x1fffff;
+		return LE32(psx_ram[offset>>2]);
+	}
+
+	if (offset == 0xbfc00180 || offset == 0xbfc00184)	// exception vector
+	{
+		return FUNCT_HLECALL;
+	}	
+
+	if (offset == 0x1f801014)
+	{
+		return spu_delay;
+	}
+
+	if (offset == 0xbf801014)
+	{
+		return spu_delay;
+	}
+
+	if (offset == 0x1f801814)
+	{
+		gpu_stat ^= 0xffffffff;
+		return gpu_stat;
+	}
+
+	if (offset >= 0x1f801c00 && offset <= 0x1f801dff)
+	{
+		if ((mem_mask == 0xffff0000) || (mem_mask == 0xffffff00))
+		{
+			return SPUreadRegister(offset) & ~mem_mask;
+		}
+		else if (mem_mask == 0x0000ffff)
+		{
+			return SPUreadRegister(offset)<<16;
+		}
+		else printf("SPU: read unknown mask %08x\n", mem_mask);
+	}
+
+	if (offset >= 0xbf900000 && offset <= 0xbf9007ff)
+	{
+		if ((mem_mask == 0xffff0000) || (mem_mask == 0xffffff00))
+		{
+			return SPU2read(offset) & ~mem_mask;
+		}
+		else if (mem_mask == 0x0000ffff)
+		{
+			return SPU2read(offset)<<16;
+		}
+		else if (mem_mask == 0)
+		{
+			return SPU2read(offset) | SPU2read(offset+2)<<16;
+		}
+		else printf("SPU2: read unknown mask %08x\n", mem_mask);
+	}
+
+	if (offset >= 0x1f801100 && offset <= 0x1f801128)
+	{
+		int cnt = (offset>>4) & 0xf;
+		
+		switch (offset & 0xf)
+		{
+			case 0:
+//				printf("RC: read counter %d count = %x\n", cnt, root_cnts[cnt].count);
+				return root_cnts[cnt].count;
+				break;
+			case 4:
+//				printf("RC: read counter %d mode\n", cnt);
+				return root_cnts[cnt].mode;
+				break;
+			case 8:
+//				printf("RC: read counter %d target\n", cnt);
+				return root_cnts[cnt].target;
+				break;
+		}
+
+		return 0;
+	}
+
+	if (offset == 0x1f8010f4)
+	{
+		return dma_icr;
+	}
+	else if (offset == 0x1f801070)
+	{
+//		printf("Read IRQ_data %x (mask %08x)\n", irq_data, mem_mask);
+		return irq_data;
+	}
+	else if (offset == 0x1f801074)
+	{
+		return irq_mask;
+	}
+
+/*	if (offset == 0xbf801508)
+	{
+		return dma7_bcr;
+	}*/
+
+	if (offset == 0xbf920344)
+	{
+		return 0x80808080;
+	}
+
+	#if DEBUG_UNK_RW
+	{
+		union cpuinfo mipsinfo;
+
+		mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+		printf("Unknown read: %08x, mask %08x (PC=%x)\n", offset&~3, mem_mask, mipsinfo.i);
+	}
+	#endif
+	return 0;
+}
+
+static void psx_dma4(uint32 madr, uint32 bcr, uint32 chcr)
+{
+	if (chcr == 0x01000201)	// cpu to SPU
+	{
+//		printf("DMA4: RAM %08x to SPU\n", madr);
+		bcr = (bcr>>16) * (bcr & 0xffff) * 2;
+		SPUwriteDMAMem(madr&0x1fffff, bcr);
+	}
+	else
+	{
+//		printf("DMA4: SPU to RAM %08x\n", madr);
+		bcr = (bcr>>16) * (bcr & 0xffff) * 2;
+		SPUreadDMAMem(madr&0x1fffff, bcr);
+	}
+}
+
+static void ps2_dma4(uint32 madr, uint32 bcr, uint32 chcr)
+{
+	if (chcr == 0x01000201)	// cpu to SPU2
+	{
+		#if DEBUG_HLE_IOP
+		printf("DMA4: RAM %08x to SPU2\n", madr);
+		#endif
+		bcr = (bcr>>16) * (bcr & 0xffff) * 4;
+		SPU2writeDMA4Mem(madr&0x1fffff, bcr);
+	}
+	else
+	{
+		#if DEBUG_HLE_IOP
+		printf("DMA4: SPU2 to RAM %08x\n", madr);
+		#endif
+		bcr = (bcr>>16) * (bcr & 0xffff) * 4;
+		SPU2readDMA4Mem(madr&0x1fffff, bcr);
+	}
+
+	dma4_delay = 80;
+}
+
+static void ps2_dma7(uint32 madr, uint32 bcr, uint32 chcr)
+{
+	if ((chcr == 0x01000201) || (chcr == 0x00100010) || (chcr == 0x000f0010) || (chcr == 0x00010010))	// cpu to SPU2
+	{
+		#if DEBUG_HLE_IOP
+		printf("DMA7: RAM %08x to SPU2\n", madr);
+		#endif
+		bcr = (bcr>>16) * (bcr & 0xffff) * 4;
+		SPU2writeDMA7Mem(madr&0x1fffff, bcr);
+	}
+	else
+	{
+		#if DEBUG_HLE_IOP
+		printf("DMA7: SPU2 to RAM %08x\n", madr);
+		#endif
+		bcr = (bcr>>16) * (bcr & 0xffff) * 4;
+//		SPU2readDMA7Mem(madr&0x1fffff, bcr);
+	}
+
+	dma7_delay = 80;
+}
+
+void psx_hw_write(offs_t offset, uint32 data, uint32 mem_mask)
+{
+	union cpuinfo mipsinfo;
+
+	if (offset >= 0x00000000 && offset <= 0x007fffff)
+	{
+		offset &= 0x1fffff;
+//		if (offset < 0x10000) printf("Write %x to kernel @ %x\n", data, offset);
+
+		mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+
+		psx_ram[offset>>2] &= LE32(mem_mask);
+		psx_ram[offset>>2] |= LE32(data);
+		return;
+	}
+
+	if (offset >= 0x80000000 && offset <= 0x807fffff)
+	{
+		offset &= 0x1fffff;
+//		if (offset < 0x10000) printf("Write %x to kernel @ %x\n", data, offset);
+		mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+		psx_ram[offset>>2] &= LE32(mem_mask);
+		psx_ram[offset>>2] |= LE32(data);
+		return;
+	}
+
+	if (offset == 0x1f801014 || offset == 0xbf801014)
+	{
+		spu_delay &= mem_mask;
+		spu_delay |= data;
+		return;
+	}
+
+	if (offset >= 0x1f801c00 && offset <= 0x1f801dff)
+	{
+	  //		printf("SPU2 wrote %x to SPU1 address %x!\n", data, offset);
+		if (mem_mask == 0xffff0000)
+		{
+			SPUwriteRegister(offset, data);
+			return;
+		}
+		else if (mem_mask == 0x0000ffff)
+		{
+			SPUwriteRegister(offset, data>>16);
+			return;
+		}
+		else printf("SPU: write unknown mask %08x\n", mem_mask);
+	}
+
+	if (offset >= 0xbf900000 && offset <= 0xbf9007ff)
+	{
+		if (mem_mask == 0xffff0000)
+		{
+			SPU2write(offset, data);
+			return;
+		}
+		else if (mem_mask == 0x0000ffff)
+		{
+			SPU2write(offset, data>>16);
+			return;
+		}
+		else if (mem_mask == 0)
+		{
+			SPU2write(offset, data & 0xffff);
+			SPU2write(offset+2, data>>16);
+			return;
+		}
+		else printf("SPU2: write unknown mask %08x\n", mem_mask);
+	}
+
+	if (offset >= 0x1f801100 && offset <= 0x1f801128)
+	{
+		int cnt = (offset>>4) & 0xf;
+		
+		switch (offset & 0xf)
+		{
+			case 0:
+				root_cnts[cnt].count = data;
+//				printf("RC: counter %d count = %x\n", cnt, data);
+				break;
+			case 4:
+				root_cnts[cnt].mode = data;
+//				printf("RC: counter %d mode = %x\n", cnt, data);
+				break;
+			case 8:
+				root_cnts[cnt].target = data;
+//				printf("RC: counter %d target = %x\n", cnt, data);
+				break;
+		}
+
+		return;
+	}
+
+	// DMA4
+	if (offset == 0x1f8010c0)
+	{
+		dma4_madr = data;
+		return;
+	}
+	else if (offset == 0x1f8010c4)
+	{
+		dma4_bcr = data;
+		return;
+	}
+	else if (offset == 0x1f8010c8)
+	{
+		dma4_chcr = data;
+		psx_dma4(dma4_madr, dma4_bcr, dma4_chcr);
+
+		if (dma_icr & (1 << (16+4)))
+		{
+			dma_timer = 3;
+		}
+		return;
+	}
+	else if (offset == 0x1f8010f4)
+	{
+		dma_icr = ( dma_icr & mem_mask ) |
+			  ( ~mem_mask & 0x80000000 & dma_icr) |
+			  ( ~data & ~mem_mask & 0x7f000000 & dma_icr) |
+			  ( data & ~mem_mask & 0x00ffffff);
+
+		if ((dma_icr & 0x7f000000) != 0)
+		{
+			dma_icr &= ~0x80000000;
+		}
+
+		return;
+	}
+	else if (offset == 0x1f801070)
+	{
+		irq_data = (irq_data & mem_mask) | (irq_data & irq_mask & data);
+		psx_irq_update();
+		return;
+	}
+	else if (offset == 0x1f801074)
+	{
+		irq_mask &= mem_mask;
+		irq_mask |= data;
+		psx_irq_update();
+		return;
+	}
+
+	// PS2 DMA4
+	if (offset == 0xbf8010c0)
+	{
+		dma4_madr = data;
+		return;
+	}
+	else if (offset == 0xbf8010c8)
+	{
+		dma4_chcr = data;
+		ps2_dma4(dma4_madr, dma4_bcr, dma4_chcr);
+
+		if (dma_icr & (1 << (16+4)))
+		{
+			dma_timer = 3;
+		}
+		return;
+	}
+							 
+	if (offset == 0xbf8010c4 || offset == 0xbf8010c6)
+	{
+		dma4_bcr &= mem_mask;
+		dma4_bcr |= data;
+		return;
+	}
+
+	// PS2 DMA7
+	if (offset == 0xbf801500)
+	{
+		dma7_madr = data;
+		return;
+	}
+	else if (offset == 0xbf801504)
+	{
+		dma7_chcr = data;
+		ps2_dma7(dma7_madr, dma7_bcr, dma7_chcr);
+		return;
+	}
+							 
+	if (offset == 0xbf801508 || offset == 0xbf80150a)
+	{
+		dma7_bcr &= mem_mask;
+		dma7_bcr |= data;
+		return;
+	}
+
+	#if DEBUG_UNK_RW
+	{
+		union cpuinfo mipsinfo;
+
+		mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+		printf("Unknown write: %08x to %08x, mask %08x (PC=%x)\n", data, offset&~3, mem_mask, mipsinfo.i);
+	}
+	#endif
+}
+
+// called per sample, 1/44100th of a second (768 clock cycles)
+void psx_hw_slice(void)
+{
+	psx_hw_runcounters();
+
+	if (!WAI)
+		mips_execute(768/CLOCK_DIV);
+
+	if (dma_timer)
+	{
+		dma_timer--;
+		if (dma_timer == 0)
+		{
+			dma_icr |= (1 << (24+4));
+			psx_irq_set(0x0008);
+		}
+	}
+}
+
+void ps2_hw_slice(void)
+{
+	int i = 0;
+
+	timerexp = 0;
+	psx_hw_runcounters();
+
+	if (iCurThread != -1)
+	{
+		mips_execute(836/CLOCK_DIV);
+	}
+	else	// no thread, don't run CPU, just update counters
+	{
+		if (timerexp)
+		{
+			ps2_reschedule();
+
+			if (iCurThread != -1)
+			{
+				mips_execute((836/CLOCK_DIV)-i);
+				i = (836/CLOCK_DIV);
+			}
+		}
+	}
+}
+
+static int fcnt = 0;
+
+void psx_hw_frame(void)
+{
+	if (psf_refresh == 50)
+	{
+		fcnt++;;
+
+		if (fcnt < 6)
+		{
+			psx_irq_set(1);
+		}
+		else
+		{
+			fcnt = 0;
+		}
+	}
+	else	// NTSC
+	{
+		psx_irq_set(1);
+	}
+}
+
+void ps2_hw_frame(void)
+{
+	ps2_reschedule();
+}
+
+// BIOS HLE
+
+// heap block struct offsets
+enum
+{
+	BLK_STAT = 0,
+	BLK_SIZE = 4,
+	BLK_FD = 8,
+	BLK_BK = 12
+};
+
+static uint32 heap_addr, entry_int = 0;
+
+extern uint32 mips_get_cause(void);
+extern uint32 mips_get_status(void);
+extern void mips_set_status(uint32 status);
+extern uint32 mips_get_ePC(void);
+
+static uint32 irq_regs[37];
+
+static int irq_mutex = 0;
+
+static void call_irq_routine(uint32 routine, uint32 parameter)
+{
+	int j, oldICount;
+	union cpuinfo mipsinfo;
+
+	if (!irq_mutex)
+	{
+		irq_mutex = 1;
+	}
+	else
+	{
+		printf("IOP: ERROR!  IRQ reentry!\n");
+		return;
+	}
+
+	// save regs for IRQ
+	for (j = 0; j < 32; j++)
+	{
+		mips_get_info(CPUINFO_INT_REGISTER + MIPS_R0 + j, &mipsinfo);
+		irq_regs[j] = mipsinfo.i;
+	}
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+	irq_regs[32] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+	irq_regs[33] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_PC, &mipsinfo);
+	irq_regs[34] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
+	irq_regs[35] = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
+	irq_regs[36] = mipsinfo.i;
+
+	// PC = timer handler routine
+	mipsinfo.i = routine; 
+	mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+	// parameter in a0
+	mipsinfo.i = parameter;
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+
+	// RA = a trap address we can set
+	mipsinfo.i = 0x80001000;
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+
+	// make sure we're set
+	psx_ram[0x1000/4] = LE32(FUNCT_HLECALL);
+
+	softcall_target = 0;
+	oldICount = mips_get_icount();
+	while (!softcall_target)
+	{
+		mips_execute(10);
+	}
+	mips_set_icount(oldICount);
+
+	// restore IRQ regs
+	for (j = 0; j < 32; j++)
+	{
+		mipsinfo.i = irq_regs[j];
+		mips_set_info(CPUINFO_INT_REGISTER + MIPS_R0 + j, &mipsinfo);
+	}
+
+	mipsinfo.i = irq_regs[32];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+	mipsinfo.i = irq_regs[33];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+	mipsinfo.i = irq_regs[34];
+	mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+	mipsinfo.i = irq_regs[35];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
+	mipsinfo.i = irq_regs[36];
+	mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
+
+	irq_mutex = 0;
+}
+
+void psx_bios_exception(uint32 pc)
+{
+	uint32 a0, status;
+	union cpuinfo mipsinfo;
+	int i, oldICount;
+
+//	printf("bios_exception: cause %x\n", mips_get_cause() & 0x3c);
+
+	// get a0
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+	a0 = mipsinfo.i;
+
+	switch (mips_get_cause() & 0x3c)
+	{
+		case 0:	// IRQ
+//			printf("IRQ: %x, mask %x\n", irq_data, irq_mask);
+			// save all regs
+			for (i = 0; i < 32; i++)
+			{
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
+				irq_regs[i] = mipsinfo.i;
+			}
+			mips_get_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+			irq_regs[32] = mipsinfo.i;
+			mips_get_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+			irq_regs[33] = mipsinfo.i;
+
+			// check BIOS-driven interrupts
+			if (irq_data & 1)	// VSync
+			{
+				if (CounterEvent[3][1].status == LE32(EvStACTIVE))
+				{
+					// run the handler
+					mipsinfo.i = LE32(CounterEvent[3][1].fhandler);
+//	       				printf("Cause = %x, ePC = %x\n", mips_get_cause(), mips_get_ePC());
+//	       				printf("VBL running handler @ %x\n", mipsinfo.i);
+					mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+					mipsinfo.i = 0x80001000;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				
+					// make sure we're set
+					psx_ram[0x1000/4] = LE32(FUNCT_HLECALL);
+		
+					softcall_target = 0;
+					oldICount = mips_get_icount();
+					while (!softcall_target)
+					{
+						mips_execute(10);
+					}
+					mips_set_icount(oldICount);
+
+//	       				printf("Exiting softcall handler\n");
+
+					irq_data &= ~1;		// clear the VBL IRQ if we handled it
+				}
+			}
+			else if (irq_data & 0x70)	// root counters
+			{
+				for (i = 0; i < 3; i++)
+				{
+					if (irq_data & (1 << (i+4)))
+					{
+						if (CounterEvent[i][1].status == LE32(EvStACTIVE))
+						{
+							// run the handler
+							mipsinfo.i = LE32(CounterEvent[i][1].fhandler);
+//							printf("Cause = %x, ePC = %x\n", mips_get_cause(), mips_get_ePC());
+//							printf("Counter %d running handler @ %x\n", i, mipsinfo.i);
+							mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+							mipsinfo.i = 0x80001000;
+							mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+							
+							// make sure we're set
+							psx_ram[0x1000/4] = LE32(FUNCT_HLECALL);
+					
+							softcall_target = 0;
+							oldICount = mips_get_icount();
+							while (!softcall_target)
+							{
+								mips_execute(10);
+							}
+							mips_set_icount(oldICount);
+
+//							printf("Exiting softcall handler\n");
+							irq_data &= ~(1 << (i+4));
+						}
+						else
+						{
+//							printf("CEvt %d not active\n", i);
+						}
+					}
+				}
+			}
+		 	
+			if (entry_int)
+			{
+				psx_hw_write(0x1f801070, 0xffffffff, 0);
+
+				a0 = entry_int;
+
+//				printf("taking entry_int\n");
+
+				// RA (and PC)
+				mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+0)/4]);
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+				// SP
+				mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+4)/4]);
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
+				// FP
+				mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+8)/4]);
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
+
+				// S0-S7 are next
+				for (i = 0; i < 8; i++)
+				{
+					mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+12+(i*4))/4]);
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R16 + i, &mipsinfo);
+				}
+
+				// GP
+				mipsinfo.i = LE32(psx_ram[((a0&0x1fffff)+44)/4]);
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo);
+
+				// v0 = 1
+				mipsinfo.i = 1;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+			}
+			else
+			{
+				psx_hw_write(0x1f801070, 0, 0xffff0000);
+
+				// note: the entry_int won't be bailing us out here, so do it ourselves
+				for (i = 0; i < 32; i++)
+				{
+					mipsinfo.i = irq_regs[i];
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
+				}
+
+				mipsinfo.i = irq_regs[32];
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+				mipsinfo.i = irq_regs[33];
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+				mipsinfo.i = mips_get_ePC();
+				mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+				status = mips_get_status();
+				status = (status & 0xfffffff0) | ((status & 0x3c)>>2);
+				mips_set_status(status);
+			}
+			break;
+
+		case 0x20:	// syscall
+			// syscall always farks with the status, so get it now
+			status = mips_get_status();
+
+			switch (a0)
+			{
+				case 1: // EnterCritical
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: EnterCritical\n");
+					#endif
+					status &= ~0x0404;
+					break;
+
+				case 2:	// ExitCritical
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: ExitCritical\n");
+					#endif
+					status |= 0x0404;
+					break;
+
+				default:
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: Unknown syscall %x\n", a0);
+					#endif
+					break;
+			}
+
+			// PC = ePC + 4
+			mipsinfo.i = mips_get_ePC() + 4;
+			mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+			// and update the status accordingly
+			status = (status & 0xfffffff0) | ((status & 0x3c)>>2);
+			mips_set_status(status);
+			break;
+
+		default:
+			#if DEBUG_HLE_BIOS
+			printf("HLEBIOS: Unknown exception %x\n", mips_get_cause());
+			#endif
+			break;
+	}
+}
+
+static uint32 calc_ev(uint32 a0)
+{
+	uint32 ev;
+
+	ev = (a0 >> 24) & 0xf;
+	if (ev == 0xf) 
+	{
+		ev = 0x5;
+	}
+	ev *= 32;
+	ev += (a0 & 0x1f);
+
+	return ev;
+}
+
+static uint32 calc_spec(uint32 a1)
+{
+	uint32 spec = 0;
+	int i;
+
+	if (a1 == 0x301)
+	{
+		spec = 16;
+	}
+	else if (a1 == 0x302)
+	{
+		spec = 17;
+	}
+	else
+	{
+		for (i = 0; i < 16; i++)
+		{
+			if (a1 & (1<<i))
+			{
+				spec = i;
+				break;
+			}
+		}
+	}
+
+	return spec;
+}
+
+void psx_hw_init(void)
+{
+	timerexp = 0;
+
+	memset(filestat, 0, sizeof(filestat));
+	memset(filedata, 0, sizeof(filedata));
+
+	dma4_cb = dma7_cb = 0;
+
+	sys_time = 0;
+
+	// clear registered libraries table
+	memset(reglibs, 0, sizeof(reglibs));
+	iNumLibs = 0;
+
+	memset(evflags, 0, sizeof(evflags));
+	iNumFlags = 0;
+
+	memset(threads, 0, sizeof(threads));
+	iNumThreads = 1;	// we always have at least one thread
+
+	memset(semaphores, 0, sizeof(semaphores));
+	iNumSema = 0;
+
+	// set the initial thread to "RUNNING"
+	threads[0].iState = TS_RUNNING;
+	iCurThread = 0;
+
+	memset(iop_timers, 0, sizeof(iop_timers));
+	iNumTimers = 0;
+
+	// set PS1 BIOS HLE breakpoints
+	psx_ram[0xa0/4] = LE32(FUNCT_HLECALL);
+	psx_ram[0xb0/4] = LE32(FUNCT_HLECALL);
+	psx_ram[0xc0/4] = LE32(FUNCT_HLECALL);
+
+	Event = (EvtCtrlBlk *)&psx_ram[0x1000/4];
+	CounterEvent = (Event + (32*2));
+
+	dma_icr = 0;
+	spu_delay = 0;
+	irq_data = 0;
+	irq_mask = 0;
+	softcall_target = 0;
+	gpu_stat = 0;
+	dma4_madr = dma4_bcr = dma4_chcr = 0;
+	heap_addr = 0;
+	entry_int = 0;
+
+	WAI = 0;
+
+	root_cnts[0].mode = RC_EN;
+	root_cnts[1].mode = RC_EN;
+	root_cnts[2].mode = RC_EN;
+	root_cnts[0].sysclock = 0;
+	root_cnts[1].sysclock = 0;
+	root_cnts[2].sysclock = 0;
+}
+
+void psx_bios_hle(uint32 pc)
+{
+	uint32 subcall, status;
+	union cpuinfo mipsinfo;
+	uint32 a0, a1, a2, a3;
+	int i;
+
+	if ((pc == 0) || (pc == 0x80000000))  	 	// IOP "null" state
+	{
+		#if DEBUG_HLE_IOP
+		printf("IOP 'null' state\n");
+		#endif
+//		ao_song_done = 1;
+		return;
+	}
+
+	if (pc == 0xbfc00180 || pc == 0xbfc00184)	// exception, not BIOS call
+	{
+		psx_bios_exception(pc);
+		return;
+	}
+
+	if (pc == 0x80001000)
+	{
+//		printf("hit softcall target\n");
+		softcall_target = 1;
+		return;
+	}
+
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R9, &mipsinfo);
+
+	subcall = mipsinfo.i & 0xff;
+
+	// most calls have a0/a1 as parameters, so prefetch them
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+	a0 = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
+	a1 = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R6, &mipsinfo);
+	a2 = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R7, &mipsinfo);
+	a3 = mipsinfo.i;
+
+//	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+//	printf("HLEBIOS: return is %08x\n", mipsinfo.i);
+	
+	switch (pc)
+	{
+		case 0xa0:	// a0 syscalls
+			switch (subcall)
+			{
+				case 0x13:	// setjmp
+					// RA
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+					psx_ram[((a0&0x1fffff)+0)/4] = LE32(mipsinfo.i);
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: setjmp(%08x) => PC %08x\n", a0, mipsinfo.i);
+					#endif
+					// SP
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R29, &mipsinfo);
+					psx_ram[((a0&0x1fffff)+4)/4] = LE32(mipsinfo.i);
+					// FP
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R30, &mipsinfo);
+					psx_ram[((a0&0x1fffff)+8)/4] = LE32(mipsinfo.i);
+
+					// S0-S7 are next
+					for (i = 0; i < 8; i++)
+					{
+						mips_get_info(CPUINFO_INT_REGISTER + MIPS_R16 + i, &mipsinfo);
+						psx_ram[((a0&0x1fffff)+12+(i*4))/4] = LE32(mipsinfo.i);
+					}
+
+					// GP
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R28, &mipsinfo);
+					psx_ram[((a0&0x1fffff)+44)/4] = LE32(mipsinfo.i);
+
+					// v0 = 0
+					mipsinfo.i = 0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					break;
+
+				case 0x18:	// strncmp
+					{
+						uint8 *dst, *src;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: strncmp(%08x, %08x, %d)\n", a0, a1, a2);
+						#endif
+
+						dst = (uint8 *)psx_ram;
+						src = (uint8 *)psx_ram;
+						dst += (a0 & 0x1fffff);
+						src += (a1 & 0x1fffff);
+
+						// v0 = result
+						mipsinfo.i = strncmp((char *)dst, (char *)src, a2);
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+
+				case 0x19:	// strcpy
+					{
+						uint8 *dst, *src;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: strcpy(%08x, %08x)\n", a0, a1);
+						#endif
+
+						dst = (uint8 *)psx_ram;
+						src = (uint8 *)psx_ram;
+						dst += (a0 & 0x1fffff);
+						src += (a1 & 0x1fffff);
+
+						while (*src)
+						{
+							*dst = *src;
+							dst++;
+							src++;
+						}
+
+						// v0 = a0
+						mipsinfo.i = a0;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+
+				case 0x28:	// bzero
+					{
+						uint8 *dst;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: bzero(%08x, %08x)\n", a0, a1);
+						#endif
+
+						dst = (uint8 *)psx_ram;
+						dst += (a0 & 0x1fffff);
+						memset(dst, 0, a1);
+					}
+					break;
+
+				case 0x2a:	// memcpy
+					{
+						uint8 *dst, *src;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: memcpy(%08x, %08x, %08x)\n", a0, a1, a2);
+						#endif
+
+						dst = (uint8 *)psx_ram;
+						src = (uint8 *)psx_ram;
+						dst += (a0 & 0x1fffff);
+						src += (a1 & 0x1fffff);
+
+						while (a2)
+						{
+							*dst = *src;
+							dst++;
+							src++;
+							a2--;
+						}
+
+						// v0 = a0
+						mipsinfo.i = a0;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+				
+				case 0x2b:	// memset
+					{
+						uint8 *dst;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: memset(%08x, %08x, %08x)\n", a0, a1, a2);
+						#endif
+
+						dst = (uint8 *)psx_ram;
+						dst += (a0 & 0x1fffff);
+
+						while (a2)
+						{
+							*dst = a1;
+							dst++;
+							a2--;
+						}
+
+						// v0 = a0
+						mipsinfo.i = a0;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+
+				case 0x2f:	// rand
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: rand\n");
+					#endif
+
+					// v0 = result
+					mipsinfo.i = 1 + (int)(32767.0*rand()/(RAND_MAX+1.0));
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					break;
+
+				case 0x30:	// srand
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: srand(%x)\n", a0);
+					#endif
+					srand(a0);
+					break;
+
+				case 0x33:	// malloc
+					{
+						uint32 chunk, fd;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: malloc(%x)\n", a0);
+						#endif
+
+						chunk = heap_addr;
+
+						// find a free block that's big enough
+						while ((a0 > LE32(psx_ram[(chunk+BLK_SIZE)/4])) ||
+						       (LE32(psx_ram[(chunk+BLK_STAT)/4]) ==  1))
+						{
+							chunk = LE32(psx_ram[(chunk+BLK_FD)]);
+						}
+
+						// split free block
+						fd = chunk + 16 + a0;	// free block starts after block record and allocation size
+						psx_ram[(fd+BLK_STAT)/4] = psx_ram[(chunk+BLK_STAT)/4];
+						psx_ram[(fd+BLK_SIZE)/4] = LE32(LE32(psx_ram[(chunk+BLK_SIZE)/4]) - a0);
+						psx_ram[(fd+BLK_FD)/4] = psx_ram[(chunk+BLK_FD)/4]; 
+						psx_ram[(fd+BLK_BK)/4] = chunk;
+
+						psx_ram[(chunk+BLK_STAT)/4] = LE32(1);
+						psx_ram[(chunk+BLK_SIZE)/4] = LE32(a0);
+						psx_ram[(chunk+BLK_FD)/4] = LE32(fd);
+
+						mipsinfo.i = chunk + 16;
+						mipsinfo.i |= 0x80000000;
+						#if DEBUG_HLE_BIOS
+						printf("== %08x\n", mipsinfo.i);
+						#endif
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+				
+				case 0x39:	// InitHeap
+					// heap address in A0, length in A1
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: InitHeap(%08x, %08x)\n", a0, a1);
+					#endif
+
+					heap_addr = a0 & 0x3fffffff;
+
+					psx_ram[(heap_addr+BLK_STAT)/4] = LE32(0);
+					psx_ram[(heap_addr+BLK_FD)/4] = LE32(0);
+					psx_ram[(heap_addr+BLK_BK)/4] = LE32(0);
+
+					// if heap size out of range, clamp it
+					if (((a0 & 0x1fffff) + a1) >= 2*1024*1024)
+					{
+						psx_ram[(heap_addr+BLK_SIZE)/4] = LE32(0x1ffffc - (a0 & 0x1fffff));
+					}
+					else
+					{
+						psx_ram[(heap_addr+BLK_SIZE)/4] = LE32(a1);
+					}
+					break;
+
+				case 0x3f:	// printf
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: printf(%08x) = %s\n", a0, &psx_ram[(a0&0x1fffff)/4]);
+					#endif
+					break;
+
+				case 0x72:	//__96_remove
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: __96_remove\n");
+					#endif
+					break;
+
+				default:
+					#if DEBUG_HLE_BIOS
+					printf("Unknown BIOS A0 call = %x\n", subcall);
+					#endif
+					break;
+			}
+			break;
+
+		case 0xb0:	// b0 syscalls
+			switch (subcall)
+			{		 
+				case 0x07:	// DeliverEvent
+					{
+						int ev, spec;
+
+
+						ev = calc_ev(a0);
+						spec = calc_spec(a1);
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: DeliverEvent(ev %d, spec %d)\n", ev, spec);
+						#endif
+
+						if (Event[ev][spec].status != LE32(EvStACTIVE))
+						{
+							#if DEBUG_HLE_BIOS
+							printf("event not active\n");
+							#endif
+							return;
+						}
+
+						// if interrupt mode, do the call
+						if (Event[ev][spec].mode == LE32(EvMdINTR))
+						{
+							#if DEBUG_HLE_BIOS
+							printf("INTR type, need to call handler %x\n", LE32(Event[ev][spec].fhandler));
+							#endif
+						}
+						else
+						{
+							Event[ev][spec].status = LE32(EvStALREADY);
+						}
+					}
+					break;
+			     
+				case 0x08:	// OpenEvent
+					{
+						int ev, spec;
+						
+						ev = calc_ev(a0);
+						spec = calc_spec(a1);
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: OpenEvent(%08x, %08x, %08x, %08x) = ev %d spec %d\n", a0, a1, a2, a3, ev, spec);
+						if (ev >= 64 && ev <= 67)
+						{
+							printf("HLEBIOS: event %d maps to root counter %d\n", ev, ev-64);
+						}
+						#endif
+
+						Event[ev][spec].status = LE32(EvStWAIT);
+						Event[ev][spec].mode = LE32(a2);
+						Event[ev][spec].fhandler = LE32(a3);
+
+						// v0 = ev | spec<<8;
+						mipsinfo.i = ev | (spec<<8);
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+
+				case 0x0a:	// WaitEvent
+					{
+						int ev, spec;
+						
+						ev = a0 & 0xff;
+						spec = (a0 >> 8) & 0xff;
+
+						mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: WaitEvent(ev %d spec %d) PC=%x\n", ev, spec, mipsinfo.i);
+						#endif
+
+						Event[ev][spec].status = LE32(EvStACTIVE);
+
+						// v0 = 1
+						mipsinfo.i = 1;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+
+						WAI = 1;
+						mips_shorten_frame();
+					}
+					break;
+
+				case 0x0b:	// TestEvent
+					{
+						int ev, spec;
+
+						ev   = a0 & 0xff;
+						spec = (a0 >> 8) & 0xff;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: TestEvent(ev %d spec %d)\n", ev, spec);
+						#endif
+
+						// v0 = (is event ready?)
+						if (Event[ev][spec].status == LE32(EvStALREADY))
+						{
+							Event[ev][spec].status = LE32(EvStACTIVE);
+							mipsinfo.i = 1;
+						}
+						else
+						{
+							mipsinfo.i = 0;
+						}
+
+						WAI = 1;
+
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+
+						// it looks like this sets v1 to something non-zero too
+						// (code in Crash 2 & 3 actually relies on that behavior)
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R3, &mipsinfo);
+					}
+					break;
+
+				case 0x0c:	// EnableEvent
+					{
+						int ev, spec;
+						
+						ev = a0 & 0xff;
+						spec = (a0 >> 8) & 0xff;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: EnableEvent(ev %d spec %d)\n", ev, spec);
+						#endif
+
+						Event[ev][spec].status = LE32(EvStACTIVE);
+
+						// v0 = 1
+						mipsinfo.i = 1;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+
+				case 0x0d:	// DisableEvent
+					{
+						int ev, spec;
+						
+						ev = a0 & 0xff;
+						spec = (a0 >> 8) & 0xff;
+
+						#if DEBUG_HLE_BIOS
+						printf("HLEBIOS: DisableEvent(ev %d spec %d)\n", ev, spec);
+						#endif
+
+						Event[ev][spec].status = LE32(EvStWAIT);
+
+						// v0 = 1
+						mipsinfo.i = 1;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+					}
+					break;
+
+				case 0x17:	// ReturnFromException
+					for (i = 0; i < 32; i++)
+					{
+						mipsinfo.i = irq_regs[i];
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R0 + i, &mipsinfo);
+					}
+
+					mipsinfo.i = irq_regs[32];
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_HI, &mipsinfo);
+					mipsinfo.i = irq_regs[33];
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_LO, &mipsinfo);
+					mipsinfo.i = mips_get_ePC();
+//					printf("ReturnFromException: IRQ state %x\n", irq_data & irq_mask);
+//					printf("HLEBIOS: ReturnFromException, cause = %08x, PC = %08x\n", mips_get_cause(), mipsinfo.i);
+					mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+					status = mips_get_status();
+					status = (status & 0xfffffff0) | ((status & 0x3c)>>2);
+					mips_set_status(status);
+					return;	// force return to avoid PC=RA below
+					break;
+
+				case 0x19:	// HookEntryInt
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: HookEntryInt(%08x)\n", a0);
+					#endif
+					entry_int = a0;
+					break;
+
+				case 0x3f:	// puts
+//					printf("HLEBIOS: puts\n");
+					break;
+
+				case 0x5b:	// ChangeClearPAD
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: ChangeClearPAD\n");
+					#endif
+					break;
+
+				default:
+					#if DEBUG_HLE_BIOS
+					printf("Unknown BIOS B0 call = %x\n", subcall);
+					#endif
+					break;
+			}
+			break;
+
+		case 0xc0:	// c0 syscalls
+			switch (subcall)
+			{		
+				case 0xa:	// ChangeClearRCnt
+					#if DEBUG_HLE_BIOS
+					printf("HLEBIOS: ChangeClearRCnt(%08x, %08x)\n", a0, a1);
+					#endif
+
+					// v0 = (a0*4)+0x8600
+					mipsinfo.i = LE32(psx_ram[((a0<<2) + 0x8600)/4]);
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+
+					// (a0*4)+0x8600 = a1;
+					psx_ram[((a0<<2) + 0x8600)/4] = LE32(a1);
+					break;
+			      
+				default:
+					#if DEBUG_HLE_BIOS
+					printf("Unknown BIOS C0 call = %x\n", subcall);
+					#endif
+					break;
+			}
+			break;
+	}
+
+	// PC = RA
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+	mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+}
+
+// root counters
+
+void psx_hw_runcounters(void)
+{
+	int i, j;
+	union cpuinfo mipsinfo;
+
+	// don't process any IRQ sources when interrupts are suspended
+	if (!intr_susp)
+	{
+		if (dma4_delay)
+		{
+			dma4_delay--;
+
+			if (dma4_delay == 0)
+			{
+				SPU2interruptDMA4();
+
+				if (dma4_cb)
+				{
+					call_irq_routine(dma4_cb, dma4_flag);
+				}
+			}
+		}
+
+		if (dma7_delay)
+		{
+			dma7_delay--;
+
+			if (dma7_delay == 0)
+			{
+				SPU2interruptDMA7();
+
+				if (dma7_cb)
+				{
+					call_irq_routine(dma7_cb, dma7_flag);
+				}
+			}
+		}
+
+		for (i = 0; i < iNumThreads; i++)
+		{
+			if (threads[i].iState == TS_WAITDELAY)
+			{
+				if (threads[i].waitparm > CLOCK_DIV)
+				{
+					threads[i].waitparm -= CLOCK_DIV;
+				}
+				else	// time's up
+				{
+					threads[i].waitparm = 0;
+					threads[i].iState = TS_READY;
+
+					timerexp = 1;
+
+					ps2_reschedule();
+				}
+			}
+		}
+
+		sys_time += 836;
+
+		if (iNumTimers > 0)
+		{
+			for (i = 0; i < iNumTimers; i++)
+			{
+				if (iop_timers[i].iActive > 0)
+				{
+					iop_timers[i].count += 836;
+					if (iop_timers[i].count >= iop_timers[i].target)
+					{
+						iop_timers[i].count -= iop_timers[i].target;
+
+	//					printf("Timer %d: handler = %08x, param = %08x\n", i, iop_timers[i].handler, iop_timers[i].hparam);
+						call_irq_routine(iop_timers[i].handler, iop_timers[i].hparam);
+
+						timerexp = 1;
+					} 
+				}
+			}
+		}
+	}
+
+// PS1 root counters
+	for (i = 0; i < 3; i++)
+	{
+		if ((!(root_cnts[i].mode & RC_EN)) && (root_cnts[i].mode != 0))
+		{
+			if (root_cnts[i].mode & RC_DIV8)
+			{
+				root_cnts[i].count += 768/8;
+			}
+			else
+			{
+				root_cnts[i].count += 768;
+			}
+
+			if (root_cnts[i].count >= root_cnts[i].target)
+			{
+				if (!(root_cnts[i].mode & RC_RESET))
+				{
+					root_cnts[i].mode |= RC_EN;
+				}
+				else
+				{
+					root_cnts[i].count %= root_cnts[i].target;
+				}
+				
+				psx_irq_set(1<<(4+i));
+			}
+		}
+	}
+}
+
+// PEOpS callbacks
+
+void SPUirq(void) 
+{
+//	psx_irq_set(0x200);
+}
+
+// PSXCPU callbacks
+
+uint8 program_read_byte_32le(offs_t address)
+{
+	switch (address & 0x3)
+	{
+		case 0:
+			return psx_hw_read(address, 0xffffff00);
+			break;
+		case 1:
+			return psx_hw_read(address, 0xffff00ff)>>8;
+			break;
+		case 2:
+			return psx_hw_read(address, 0xff00ffff)>>16;
+			break;
+		case 3:
+			return psx_hw_read(address, 0x00ffffff)>>24;
+			break;
+	}
+}
+
+uint16 program_read_word_32le(offs_t address)
+{
+	if (address & 2)
+		return psx_hw_read(address, 0x0000ffff)>>16;
+
+	return psx_hw_read(address, 0xffff0000);
+}
+
+uint32 program_read_dword_32le(offs_t address)
+{
+	return psx_hw_read(address, 0);
+}
+
+void program_write_byte_32le(offs_t address, uint8 data)
+{
+	switch (address & 0x3)
+	{
+		case 0:
+			psx_hw_write(address, data, 0xffffff00);
+			break;
+		case 1:
+			psx_hw_write(address, data<<8, 0xffff00ff);
+			break;
+		case 2:
+			psx_hw_write(address, data<<16, 0xff00ffff);
+			break;
+		case 3:
+			psx_hw_write(address, data<<24, 0x00ffffff);
+			break;
+	}
+}
+
+void program_write_word_32le(offs_t address, uint16 data)
+{
+	if (address & 2)
+	{
+		psx_hw_write(address, data<<16, 0x0000ffff);
+		return;
+	}
+
+	psx_hw_write(address, data, 0xffff0000);
+}
+
+void program_write_dword_32le(offs_t address, uint32 data)
+{
+	psx_hw_write(address, data, 0);
+}
+
+// sprintf replacement
+static iop_sprintf(char *out, char *fmt, uint32 pstart)
+{
+	char temp[64], tfmt[64];
+	char *cf, *pstr;
+	union cpuinfo mipsinfo;
+	int curparm, fp, isnum;
+
+	curparm = pstart;
+	cf = fmt;
+
+	while (*cf != '\0')
+	{
+		if (*cf != '%')
+		{
+			if (*cf == 27)
+			{
+				*out++ = '[';
+				*out++ = 'E';
+				*out++ = 'S';
+				*out++ = 'C';
+				*out = ']';
+			}
+			else
+			{
+				*out = *cf;
+			}
+			out++;
+			cf++;
+		}
+		else	// got format
+		{
+			cf++;
+
+			tfmt[0] = '%';
+			fp = 1;
+			while (((*cf >= '0') && (*cf <= '9')) || (*cf == '.'))
+			{
+				tfmt[fp] = *cf;
+				fp++;
+				cf++;
+			}
+
+			tfmt[fp] = *cf;
+			tfmt[fp+1] = '\0';
+
+			isnum = 0;			
+			switch (*cf)
+			{
+				case 'x':
+				case 'X':
+				case 'd':
+				case 'D':
+				case 'c':
+				case 'C':
+				case 'u':
+				case 'U':
+					isnum = 1;
+					break;
+			}
+
+//			printf("]]] temp format: [%s] [%d]\n", tfmt, isnum);
+
+			if (isnum)
+			{
+				mips_get_info(curparm, &mipsinfo);
+//				printf("parameter %d = %x\n", curparm-pstart, mipsinfo.i);
+				curparm++;
+				sprintf(temp, tfmt, (int32)mipsinfo.i);
+			}
+			else
+			{
+				mips_get_info(curparm, &mipsinfo);
+				curparm++;
+
+				pstr = (char *)psx_ram;
+				pstr += (mipsinfo.i & 0x1fffff);
+
+				sprintf(temp, tfmt, pstr);
+			}
+
+			pstr = &temp[0];
+			while (*pstr != '\0')
+			{
+				*out = *pstr;
+				out++;
+				pstr++;
+			}
+
+			cf++;
+		}
+	}
+
+	*out = '\0';
+}
+
+// PS2 IOP callbacks
+void psx_iop_call(uint32 pc, uint32 callnum)
+{
+	uint32 scan;
+	char *mname, *str1, *str2, *str3, name[9], out[512];
+	uint32 a0, a1, a2, a3;
+	union cpuinfo mipsinfo;
+	int i;
+
+//	printf("IOP call @ %08x\n", pc);
+
+	// prefetch parameters
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+	a0 = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
+	a1 = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R6, &mipsinfo);
+	a2 = mipsinfo.i;
+	mips_get_info(CPUINFO_INT_REGISTER + MIPS_R7, &mipsinfo);
+	a3 = mipsinfo.i;
+
+	scan = (pc&0x0fffffff)/4;
+	while ((psx_ram[scan] != LE32(0x41e00000)) && (scan >= (0x10000/4)))
+	{
+		scan--;
+	}
+
+	if (psx_ram[scan] != LE32(0x41e00000))
+	{
+		printf("FATAL ERROR: couldn't find IOP link signature\n");
+		return;
+	}
+
+	scan += 3;	// skip zero and version
+	memcpy(name, &psx_ram[scan], 8);
+	name[8] = '\0';
+
+//	printf("IOP: call module [%s] service %d (PC=%08x)\n", name, callnum, pc);
+
+	if (!strcmp(name, "stdio"))
+	{
+		switch (callnum)
+		{
+			case 4:	// printf
+				mname = (char *)psx_ram;
+				mname += a0 & 0x1fffff;
+				mname += (a0 & 3);
+
+				iop_sprintf(out, mname, CPUINFO_INT_REGISTER + MIPS_R5);	// a1 is first parm
+				
+			/*	if (out[strlen(out)-1] != '\n')
+				{
+					strcat(out, "\n");
+				}*/
+
+				#if DEBUG_HLE_IOP
+				printf("%s", out);
+				#endif
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "sifman"))
+	{
+		switch (callnum)
+		{
+			case 5:	// sceSifInit
+				#if DEBUG_HLE_IOP
+				printf("IOP: sceSifInit()\n");
+				#endif
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 7: // sceSifSetDma
+				#if DEBUG_HLE_IOP
+				printf("IOP: sceSifSetDma(%08x %08x)\n", a0, a1);
+				#endif
+
+				mipsinfo.i = 1;	// nonzero = success
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 8:	// sceSifDmaStat
+				#if DEBUG_HLE_IOP
+				printf("IOP: sceSifDmaStat(%08x)\n", a0);
+				#endif
+
+				mipsinfo.i = -1;	// dma completed
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 29: // sceSifCheckInit
+				#if DEBUG_HLE_IOP
+				printf("IOP: sceSifCheckInit()\n");
+				#endif
+
+				mipsinfo.i = 1;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "thbase"))
+	{
+		uint32 newAlloc;
+
+		switch (callnum)
+		{
+			case 4:	// CreateThread
+				#if DEBUG_THREADING
+				printf("IOP: CreateThread(%08x)\n", a0);
+				#endif
+				a0 &= 0x1fffff;
+				a0 /= 4;
+				#if DEBUG_THREADING
+				printf("   : flags %x routine %08x pri %x stacksize %d refCon %08x\n",
+					psx_ram[a0], psx_ram[a0+1], psx_ram[a0+2], psx_ram[a0+3], psx_ram[a0+4]);
+				#endif
+
+				newAlloc = psf2_get_loadaddr();
+				// force 16-byte alignment
+				if (newAlloc & 0xf)
+				{
+					newAlloc &= ~0xf;
+					newAlloc += 16;
+				}
+				psf2_set_loadaddr(newAlloc + LE32(psx_ram[a0+3]));
+				
+				threads[iNumThreads].iState = TS_CREATED;
+				threads[iNumThreads].stackloc = newAlloc;
+				threads[iNumThreads].flags = LE32(psx_ram[a0]);
+				threads[iNumThreads].routine = LE32(psx_ram[a0+2]);
+				threads[iNumThreads].stacksize = LE32(psx_ram[a0+3]);
+				threads[iNumThreads].refCon = LE32(psx_ram[a0+4]);
+
+				mipsinfo.i = iNumThreads;
+				iNumThreads++;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 6:	// StartThread
+				#if DEBUG_THREADING
+				printf("IOP: StartThread(%d %d)\n", a0, a1);
+				#endif
+
+				FreezeThread(iCurThread, 1);
+				ThawThread(a0);
+				iCurThread = a0;
+				break;
+
+			case 20:// GetThreadID
+				#if DEBUG_THREADING
+				printf("IOP: GetThreadId()\n");
+				#endif
+
+				mipsinfo.i = iCurThread;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 24:// SleepThread
+				#if DEBUG_THREADING
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: SleepThread() [curThread %d, PC=%x]\n", iCurThread, mipsinfo.i);
+				#endif
+
+				FreezeThread(iCurThread, 1);
+				threads[iCurThread].iState = TS_SLEEPING;
+				iCurThread = -1;
+
+				ps2_reschedule();
+				break;
+
+			case 25:// WakeupThread
+				#if DEBUG_THREADING
+				printf("IOP: WakeupThread(%d)\n", a0);
+				#endif
+
+				// set thread to "ready to go"
+				threads[a0].iState = TS_READY;	
+				break;
+
+			case 26:// iWakeupThread
+				#if DEBUG_THREADING
+				printf("IOP: iWakeupThread(%d)\n", a0);
+				#endif
+
+				// set thread to "ready to go" if it's not running
+				if (threads[a0].iState != TS_RUNNING)
+				{
+					threads[a0].iState = TS_READY;	
+				}
+				break;
+
+			case 33:// DelayThread
+				{
+					double dTicks;
+					int i;
+
+					#if DEBUG_THREADING
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+					printf("IOP: DelayThread(%d) (PC=%x) [curthread = %d]\n", a0, mipsinfo.i, iCurThread);
+					#endif
+
+					if (a0 < 100)
+					{
+						a0 = 100;
+					}
+					dTicks = (double)a0;
+
+					FreezeThread(iCurThread, 1);
+					threads[iCurThread].iState = TS_WAITDELAY;
+					dTicks /= (double)1000000.0;
+					dTicks *= (double)36864000.0;	// 768*48000 = IOP native-mode clock rate
+					threads[iCurThread].waitparm = (uint32)dTicks;
+					iCurThread = -1;
+
+					ps2_reschedule();
+				}
+				break;
+
+			case 34://GetSystemTime
+				#if DEBUG_HLE_IOP
+				printf("IOP: GetSystemTime(%x)\n", a0);
+				#endif
+
+				a0 &= 0x1fffff;
+				a0 /= 4;
+
+				psx_ram[a0] = LE32(sys_time & 0xffffffff);  	// low
+				psx_ram[a0+1] = LE32(sys_time >> 32);	// high
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 39:// USec2SysClock
+				{
+					uint64 dTicks = (uint64)a0;
+					uint32 hi, lo;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: USec2SysClock(%d %08x)\n", a0, a1);
+					#endif
+
+					dTicks *= (uint64)36864000;
+					dTicks /= (uint64)1000000;
+			
+					hi = dTicks>>32;
+					lo = dTicks & 0xffffffff;
+
+					psx_ram[((a1 & 0x1fffff)/4)] = LE32(lo);
+					psx_ram[((a1 & 0x1fffff)/4)+1] = LE32(hi);
+
+					mipsinfo.i = 0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+			case 40://SysClock2USec
+				{
+					uint64 temp;
+					uint32 seconds, usec;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: SysClock2USec(%08x %08x %08x)\n", a0, a1, a2);
+					#endif
+
+					a0 &= 0x1fffff;
+					a1 &= 0x1fffff;
+					a2 &= 0x1fffff;
+					a0 /= 4;
+					a1 /= 4;
+					a2 /= 4;
+
+					temp = LE32(psx_ram[a0]);
+					temp |= (uint64)LE32(psx_ram[a0+1])<<32;
+
+					temp *= (uint64)1000000;
+					temp /= (uint64)36864000;
+
+					// temp now is USec
+					seconds = (temp / 1000000) & 0xffffffff;
+					usec = (temp % 1000000) & 0xffffffff;
+
+					psx_ram[a1] = LE32(seconds);
+					psx_ram[a2] = LE32(usec);
+				}
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "thevent"))
+	{
+		switch (callnum)
+		{
+			case 4:	// CreateEventFlag
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				#if DEBUG_HLE_IOP
+				printf("IOP: CreateEventFlag(%08x) (PC=%x)\n", a0, mipsinfo.i);
+				#endif
+
+				a0 &= 0x1fffff;
+				a0 /= 4;
+ 
+				evflags[iNumFlags].type = LE32(psx_ram[a0]);
+				evflags[iNumFlags].value = LE32(psx_ram[a0+1]);
+				evflags[iNumFlags].param = LE32(psx_ram[a0+2]);
+				evflags[iNumFlags].inUse = 1;
+
+				#if DEBUG_HLE_IOP
+				printf("     Flag %02d: type %d init %08x param %08x\n", iNumFlags, evflags[iNumFlags].type, evflags[iNumFlags].value, evflags[iNumFlags].param);
+				#endif
+
+				mipsinfo.i = iNumFlags+1;
+				iNumFlags++;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 6: // SetEventFlag
+				a0--;
+				#if DEBUG_HLE_IOP
+				printf("IOP: SetEventFlag(%d %08x)\n", a0, a1);
+				#endif
+
+				evflags[a0].value |= a1;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 7: // iSetEventFlag
+				a0--;
+				#if DEBUG_HLE_IOP
+				printf("IOP: iSetEventFlag(%08x %08x)\n", a0, a1);
+				#endif
+
+				evflags[a0].value |= a1;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+
+				for (i=0; i < iNumThreads; i++)
+				{
+					if ((threads[i].iState == TS_WAITEVFLAG) && (threads[i].waitparm == a0))
+					{
+						threads[i].iState = TS_READY;	
+					}
+				}
+				break;
+
+			case 8:	// ClearEventFlag
+				a0--;
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				#if DEBUG_HLE_IOP
+				printf("IOP: ClearEventFlag(%d %08x) (PC=%x)\n", a0, a1, mipsinfo.i);
+				#endif
+
+				evflags[a0].value &= a1;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 9: // iClearEventFlag
+				a0--;
+				#if DEBUG_HLE_IOP
+				printf("IOP: iClearEventFlag(%d %08x)\n", a0, a1);
+				#endif
+
+				evflags[a0].value &= a1;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 10:// WaitEventFlag
+				a0--;
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: WaitEventFlag(%d %08x %d %08x PC=%x)\n", a0, a1, a2, a3, mipsinfo.i);
+				#endif
+
+				// if we're not set, freeze this thread
+				if (!(evflags[a0].value & a1))
+				{
+					FreezeThread(iCurThread, 1);
+					threads[iCurThread].iState = TS_WAITEVFLAG;
+					threads[iCurThread].waitparm = a0;
+					iCurThread = -1;
+
+					ps2_reschedule();
+				}
+				else
+				{
+					mipsinfo.i = 0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "thsemap"))
+	{
+		int foundthread;
+
+		switch (callnum)
+		{
+			case 4:	// CreateSema
+				#if DEBUG_HLE_IOP
+				printf("IOP: CreateSema(%08x)\n", a0);
+				#endif
+
+				mipsinfo.i = -1;
+				for (i = 0; i < SEMA_MAX; i++)
+				{
+					if (!semaphores[i].inuse)
+					{
+						mipsinfo.i = i;
+						break;
+					}
+				}
+
+				if (mipsinfo.i == -1)
+				{
+					printf("IOP: out of semaphores!\n");
+				}
+
+				a0 &= 0x7fffffff;
+				a0 /= 4;
+
+//				printf("Sema %d Parms: %08x %08x %08x %08x\n", mipsinfo.i, psx_ram[a0], psx_ram[a0+1], psx_ram[a0+2], psx_ram[a0+3]);
+	
+				if (mipsinfo.i != -1)
+				{
+					semaphores[mipsinfo.i].attr = LE32(psx_ram[a0]);
+					semaphores[mipsinfo.i].option = LE32(psx_ram[a0+1]);
+					semaphores[mipsinfo.i].init = LE32(psx_ram[a0+2]);
+					semaphores[mipsinfo.i].max = LE32(psx_ram[a0+3]);
+
+					semaphores[mipsinfo.i].current = semaphores[mipsinfo.i].init;
+
+					semaphores[mipsinfo.i].inuse = 1;
+				}
+
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 6: // SignalSema
+				#if DEBUG_HLE_IOP
+				printf("IOP: SignalSema(%d) (current %d)\n", a0, semaphores[a0].current);
+				#endif
+
+				foundthread = 0;
+				for (i=0; i < iNumThreads; i++)
+				{
+					if ((threads[i].iState == TS_WAITSEMA) && (threads[i].waitparm == a0))
+					{
+						threads[i].iState = TS_READY;	
+						semaphores[a0].threadsWaiting--;
+						foundthread = 1;
+						break;
+					}
+				}
+
+				mipsinfo.i = 0;
+
+				if (!foundthread)
+				{
+					if (semaphores[a0].current < semaphores[a0].max)
+					{
+						semaphores[a0].current++;
+					}
+					else
+					{
+						mipsinfo.i = -420;	// semaphore overflow
+					}
+				}
+
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 7: // iSignalSema
+				#if DEBUG_HLE_IOP
+				printf("IOP: iSignalSema(%d)\n", a0);
+				#endif
+
+				foundthread = 0;
+				for (i=0; i < iNumThreads; i++)
+				{
+					if ((threads[i].iState == TS_WAITSEMA) && (threads[i].waitparm == a0))
+					{
+						threads[i].iState = TS_READY;	
+						semaphores[a0].threadsWaiting--;
+						foundthread = 1;
+						break;
+					}
+				}
+
+				mipsinfo.i = 0;
+
+				if (!foundthread)
+				{
+					if (semaphores[a0].current < semaphores[a0].max)
+					{
+						semaphores[a0].current++;
+					}
+					else
+					{
+						mipsinfo.i = -420;	// semaphore overflow
+					}
+				}
+
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 8: // WaitSema
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: WaitSema(%d) (cnt %d) (th %d) (PC=%x)\n", a0, iCurThread, semaphores[a0].current, mipsinfo.i);
+				#endif
+		
+				if (semaphores[a0].current > 0)
+				{
+					semaphores[a0].current--;
+				}
+				else
+				{
+					FreezeThread(iCurThread, 1);
+					threads[iCurThread].iState = TS_WAITSEMA;
+					threads[iCurThread].waitparm = a0;
+					ps2_reschedule();
+				}
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "timrman"))
+	{
+		switch (callnum)
+		{
+			case 4:	// AllocHardTimer
+				#if DEBUG_HLE_IOP
+				printf("IOP: AllocHardTimer(%d %d %d)\n", a0, a1, a2);
+				#endif
+				// source, size, prescale
+								     
+				if (a1 != 32)
+				{
+					printf("IOP: AllocHardTimer doesn't support 16-bit timers!\n");
+				}
+
+				iop_timers[iNumTimers].source = a0;
+				iop_timers[iNumTimers].prescale = a2;
+
+				mipsinfo.i = iNumTimers+1;
+				iNumTimers++;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 6: // FreeHardTimer
+				#if DEBUG_HLE_IOP
+				printf("IOP: FreeHardTimer(%d)\n", a0);
+				#endif
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 10:// GetTimerCounter
+				mipsinfo.i = iop_timers[a0-1].count;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 20: // SetTimerHandler
+				#if DEBUG_HLE_IOP
+				printf("IOP: SetTimerHandler(%d %d %08x %08x)\n", a0, a1, a2, a3);
+				#endif
+				// id, compare, handler, common (last is param for handler)
+
+				iop_timers[a0-1].target = a1;
+				iop_timers[a0-1].handler = a2;
+				iop_timers[a0-1].hparam = a3;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 22: // SetupHardTimer
+				#if DEBUG_HLE_IOP
+				printf("IOP: SetupHardTimer(%d %d %d %d)\n", a0, a1, a2, a3);
+				#endif
+				// id, source, mode, prescale
+
+				iop_timers[a0-1].source = a1;
+				iop_timers[a0-1].mode = a2;
+				iop_timers[a0-1].prescale = a3;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 23: // StartHardTimer
+				#if DEBUG_HLE_IOP
+				printf("IOP: StartHardTimer(%d)\n", a0);
+				#endif
+
+				iop_timers[a0-1].iActive = 1;
+				iop_timers[a0-1].count = 0;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 24: // StopHardTimer
+				#if DEBUG_HLE_IOP
+				printf("IOP: StopHardTimer(%d)\n", a0);
+				#endif
+
+				iop_timers[a0-1].iActive = 0;
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "sysclib"))
+	{
+		switch (callnum)
+		{
+			case 12:	// memcpy
+				{
+					uint8 *dst, *src;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: memcpy(%08x, %08x, %d)\n", a0, a1, a2);
+					#endif
+
+					dst = (uint8 *)&psx_ram[(a0&0x1fffff)/4];
+					src = (uint8 *)&psx_ram[(a1&0x1fffff)/4];
+					// get exact byte alignment
+					dst += a0 % 4;
+					src += a1 % 4;
+
+					while (a2)
+					{
+						*dst = *src;
+						dst++;
+						src++;
+						a2--;
+					}
+
+					// v0 = a0
+					mipsinfo.i = a0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+			case 13:	// memmove
+				{
+					uint8 *dst, *src;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: memmove(%08x, %08x, %d)\n", a0, a1, a2);
+					#endif
+
+					dst = (uint8 *)&psx_ram[(a0&0x1fffff)/4];
+					src = (uint8 *)&psx_ram[(a1&0x1fffff)/4];
+					// get exact byte alignment
+					dst += a0 % 4;
+					src += a1 % 4;
+
+					dst += a2 - 1;
+					src += a2 - 1;
+
+					while (a2)
+					{
+						*dst = *src;
+						dst--;
+						src--;
+						a2--;
+					}
+
+					// v0 = a0
+					mipsinfo.i = a0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+			case 14:	// memset
+				{
+					uint8 *dst;
+
+					#if DEBUG_HLE_IOP
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+					printf("IOP: memset(%08x, %02x, %d) [PC=%x]\n", a0, a1, a2, mipsinfo.i);
+					#endif
+
+					dst = (uint8 *)&psx_ram[(a0&0x1fffff)/4];
+					dst += (a0 & 3);
+
+					memset(dst, a1, a2);
+				}
+				break;
+
+			case 17:	// bzero
+				{
+					uint8 *dst;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: bzero(%08x, %08x)\n", a0, a1);
+					#endif
+
+					dst = (uint8 *)&psx_ram[(a0&0x1fffff)/4];
+					dst += (a0 & 3);
+					memset(dst, 0, a1);
+				}
+				break;
+
+			case 19:	// sprintf
+				mname = (char *)psx_ram;
+				str1 = (char *)psx_ram;
+				mname += a0 & 0x1fffff;
+				str1 += a1 & 0x1fffff;
+
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: sprintf(%08x, %s, ...) [PC=%08x]\n", a0, str1, (uint32)mipsinfo.i);
+				printf("%x %x %x %x\n", a0, a1, a2, a3);
+				#endif
+
+				iop_sprintf(mname, str1, CPUINFO_INT_REGISTER + MIPS_R6);	// a2 is first parameter
+
+				#if DEBUG_HLE_IOP
+				printf("     = [%s]\n", mname);
+				#endif
+				break;
+
+			case 23:	// strcpy
+				{
+					uint8 *dst, *src;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: strcpy(%08x, %08x)\n", a0, a1);
+					#endif
+
+					dst = (uint8 *)&psx_ram[(a0&0x1fffff)/4];
+					src = (uint8 *)&psx_ram[(a1&0x1fffff)/4];
+					// get exact byte alignment
+					dst += a0 % 4;
+					src += a1 % 4;
+
+					while (*src != '\0')
+					{
+						*dst = *src;
+						dst++;
+						src++;
+					}
+					*dst = '\0';
+
+					// v0 = a0
+					mipsinfo.i = a0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+			case 27:	// strlen
+				{
+					char *dst;
+
+					#if DEBUG_HLE_IOP
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+					printf("IOP: strlen(%08x) [PC=%x]\n", a0, mipsinfo.i);
+					#endif
+
+					dst = (char *)&psx_ram[(a0&0x1fffff)/4];
+					dst += (a0 & 3);
+					mipsinfo.i = strlen(dst);
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+			case 30:	// strncpy
+				{
+					char *dst, *src;
+
+					#if DEBUG_HLE_IOP
+					printf("IOP: strncpy(%08x, %08x, %d)\n", a0, a1, a2);
+					#endif
+
+					dst = (char *)&psx_ram[(a0&0x1fffff)/4];
+					src = (char *)&psx_ram[(a1&0x1fffff)/4];
+					// get exact byte alignment
+					dst += a0 % 4;
+					src += a1 % 4;
+
+					while ((*src != '\0') && (a2 > 0))
+					{
+						*dst = *src;
+						dst++;
+						src++;
+						a2--;
+					}
+					*dst = '\0';
+
+					// v0 = a0
+					mipsinfo.i = a0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				}
+				break;
+
+
+			case 36:	// strtol
+				mname = (char *)&psx_ram[(a0 & 0x1fffff)/4];
+				mname += (a0 & 3);
+
+				if (a1)
+				{
+					printf("IOP: Unhandled strtol with non-NULL second parm\n");
+				}
+
+				mipsinfo.i = strtol(mname, NULL, a2);
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "intrman"))
+	{
+		switch (callnum)
+		{
+			case 4:	// RegisterIntrHandler
+				#if DEBUG_HLE_IOP
+				printf("IOP: RegisterIntrHandler(%d %08x %08x %08x)\n", a0, a1, a2, a3);
+				#endif
+
+				if (a0 == 9)
+				{
+					irq9_fval = a1;
+					irq9_cb = a2;
+					irq9_flag = a3;
+				}
+
+				// DMA4?
+				if (a0 == 36)
+				{
+					dma4_fval = a1;
+					dma4_cb = a2;
+					dma4_flag = a3;
+				}
+
+				// DMA7?
+				if (a0 == 40)
+				{
+					dma7_fval = a1;
+					dma7_cb = a2;
+					dma7_flag = a3;
+				}
+				break;
+
+			case 5:	// ReleaseIntrHandler
+				#if DEBUG_HLE_IOP
+				printf("IOP: ReleaseIntrHandler(%d)\n", a0);
+				#endif
+				break;
+
+			case 6:	// EnableIntr
+				#if DEBUG_HLE_IOP
+				printf("IOP: EnableIntr(%d)\n", a0);
+				#endif
+				break;
+
+			case 7:	// DisableIntr
+				#if DEBUG_HLE_IOP
+				printf("IOP: DisableIntr(%d)\n", a0);
+				#endif
+				break;
+
+			case 8: // CpuDisableIntr
+				#if DEBUG_HLE_IOP
+				printf("IOP: CpuDisableIntr(%d)\n", a0);
+				#endif
+				break;
+
+			case 9: // CpuEnableIntr
+				#if DEBUG_HLE_IOP
+				printf("IOP: CpuEnableIntr(%d)\n", a0);
+				#endif
+				break;
+
+			case 17:	// CpuSuspendIntr
+				#if DEBUG_HLE_IOP
+				printf("IOP: CpuSuspendIntr\n");
+				#endif
+
+				// if already suspended, return an error code
+				if (intr_susp)
+				{
+					mipsinfo.i = -102;
+				}
+				else
+				{
+					mipsinfo.i = 0;
+				}
+				intr_susp = 1;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 18:	// CpuResumeIntr
+				#if DEBUG_HLE_IOP
+				printf("IOP: CpuResumeIntr\n");
+				#endif
+				intr_susp = 0;
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 23:	// QueryIntrContext
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: QueryIntrContext(PC=%x)\n", mipsinfo.i);
+				#endif
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "loadcore"))
+	{
+		switch (callnum)
+		{
+			case 5: // FlushDcache
+				#if DEBUG_HLE_IOP
+				printf("IOP: FlushDcache()\n");
+				#endif
+				break;
+
+			case 6:	// RegisterLibraryEntries
+				a0 &= 0x1fffff;
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: RegisterLibraryEntries(%08x) (PC=%x)\n", a0, mipsinfo.i);
+				#endif
+				
+				if (psx_ram[a0/4] == LE32(0x41c00000))
+				{
+					a0 += 3*4;
+					memcpy(&reglibs[iNumLibs].name, &psx_ram[a0/4], 8);
+					reglibs[iNumLibs].name[8] = '\0';
+					#if DEBUG_HLE_IOP
+					printf("Lib name [%s]\n", &reglibs[iNumLibs].name);
+					#endif
+					a0 += 2*4;
+					reglibs[iNumLibs].dispatch = a0;
+					iNumLibs++;
+				}
+				else
+				{
+					printf("ERROR: Entry table signature missing\n");
+
+				}
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "sysmem"))
+	{
+		uint32 newAlloc;
+
+		switch (callnum)
+		{
+			case 4:	// AllocMemory
+				newAlloc = psf2_get_loadaddr();
+				// make sure we're 16-byte aligned
+				if (newAlloc & 15)
+				{
+					newAlloc &= ~15;
+					newAlloc += 16;
+				}
+
+				if (a1 & 15)
+				{
+					a1 &= ~15;
+					a1 += 16;
+				}
+
+				if (a1 == 1114112)	// HACK for crappy code in Shadow Hearts rip that assumes the buffer address
+				{
+					printf("SH Hack: was %x now %x\n", newAlloc, 0x60000);
+					newAlloc = 0x60000;
+				}
+
+				psf2_set_loadaddr(newAlloc + a1);
+
+				#if DEBUG_HLE_IOP
+				printf("IOP: AllocMemory(%d, %d, %x) = %08x\n", a0, a1, a2, newAlloc|0x80000000);
+				#endif
+
+				mipsinfo.i = newAlloc; // | 0x80000000;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 5:	// FreeMemory
+				#if DEBUG_HLE_IOP
+				printf("IOP: FreeMemory(%x)\n", a0);
+				#endif
+				break;
+
+			case 7:	// QueryMaxFreeMemSize
+				#if DEBUG_HLE_IOP
+				printf("IOP: QueryMaxFreeMemSize\n");
+				#endif
+
+				mipsinfo.i = (2*1024*1024) - psf2_get_loadaddr();
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 8:	// QueryTotalFreeMemSize
+				#if DEBUG_HLE_IOP
+				printf("IOP: QueryTotalFreeMemSize\n");
+				#endif
+
+				mipsinfo.i = (2*1024*1024) - psf2_get_loadaddr();
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 14: // Kprintf
+				mname = (char *)psx_ram;
+				mname += a0 & 0x1fffff;
+				mname += (a0 & 3);
+
+				iop_sprintf(out, mname, CPUINFO_INT_REGISTER + MIPS_R5);	// a1 is first parm
+				
+				if (out[strlen(out)-1] != '\n')
+				{
+					strcat(out, "\n");
+				}
+
+				// filter out ESC characters
+				{
+					int ch;
+
+					for (ch = 0; ch < strlen(out); ch++)
+					{
+						if (out[ch] == 27)
+						{
+							out[ch] = ']';
+						}
+					}
+				}
+
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("KTTY: %s [PC=%x]\n", out, mipsinfo.i);
+				#endif
+
+				#if 0
+				{
+					FILE *f;
+					f = fopen("psxram.bin", "wb");
+					fwrite(psx_ram, 2*1024*1024, 1, f);
+					fclose(f);
+				}
+				#endif
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+	}
+	else if (!strcmp(name, "modload"))
+	{
+		uint8 *tempmem;
+		uint32 newAlloc;
+
+		switch (callnum)
+		{
+			case 7:	// LoadStartModule
+				mname = (char *)&psx_ram[(a0 & 0x1fffff)/4];
+				mname += 8;
+				str1 = (char *)&psx_ram[(a2 & 0x1fffff)/4];
+				#if DEBUG_HLE_IOP
+				printf("LoadStartModule: %s\n", mname);
+				#endif
+
+				// get 2k for our parameters
+				newAlloc = psf2_get_loadaddr();
+				// force 16-byte alignment
+				if (newAlloc & 0xf)
+				{
+					newAlloc &= ~0xf;
+					newAlloc += 16;
+				}
+				psf2_set_loadaddr(newAlloc + 2048);
+
+				tempmem = (uint8 *)malloc(2*1024*1024);
+				if (psf2_load_file(mname, tempmem, 2*1024*1024) != 0xffffffff)
+				{
+					uint32 start;
+					int i;
+
+					start = psf2_load_elf(tempmem, 2*1024*1024);
+
+					if (start != 0xffffffff)
+					{
+						uint32 args[20], numargs = 1, argofs;
+						uint8 *argwalk = (uint8 *)psx_ram, *argbase;
+						
+						argwalk += (a2 & 0x1fffff);
+						argbase = argwalk;
+
+						args[0] = a0;	// program name is argc[0]
+
+						argofs = 0;
+
+						if (a1 > 0)
+						{
+							args[numargs] = a2;
+							numargs++;
+
+							while (a1)
+							{
+								if ((*argwalk == 0) && (a1 > 1))
+								{
+									args[numargs] = a2 + argofs + 1;
+									numargs++;
+								}
+								argwalk++;
+								argofs++;
+								a1--;
+							}
+						}
+
+						for (i = 0; i < numargs; i++) 
+						{
+							#if DEBUG_HLE_IOP
+//							printf("Arg %d: %08x [%s]\n", i, args[i], &argbase[args[i]-a2]); 
+							#endif
+							psx_ram[(newAlloc/4)+i] = LE32(args[i]);
+						}
+
+						// set argv and argc
+						mipsinfo.i = numargs;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R4, &mipsinfo);
+						mipsinfo.i = 0x80000000 | newAlloc;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R5, &mipsinfo);
+
+						// leave RA alone, PC = module start
+						// (NOTE: we get called in the delay slot!)
+						mipsinfo.i = start - 4;
+						mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+					}
+				}
+				free(tempmem);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+				break;
+		}
+
+	}
+	else if (!strcmp(name, "ioman"))
+	{
+		switch (callnum)
+		{
+			case 4:	// open
+				{
+					int i, slot2use;
+
+					slot2use = -1;
+					for (i = 0; i < MAX_FILE_SLOTS; i++)
+					{
+						if (filestat[i] == 0)
+						{
+							slot2use = i;
+							break;
+						}
+					}
+
+					if (slot2use == -1)
+					{
+						printf("IOP: out of file slots!\n");
+						mipsinfo.i = 0xffffffff;
+						mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+						return;
+					}
+
+					mname = (char *)psx_ram;
+					mname += (a0 & 0x1fffff);
+
+					if (!strncmp(mname, "aofile:", 7))
+					{
+						mname += 8;
+					}
+					else if (!strncmp(mname, "hefile:", 7))
+					{
+						mname += 8;
+					}
+					else if (!strncmp(mname, "host0:", 6)) 
+					{
+						mname += 7;
+					}
+
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+					#if DEBUG_HLE_IOP
+					printf("IOP: open(\"%s\") (PC=%08x)\n", mname, mipsinfo.i);
+					#endif
+
+					filedata[slot2use] = malloc(6*1024*1024);
+					filesize[slot2use] = psf2_load_file(mname, filedata[slot2use], 6*1024*1024);
+					filepos[slot2use] = 0;
+					filestat[slot2use] = 1;
+
+					if (filesize[slot2use] == 0xffffffff)
+					{
+						mipsinfo.i = filesize[slot2use];
+					}
+					else
+					{
+						mipsinfo.i = slot2use;
+					}
+				}
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 5:	// close
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: close(%d) (PC=%08x)\n", a0, mipsinfo.i);
+				#endif
+				free(filedata[a0]);
+				filedata[a0] = (uint8 *)NULL;
+				filepos[a0] = 0;
+				filesize[a0] = 0;
+				filestat[a0] = 0;
+				break;
+
+			case 6:	// read
+				#if DEBUG_HLE_IOP
+				printf("IOP: read(%x %x %d) [pos %d size %d]\n", a0, a1, a2, filepos[a0], filesize[a0]);
+				#endif
+
+				if (filepos[a0] >= filesize[a0])
+				{
+					mipsinfo.i = 0;
+				}
+				else
+				{
+					uint8 *rp;
+
+					if ((filepos[a0] + a2) > filesize[a0])
+					{
+						a2 = filesize[a0] - filepos[a0];
+					}
+
+					rp = (uint8 *)psx_ram;
+					rp += (a1 & 0x1fffff);
+					memcpy(rp, &filedata[a0][filepos[a0]], a2);
+
+					filepos[a0] += a2;
+					mipsinfo.i = a2;
+				}
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 8:	// lseek
+				#if DEBUG_HLE_IOP
+				mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+				printf("IOP: lseek(%d, %d, %s) (PC=%08x)\n", a0, a1, seek_types[a2], mipsinfo.i);
+				#endif
+
+				switch (a2)
+				{
+					case 0:	// SEEK_SET
+						if (a1 <= filesize[a0])
+						{
+							filepos[a0] = a1;
+						}
+						break;
+					case 1:	// SEEK_CUR
+						if ((a1 + filepos[a0]) < filesize[a0])
+						{
+							filepos[a0] += a1;
+						}
+						break;
+					case 2:	// SEEK_END
+						filepos[a0] = filesize[a0] - a1;
+						break;
+				}
+
+				mipsinfo.i = filepos[a0];
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 20:	// AddDrv
+				#if DEBUG_HLE_IOP
+				printf("IOP: AddDrv(%x)\n", a0);
+				#endif
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			case 21:	// DelDrv
+				#if DEBUG_HLE_IOP
+				printf("IOP: DelDrv(%x)\n", a0);
+				#endif
+
+				mipsinfo.i = 0;
+				mips_set_info(CPUINFO_INT_REGISTER + MIPS_R2, &mipsinfo);
+				break;
+
+			default:
+				printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+		}		      
+	}		
+	else
+	{
+		int lib;
+
+		if (iNumLibs > 0)
+		{
+			for (lib = 0; lib < iNumLibs; lib++)
+			{
+				if (!strcmp(name, reglibs[lib].name))
+				{
+					#if DEBUG_HLE_IOP
+					uint32 PC;
+
+					mips_get_info(CPUINFO_INT_REGISTER + MIPS_R31, &mipsinfo);
+					PC = mipsinfo.i;
+					#endif
+
+					// zap the delay slot handling
+					mipsinfo.i = 0;
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYV, &mipsinfo);
+					mips_set_info(CPUINFO_INT_REGISTER + MIPS_DELAYR, &mipsinfo);
+
+					mipsinfo.i = LE32(psx_ram[(reglibs[lib].dispatch/4) + callnum]);
+
+					// (NOTE: we get called in the delay slot!)
+					#if DEBUG_HLE_IOP
+					printf("IOP: Calling %s (%d) service %d => %08x (parms %08x %08x %08x %08x) (PC=%x)\n",
+							 reglibs[lib].name, 
+							 lib,
+							 callnum, 
+							 (uint32)mipsinfo.i, 
+							 a0, a1, a2, a3, PC);
+					#endif
+
+					#if 0
+					if (!strcmp(reglibs[lib].name, "ssd"))
+					{
+						if (callnum == 37)
+						{
+							psxcpu_verbose = 4096;
+						}
+					}
+					#endif
+
+					mipsinfo.i -= 4;
+					mips_set_info(CPUINFO_INT_PC, &mipsinfo);
+
+					return;
+				}
+			}
+		}
+
+		printf("IOP: Unhandled service %d for module %s\n", callnum, name);
+	}
+}
+