/**
 ** Supermodel
 ** A Sega Model 3 Arcade Emulator.
 ** Copyright 2011 Bart Trzynadlowski, Nik Henson
 **
 ** This file is part of Supermodel.
 **
 ** Supermodel 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 3 of the License, or (at your option)
 ** any later version.
 **
 ** Supermodel is distributed in the hope that it will be useful, but WITHOUT
 ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 ** more details.
 **
 ** You should have received a copy of the GNU General Public License along
 ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.
 **/
 
/*
 * SCSP.cpp
 * 
 * WARNING: Here be dragons! Tread carefully. Enabling/disabling things may
 * break save state support.
 *
 * SCSP (Sega Custom Sound Processor) emulation. This code was generously
 * donated by ElSemi. Interfaces directly to the 68K processor through
 * callbacks. Some minor interface changes were made (external global variables
 * were removed). 
 *
 * The MIDI input buffer has been increased from 8 (which I assume is the
 * actual size) in order to accommodate Model 3's PowerPC/68K communication.
 * There is probably tight synchronization between the CPUs, with PowerPC-side
 * interrupts being generated to fill the MIDI buffer as the 68K pulls data
 * out, or there may be a UART with a large FIFO buffer. This can be simulated
 * by increasing the MIDI buffer (MIDI_STACK_SIZE).
 *
 * To-Do List
 * ----------
 * - Remove asserts() in DSP code and make sure memory allocation errors are
 *   being handled properly.
 * - Wrap up into an object. Remove any unused #ifdef pathways.
 */


/*	
	SEGA Custom Sound Processor (SCSP) Emulation
	by ElSemi.
	Driven by MC68000
*/

#include "Supermodel.h"
#include "Sound/SCSP.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include "Sound/SCSPDSP.h"

//#define NEWSCSP
//#define RB_VOLUME

//#define REVERB
#define USEDSP

#define MAX_SCSP	2
/*#define TIMER_LIMITSA  0x101
#define TIMER_LIMITSB  0x100
#define TIMER_LIMITSC  0xff
*/

#define TIMER_LIMITSA  0xff
#define TIMER_LIMITSB  0xff
#define TIMER_LIMITSC  0xff

// These globals control the operation of the SCSP, they are no longer extern and are set through SCSP_SetBuffers(). --Bart
float SysFPS;
//extern "C" A68KContext M68000_regs;
//extern void __cdecl ErrorLogMessage(char *,...);
signed short *bufferl;		
signed short *bufferr;
int length;
int cnts;

signed int *buffertmpl,*buffertmpr;	// these are allocated inside this file

unsigned int srate=44100;



#define REVERB_LEN	0x10000
#define REVERB_DIF	6000
#define REVERB_DIV	4


signed short bufferrevr[REVERB_LEN];
signed short bufferrevl[REVERB_LEN];
unsigned int RevR,RevW;

//#define _DEBUG

#ifndef _DEBUG
#define ErrorLogMessage
#endif

#ifndef BYTE
#define BYTE UINT8
#endif

#ifndef WORD
#define WORD UINT16
#endif

#ifndef DWORD
#define DWORD UINT32
#endif

static CMutex *MIDILock;	// for safe access to the MIDI FIFOs
static int (*Run68kCB)(int cycles);
static void (*Int68kCB)(int irq);
static void (*RetIntCB)();
static DWORD IrqTimA=1;
static DWORD IrqTimBC=2;
static DWORD IrqMidi=3;

#define MIDI_STACK_SIZE			128
#define MIDI_STACK_SIZE_MASK	(MIDI_STACK_SIZE-1)

static BYTE MidiOutStack[8];
static BYTE MidiOutW=0,MidiOutR=0;
static BYTE MidiStack[MIDI_STACK_SIZE];
static BYTE MidiOutFill;
static BYTE MidiInFill;
static BYTE MidiW=0,MidiR=0;
static BYTE HasSlaveSCSP=0;

static DWORD FNS_Table[0x400];
/*static int TLTABLE[256];
static int LPANTABLE[16];
static int RPANTABLE[16];
*/

#ifdef RB_VOLUME
static int volume[256*4];	// precalculated attenuation values with some marging for enveloppe and pan levels
static int pan_left[32], pan_right[32];	// pan volume offsets
#else
static float SDLT[8]={-1000000.0,-36.0,-30.0,-24.0,-18.0,-12.0,-6.0,0.0};
static int LPANTABLE[0x10000];
static int RPANTABLE[0x10000];
#endif

static int TimPris[3];
static int TimCnt[3];

#define SHIFT	12
#define FIX(v)	((DWORD) ((float) (1<<SHIFT)*(v)))


#define EG_SHIFT	8

#include "SCSPLFO.cpp"

/*
	SCSP features 32 programmable slots
	that can generate FM and PCM (from ROM/RAM) sound
*/
//SLOT PARAMETERS
#define KEYONEX(slot)	((slot->data[0x0]>>0x0)&0x1000)
#define KEYONB(slot)	((slot->data[0x0]>>0x0)&0x0800)
#define SBCTL(slot)		((slot->data[0x0]>>0x9)&0x0003)
#define SSCTL(slot)		((slot->data[0x0]>>0x7)&0x0003)
#define LPCTL(slot)		((slot->data[0x0]>>0x5)&0x0003)
#define PCM8B(slot)		((slot->data[0x0]>>0x0)&0x0010)

#define SA(slot)		(((slot->data[0x0]&0xF)<<16)|(slot->data[0x1]))

#define LSA(slot)		(slot->data[0x2])

#define LEA(slot)		(slot->data[0x3])

#define D2R(slot)		((slot->data[0x4]>>0xB)&0x001F)
#define D1R(slot)		((slot->data[0x4]>>0x6)&0x001F)
#define EGHOLD(slot)	((slot->data[0x4]>>0x0)&0x0020)
#define AR(slot)		((slot->data[0x4]>>0x0)&0x001F)

#define LPSLNK(slot)	((slot->data[0x5]>>0x0)&0x4000)
#define KRS(slot)		((slot->data[0x5]>>0xA)&0x000F)
#define DL(slot)		((slot->data[0x5]>>0x5)&0x001F)
#define RR(slot)		((slot->data[0x5]>>0x0)&0x001F)

#define STWINH(slot)	((slot->data[0x6]>>0x0)&0x0200)
#define SDIR(slot)		((slot->data[0x6]>>0x0)&0x0100)
#define TL(slot)		((slot->data[0x6]>>0x0)&0x00FF)

#define MDL(slot)		((slot->data[0x7]>>0xB)&0x0007)
#define MDXSL(slot)		((slot->data[0x7]>>0x6)&0x003F)
#define MDYSL(slot)		((slot->data[0x7]>>0x0)&0x003F)

#define OCT(slot)		((slot->data[0x8]>>0xB)&0x000F)
#define FNS(slot)		((slot->data[0x8]>>0x0)&0x03FF)

#define LFORE(slot)		((slot->data[0x9]>>0x0)&0x8000)
#define LFOF(slot)		((slot->data[0x9]>>0xA)&0x001F)
#define PLFOWS(slot)	((slot->data[0x9]>>0x8)&0x0003)
#define PLFOS(slot)		((slot->data[0x9]>>0x5)&0x0007)
#define ALFOWS(slot)	((slot->data[0x9]>>0x3)&0x0003)
#define ALFOS(slot)		((slot->data[0x9]>>0x0)&0x0007)

#define ISEL(slot)		((slot->data[0xA]>>0x3)&0x000F)
#define IMXL(slot)		((slot->data[0xA]>>0x0)&0x0007)

#define DISDL(slot)		((slot->data[0xB]>>0xD)&0x0007)
#define DIPAN(slot)		((slot->data[0xB]>>0x8)&0x001F)
#define EFSDL(slot)		((slot->data[0xB]>>0x5)&0x0007)
#define EFPAN(slot)		((slot->data[0xB]>>0x0)&0x001F)

//Envelope step (fixed point)
int ARTABLE[64],DRTABLE[64];

//Envelope times in ms
double ARTimes[64]={100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0,
					1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0,
					76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4,
					2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0};
double DRTimes[64]={100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0,
					14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0,
					920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0,
					28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1};


typedef enum {ATTACK,DECAY1,DECAY2,RELEASE} _STATE;
struct _EG
{
	int volume;	//
	_STATE state;
	int step;
	//step vals
	int AR;		//Attack
	int D1R;	//Decay1
	int D2R;	//Decay2
	int RR;		//Release

	int DL;		//Decay level
	BYTE EGHOLD;
	BYTE LPLINK;
};

struct _SLOT
{
	union
	{
		WORD data[0x10];	//only 0x1a bytes used
		BYTE datab[0x20];
	};
	BYTE active;	//this slot is currently playing
	BYTE *base;		//samples base address
	DWORD cur_addr;	//current play address (24.8)
	DWORD step;		//pitch step (24.8)
	BYTE Back;
	_EG EG;			//Envelope
	_LFO PLFO;		//Phase LFO
	_LFO ALFO;		//Amplitude LFO
	int slot;
	signed short Prev;	//Previous sample (for interpolation)
};

#define MEM4B(scsp)		((scsp->data[0]>>0x0)&0x0200)
#define DAC18B(scsp)	((scsp->data[0]>>0x0)&0x0100)
#define MVOL(scsp)		((scsp->data[0]>>0x0)&0x000F)
#define RBL(scsp)		((scsp->data[1]>>0x7)&0x0003)
#define RBP(scsp)		((scsp->data[1]>>0x0)&0x007F)
#define MOFULL(scsp)	((scsp->data[2]>>0x0)&0x1000)
#define MOEMPTY(scsp)	((scsp->data[2]>>0x0)&0x0800)
#define MIOVF(scsp)		((scsp->data[2]>>0x0)&0x0400)
#define MIFULL(scsp)	((scsp->data[2]>>0x0)&0x0200)
#define MIEMPTY(scsp)	((scsp->data[2]>>0x0)&0x0100)

#define SCILV0(scsp)    ((scsp->data[0x24/2]>>0x0)&0xff)
#define SCILV1(scsp)    ((scsp->data[0x26/2]>>0x0)&0xff)
#define SCILV2(scsp)    ((scsp->data[0x28/2]>>0x0)&0xff)

#define SCIEX0	0
#define SCIEX1	1
#define SCIEX2	2
#define SCIMID	3
#define SCIDMA	4
#define SCIIRQ	5
#define SCITMA	6
#define SCITMB	7

struct _SCSP
{
	union
	{
		WORD data[0x30/2];
		BYTE datab[0x30];
	};
	_SLOT Slots[32];
	signed short RINGBUF[64];
	unsigned char BUFPTR;
	unsigned char *SCSPRAM;	
	char Master;
#ifdef USEDSP
	_SCSPDSP DSP;
	signed short *MIXBuf;
#endif
} SCSPs[MAX_SCSP],*SCSP=SCSPs;

signed short *RBUFDST;	//this points to where the sample will be stored in the RingBuf

unsigned char DecodeSCI(unsigned char irq)
{
	unsigned char SCI=0;
	unsigned char v;
	v=(SCILV0((SCSP))&(1<<irq))?1:0;
	SCI|=v;
	v=(SCILV1((SCSP))&(1<<irq))?1:0;
	SCI|=v<<1;
	v=(SCILV2((SCSP))&(1<<irq))?1:0;
	SCI|=v<<2;
	return SCI;
}

void CheckPendingIRQ()
{
	DWORD pend=SCSP[0].data[0x20/2];
	DWORD en=SCSP[0].data[0x1e/2];
	/*if(pend&0x8)
		if(en&0x8)
		{
			Int68kCB(IrqMidi);
			return;
		}
	*/
	
	/*
	 * MIDI FIFO critical section
	 *
	 * NOTE: I don't think a mutex is really needed here, so I've disabled
	 * this critical section.
	 */
	//if (g_Config.multiThreaded)
	//	MIDILock->Lock();

	if(MidiW!=MidiR)
	{
		//if (g_Config.multiThreaded)
		//	MIDILock->Unlock();
		
		//SCSP.data[0x20/2]|=0x8;	//Hold midi line while there are commands pending
		Int68kCB(IrqMidi);
		//printf("68K: MIDI IRQ\n");
		//ErrorLogMessage("Midi");
		return;
	}
	
	//if (g_Config.multiThreaded)
	//	MIDILock->Unlock();
	
	if(!pend)
		return;
	if(pend&0x40)
		if(en&0x40)
		{
			Int68kCB(IrqTimA);
			//ErrorLogMessage("TimA");
			return;
		}
	if(pend&0x80)
		if(en&0x80)
		{
			Int68kCB(IrqTimBC);
			//ErrorLogMessage("TimB");
			return;
		}
	if(pend&0x100)
		if(en&0x100)
		{
			Int68kCB(IrqTimBC);
			//ErrorLogMessage("TimC");
			return;
		}
	Int68kCB(0);
}

int Get_AR(int base,int R)
{
	int Rate=base+(R<<1);
//	int Rate=(base+R)<<1;
	if(Rate>63)	Rate=63;
	if(Rate<0) Rate=0;
	return ARTABLE[Rate];
}

int Get_DR(int base,int R)
{
	int Rate=base+(R<<1);
//	int Rate=(base+R)<<1;
	if(Rate>63)	Rate=63;
	if(Rate<0) Rate=0;
	return DRTABLE[Rate];
}

int Get_RR(int base,int R)
{
	int Rate=base+(R<<1);
//	int Rate=(base+R)<<1;
	if(Rate>63)	Rate=63;
	if(Rate<0) Rate=0;
	return DRTABLE[Rate];
}

void Compute_EG(_SLOT *slot)
{
	int octave=OCT(slot);
	int rate;
	if(octave&8) octave=octave-16;
	if(KRS(slot)!=0xf)
		rate=2*(octave+KRS(slot))+((FNS(slot)>>9)&1);
	else
		rate=((FNS(slot)>>9)&1);
	slot->EG.volume=0;
	slot->EG.AR=Get_AR(rate,AR(slot));
	slot->EG.D1R=Get_DR(rate,D1R(slot));
	slot->EG.D2R=Get_DR(rate,D2R(slot));
	slot->EG.RR=Get_RR(rate,RR(slot));
	slot->EG.DL=0x1f-DL(slot);
	slot->EG.EGHOLD=EGHOLD(slot);
}

void SCSP_StopSlot(_SLOT *slot,int keyoff);

int EG_Update(_SLOT *slot)
{
	
	switch(slot->EG.state)
	{
		case ATTACK:
			slot->EG.volume+=slot->EG.AR;
			if(slot->EG.volume>=(0x3ff<<EG_SHIFT))
			{
				slot->EG.state=DECAY1;
				if(slot->EG.D1R>=(1024<<EG_SHIFT))	//Skip DECAY1, go directly to DECAY2
					slot->EG.state=DECAY2;
				slot->EG.volume=0x3ff<<EG_SHIFT;
			}
			if(slot->EG.EGHOLD)
				return 0x3ff<<(SHIFT-10);
			break;
		case DECAY1:
			slot->EG.volume-=slot->EG.D1R;
			if((slot->EG.volume>>(EG_SHIFT+5))<=slot->EG.DL)
				slot->EG.state=DECAY2;
			break;
		case DECAY2:
			if(slot->EG.volume<=0 || slot->EG.DL==0)
			{
				slot->EG.volume=0;
				SCSP_StopSlot(slot,0);
			}
			if(D2R(slot)==0)
				return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10);
			slot->EG.volume-=slot->EG.D2R;
			if(slot->EG.volume<=0)
				slot->EG.volume=0;

			break;
		case RELEASE:
			slot->EG.volume-=slot->EG.RR;
			if(slot->EG.volume<=0)
			{
				SCSP_StopSlot(slot,0);
				slot->EG.volume=0;
				slot->EG.state=ATTACK;
			}
			//slot->EG.volume=0;
			break;
		default:
			return 1<<SHIFT;
	}
	return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10);
	
}

DWORD SCSP_Step(_SLOT *slot)
{
	int octave=OCT(slot);
	int Fn;
	/*
	int Fo=44100;
	if(octave&8)
		Fo>>=(16-octave);
	else
		Fo<<=octave;
	Fn=Fo*(((FNS(slot))<<(SHIFT-10))|(1<<SHIFT));
	*/
	Fn=(FNS_Table[FNS(slot)]);	//24.8
	if(octave&8)
		Fn>>=(16-octave);
	else
		Fn<<=octave;


	return Fn/srate;
}

void Compute_LFO(_SLOT *slot)
{
	if(PLFOS(slot)!=0)
		LFO_ComputeStep(&(slot->PLFO),LFOF(slot),PLFOWS(slot),PLFOS(slot),0);
	if(ALFOS(slot)!=0)
		LFO_ComputeStep(&(slot->ALFO),LFOF(slot),ALFOWS(slot),ALFOS(slot),1);
}

void SCSP_StartSlot(_SLOT *slot)
{
	slot->active=1;
	slot->Back=0;
	slot->base=SCSP->SCSPRAM+SA(slot);
	slot->cur_addr=0;
	slot->step=SCSP_Step(slot);	
	Compute_EG(slot);
	slot->EG.state=ATTACK;
	slot->EG.volume=0;
	slot->Prev=0;
	Compute_LFO(slot);
	/*{
		char aux[12];
		static n=0;
		sprintf(aux,"smp%d.raw",n);
		FILE *f=fopen(aux,"wb");
		fwrite(slot->base,LEA(slot),1,f);
		fclose(f);
		++n;
	}
	*/

}

void SCSP_StopSlot(_SLOT *slot,int keyoff)
{
	if(keyoff && slot->EG.state!=RELEASE)
	{
		slot->EG.state=RELEASE;
//		return;
	}
	else
		slot->active=0;
	slot->data[0]&=~0x800;
	//DebugLog("KEYOFF2 %d",slot->slot);
}

#define log2(n) (log((float) n)/log((float) 2))

bool SCSP_Init(int n)
{
	if(n==2)
	{
		SCSP=SCSPs+1;
		memset(SCSP,0,sizeof(SCSP));
		SCSP->Master=0;
		HasSlaveSCSP=1;
#ifdef USEDSP
		SCSPDSP_Init(&SCSP->DSP);
#endif

	}
	SCSP=SCSPs+0;
	memset(SCSP,0,sizeof(SCSP));
#ifdef USEDSP
	SCSPDSP_Init(&SCSP->DSP);
#endif
	SCSP->Master=1;
	RevR=0;
	RevW=REVERB_DIF;
	memset(bufferrevl,0,sizeof(bufferrevl));
	memset(bufferrevr,0,sizeof(bufferrevr));
	MidiR=MidiW=0;
	MidiOutR=MidiOutW=0;
	MidiOutFill=0;
	MidiInFill=0;
	for(int i=0;i<0x400;++i)
	{
		float fcent=(double) 1200.0*log2((double)(((double) 1024.0+(double)i)/(double)1024.0));
		//float fcent=1.0+(float) i/1024.0;
		fcent=(double) 44100.0*pow(2.0,fcent/1200.0);
		FNS_Table[i]=(float) (1<<SHIFT) *fcent;
		//FNS_Table[i]=(i>>(10-SHIFT))|(1<<SHIFT);
		
	}
#ifdef RB_VOLUME
	// Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB
	for(i = 0; i < 256; i++)
		volume[i] = 65536.0*pow(2.0, (-0.375/6.0)*i);
	for(i = 256; i < 256*4; i++)
		volume[i] = 0;

	// Pan values, units are a linear -3dB ramp, i.e. 8 places in the volume[] table.
	for(i = 0; i < 16; i++)
	{
		pan_left[i] = i*8;
		pan_left[i+16] = 0;
		pan_right[i] = 0;
		pan_right[i+16] = i*8;
	}
	// patch in the infinity values
	pan_left[15] = 256;
	pan_right[31] = 256;

#else
	for(int i=0;i<0x10000;++i)
	{
		int iTL =(i>>0x8)&0xff;
		int iPAN=(i>>0x0)&0x1f;
		int iSDL=(i>>0x5)&0x07;

		float TL=1.0;
		float SegaDB=0;
		//2^(-(TL-2^4))
		/*if(iTL&0x01) TL*=0.95760;
		if(iTL&0x02) TL*=0.91700;
		if(iTL&0x04) TL*=0.84090;
		if(iTL&0x08) TL*=0.70711;
		if(iTL&0x10) TL*=0.50000;
		if(iTL&0x20) TL*=0.25000;
		if(iTL&0x40) TL*=0.06250;
		if(iTL&0x80) TL*=0.00391;*/
		if(iTL&0x01) SegaDB-=0.4;
		if(iTL&0x02) SegaDB-=0.8;
		if(iTL&0x04) SegaDB-=1.5;
		if(iTL&0x08) SegaDB-=3;
		if(iTL&0x10) SegaDB-=6;
		if(iTL&0x20) SegaDB-=12;
		if(iTL&0x40) SegaDB-=24;
		if(iTL&0x80) SegaDB-=48;

		TL=pow(10.0,SegaDB/20.0);

		float PAN=1.0;
		//2^(-2^(PAN-2))
		/*if(iPAN&0x1) PAN*=0.70711;
		if(iPAN&0x2) PAN*=0.50000;
		if(iPAN&0x4) PAN*=0.25000;
		if(iPAN&0x8) PAN*=0.06250;
		if(iPAN==0xf) PAN=0.0;*/

		SegaDB=0;
		if(iPAN&0x1) SegaDB-=3;
		if(iPAN&0x2) SegaDB-=6;
		if(iPAN&0x4) SegaDB-=12;
		if(iPAN&0x8) SegaDB-=24;

		if(iPAN==0xf) PAN=0.0;
		else if(iPAN==0x1f) PAN=0.0;
		else PAN=pow(10.0,SegaDB/20.0);

		float LPAN,RPAN;

		if(iPAN<0x10)
		{
			LPAN=PAN;
			RPAN=1.0;
		}
		else
		{
			RPAN=PAN;
			LPAN=1.0;
		}

		float SDL=1.0;
		if(iSDL)
			SDL=pow(10.0,(SDLT[iSDL])/20.0);
		else
			SDL=0.0;

		if(iSDL==0x6)
			int a=1;
		if(iTL==0x3a)
			int a=1;
		
		LPANTABLE[i]=FIX((4.0*LPAN*TL*SDL));
		RPANTABLE[i]=FIX((4.0*RPAN*TL*SDL));


	}
#endif
	/*for(i=0;i<4;++i)	
		ARTABLE[i]=DRTABLE[i]=0;
	for(i=4;i<62;++i)*/

	/*for(i=2;i<62;++i)
	{
		//double t=BaseTimes[i];	//In ms
		double t=BaseTimes2[i/2]/AR2DR;	//In ms
		double step=(1023*1000.0)/((float) srate*t);
		double scale=(double) (1<<EG_SHIFT);
		ARTABLE[i]=(int) (step*scale);
		step/=AR2DR;
		DRTABLE[i]=(int) (step*scale);
	}
	*/
	ARTABLE[0]=DRTABLE[0]=0;	//Infinite time
	ARTABLE[1]=DRTABLE[1]=0;
	for(int i=2;i<64;++i)
	{
		double t,step,scale;
		t=ARTimes[i];	//In ms
		if(t!=0.0)
		{
			step=(1023*1000.0)/((float) srate*t);
			scale=(double) (1<<EG_SHIFT);
			ARTABLE[i]=(int) (step*scale);
		}
		else
			ARTABLE[i]=1024<<EG_SHIFT;

		t=DRTimes[i];	//In ms
		step=(1023*1000.0)/((float) srate*t);
		scale=(double) (1<<EG_SHIFT);
		DRTABLE[i]=(int) (step*scale);
	}
	
	for(int i=0;i<32;++i)
		SCSPs[0].Slots[i].slot=i;

#ifdef USEDSP
	//allocate 0x300 (over 1 frame) * 32 slots * 16 bit
	SCSP->MIXBuf=(signed short *) malloc(0x300*32*sizeof(signed short));
#endif

	LFO_Init();
	buffertmpl = NULL;
	buffertmpr = NULL;
	buffertmpl=(signed int*) malloc(44100*sizeof(signed int));
	if (NULL == buffertmpl)
		return ErrorLog("Insufficient memory for internal SCSP buffers.");
	buffertmpr=(signed int*) malloc(44100*sizeof(signed int));
	if (NULL == buffertmpl)
	{
		free(buffertmpl);
		return ErrorLog("Insufficient memory for internal SCSP buffers.");
	}
	memset(buffertmpl,0,44100*sizeof(signed int));
	memset(buffertmpr,0,44100*sizeof(signed int));
	
	// MIDI FIFO mutex
	MIDILock = CThread::CreateMutex();
	if (NULL == MIDILock)
	{
		free(buffertmpl);
		free(buffertmpr);
		return ErrorLog("Unable to create MIDI mutex!");
	}
	
	return OKAY;
}

void SCSP_SetRAM(int n,unsigned char *r)
{
	SCSPs[n].SCSPRAM=r;
#ifdef USEDSP
	SCSPs[n].DSP.SCSPRAM=(unsigned short*) r;
#endif
}

void SCSP_UpdateSlotReg(int s,int r)
{
	_SLOT *slot=SCSP->Slots+s;
	switch(r&0x3f)
	{
		case 0:
		case 1:
			if(KEYONEX(slot))
			{
				for(int sl=0;sl<32;++sl)
				{
					_SLOT *s2=SCSP->Slots+sl;
					if(!KEYONB(s2) && sl==cnts && s2->active)
						int a=1;
					//if(s2->EG.state!=RELEASE)
					{
						if(KEYONB(s2) && (!s2->active || (s2->active && s2->EG.state==RELEASE)))
						{
							//DebugLog("KEYON %d",sl);
							//printf("68K: KEYON %d\n",sl);
							SCSP_StartSlot(s2);
						}
						if(!KEYONB(s2) && s2->active)
						{
							//s2->active=0;
							SCSP_StopSlot(s2,1);
							//DebugLog("KEYOFF %d",sl);
						}
					}
				}
				slot->data[0]&=~0x1000;
			}
			break;
		case 0x10:
		case 0x11:
			slot->step=SCSP_Step(slot);	
			break;
		case 0xA:
		case 0xB:
			if(slot->active)
				int a=1;
//			if(RR(slot)==0x1f)
//				SCSP_StopSlot(slot,0);
			slot->EG.RR=Get_RR(0,RR(slot));
			slot->EG.DL=0x1f-DL(slot);
			break;
		case 0x12:
		case 0x13:
			Compute_LFO(slot);
			break;
	}
}

void SCSP_UpdateReg(int reg)
{
	switch(reg&0x3f)
	{
		case 0x2:
		case 0x3:
		{
#ifdef USEDSP
			unsigned int v=RBL(SCSP);
			SCSP->DSP.RBP=RBP(SCSP);
			if(v==0)
				SCSP->DSP.RBL=8*1024;
			else if(v==1)
				SCSP->DSP.RBL=16*1024;
			else if(v==2)
				SCSP->DSP.RBL=32*1024;
			else if(v==3)
				SCSP->DSP.RBL=64*1024;
#endif
		}
		break;

		case 0x6:
		case 0x7:
			SCSP_MidiOutW(SCSP->data[0x6/2]&0xff);
			break;

/*		case 0x8:
		case 0x9:
			{
				unsigned char slot=SCSP.data[0x8/2]>>11;	
				int a=1;
				
			}
			break;
*/
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
			{
				int a=1;
			}
			break;
		case 0x18:
		case 0x19:
			if(SCSP->Master)	
			{
				TimPris[0]=1<<((SCSP->data[0x18/2]>>8)&0x7);
				TimCnt[0]=((SCSP->data[0x18/2]&0xff)<<8)|(TimCnt[0]&0xff);
			}
			break;
		case 0x1a:
		case 0x1b:
			if(SCSP->Master)	
			{
				TimPris[1]=1<<((SCSP->data[0x1A/2]>>8)&0x7);
				TimCnt[1]=((SCSP->data[0x1A/2]&0xff)<<8)|(TimCnt[1]&0xff);
			}
			break;
		case 0x1C:
		case 0x1D:
			if(SCSP->Master)	
			{
				TimPris[2]=1<<((SCSP->data[0x1C/2]>>8)&0x7);
				TimCnt[2]=((SCSP->data[0x1C/2]&0xff)<<8)|(TimCnt[2]&0xff);
			}
			break;
		case 0x22:	//SCIRE
		case 0x23:
			if(SCSP->Master)	
			{
				SCSP->data[0x20/2]&=~SCSP->data[0x22/2];
				CheckPendingIRQ();
			}
			break;
		case 0x24:
		case 0x25:
		case 0x26:
		case 0x27:
		case 0x28:
		case 0x29:
			if(SCSP->Master)
			{
				IrqTimA=DecodeSCI(SCITMA);
				IrqTimBC=DecodeSCI(SCITMB);
				IrqMidi=DecodeSCI(SCIMID);
			}
			break;
	}
}

void SCSP_UpdateSlotRegR(int slot,int reg)
{

}

void SCSP_UpdateRegR(int reg)
{
	switch(reg&0x3f)
	{
		case 4:
		case 5:
			{
				unsigned short v=SCSP->data[0x5/2];
				v&=0xff00;
				
				/*
				 * MIDI FIFO critical section!
				 */
				if (g_Config.multiThreaded)
					MIDILock->Lock();
					
				v|=MidiStack[MidiR];
				//printf("read MIDI\n");
				if(MidiR!=MidiW)
				{
					++MidiR;
					MidiR&=MIDI_STACK_SIZE_MASK;
					//Int68kCB(IrqMidi);
				}
				
				MidiInFill--;
				SCSP->data[0x5/2]=v;
				
				if (g_Config.multiThreaded)
					MIDILock->Unlock();
			}
			break;
		case 8:
		case 9:
			{
				unsigned char slot=SCSP->data[0x8/2]>>11;	
				unsigned int CA=SCSP->Slots[slot&0x1f].cur_addr>>(SHIFT+12);
				SCSP->data[0x8/2]&=~(0x780);
				SCSP->data[0x8/2]|=CA<<7;
			}
			break;
	}
}


void SCSP_w8(unsigned int addr,unsigned char val)
{
	addr&=0xffff;
	if(addr<0x400)
	{
		int slot=addr/0x20;
		addr&=0x1f;
		//DebugLog("Slot %02X Reg %02X write byte %04X\n",slot,addr^1,val);
		//printf("Slot %02X Reg %02X write byte %04X\n",slot,addr^1,val);
		*(unsigned char *) &(SCSP->Slots[slot].datab[addr^1]) = val;
		SCSP_UpdateSlotReg(slot,(addr^1)&0x1f);
	}
	else if(addr<0x600)
	{
		*(unsigned char *) &(SCSP->datab[(addr&0xff)^1]) = val;
		SCSP_UpdateReg((addr^1)&0xff);
	}	
	else if(addr<0x700)
		SCSP->RINGBUF[(addr-0x600)/2]=val;
	else
	{
#ifdef USEDSP
		//DSP
		if(addr<0x780)	//COEF
			((unsigned char *) SCSP->DSP.COEF)[(addr-0x700)^1]=val;
		else if(addr<0x7C0)
			((unsigned char *) SCSP->DSP.MADRS)[(addr-0x780)^1]=val;
		else if(addr>=0x800 && addr<0xC00)
			((unsigned char *) SCSP->DSP.MPRO)[(addr-0x800)^1]=val;
		else
			int a=1;
		if(addr==0xBFE)
		{
			SCSPDSP_Start(&SCSP->DSP);
		}
		int a=1;
#endif
	}
}

void SCSP_w16(unsigned int addr,unsigned short val)
{
	addr&=0xffff;
	if(addr<0x400)
	{
		int slot=addr/0x20;
		addr&=0x1f;
		//DebugLog("Slot %02X Reg %02X write word %04X\n",slot,addr,val);
		//printf("Slot %02X Reg %02X write word %04X\n",slot,addr,val);
		*(unsigned short *) &(SCSP->Slots[slot].datab[addr]) = val;
		SCSP_UpdateSlotReg(slot,addr&0x1f);
	}
	else if(addr<0x600)
	{
		*(unsigned short *) &(SCSP->datab[addr&0xff]) = val;
		SCSP_UpdateReg(addr&0xff);
	}	
	else if(addr<0x700)
		SCSP->RINGBUF[(addr-0x600)/2]=val;
	else
	{
#ifdef USEDSP
		//DSP
		if(addr<0x780)	//COEF
			*(unsigned short *) &(SCSP->DSP.COEF[(addr-0x700)/2])=val;
		else if(addr<0x800)
			*(unsigned short *) &(SCSP->DSP.MADRS[(addr-0x780)/2])=val;
		else if(addr<0xC00)
			*(unsigned short *) &(SCSP->DSP.MPRO[(addr-0x800)/2])=val;
		else
			int a=1;
		if(addr==0xBFE)
			SCSPDSP_Start(&SCSP->DSP);
		int a=1;
#endif

	}
}

void SCSP_w32(unsigned int addr,unsigned int val)
{
	addr&=0xffff;
	
	if(addr<0x400)
	{
		int slot=addr/0x20;
		addr&=0x1f;
		//DebugLog("Slot %02X Reg %02X write dword %08X\n",slot,addr,val);
		//printf("Slot %02X Reg %02X write dword %08X\n",slot,addr,val);
		rotl(val, 16);

		*(unsigned int *) &(SCSP->Slots[slot].datab[addr]) = val;
		SCSP_UpdateSlotReg(slot,addr&0x1f);
		SCSP_UpdateSlotReg(slot,(addr&0x1f)+2);
	}
	else if(addr<0x600)
	{
		rotl(val, 16);

		*(unsigned int *) &(SCSP->datab[addr&0xff]) = val;
		SCSP_UpdateReg(addr&0xff);
		SCSP_UpdateReg((addr&0xff)+2);
	}	
	else if(addr<0x700)
		int a=1;
	else
	{
#ifdef USEDSP
		//DSP
		rotl(val, 16);
			if(addr<0x780)	//COEF
				*(unsigned int *) &(SCSP->DSP.COEF[(addr-0x700)/2])=val;
			else if(addr<0x800)
				*(unsigned int *) &(SCSP->DSP.MADRS[(addr-0x780)/2])=val;
			else if(addr<0xC00)
				*(unsigned int *) &(SCSP->DSP.MPRO[(addr-0x800)/2])=val;
			else
				int a=1;
			if(addr==0xBFC)
				SCSPDSP_Start(&SCSP->DSP);
			int a=1;
#endif
	}
}

unsigned char SCSP_r8(unsigned int addr)
{
	unsigned char v=0;
	addr&=0xffff;
	if(addr<0x400)
	{
		int slot=addr/0x20;
		addr&=0x1f;
		SCSP_UpdateSlotRegR(slot,(addr^1)&0x1f);
		
		v=*(unsigned char *) &(SCSP->Slots[slot].datab[addr^1]);
		//DebugLog("Slot %02X Reg %02X Read byte %02X",slot,addr^1,v);
	}
	else if(addr<0x600)
	{
		SCSP_UpdateRegR(addr&0xff);
		v= *(unsigned char *) &(SCSP->datab[(addr&0xff)^1]);
		//ErrorLogMessage("SCSP Reg %02X Read byte %02X",addr&0xff,v);		
	}	
	else if(addr<0x700)
		v=0;
	return v;
}

unsigned short SCSP_r16(unsigned int addr)
{
	unsigned short v=0;
	addr&=0xffff;
	if(addr<0x400)
	{
		int slot=addr/0x20;
		addr&=0x1f;
		SCSP_UpdateSlotRegR(slot,addr&0x1f);
		v=*(unsigned short *) &(SCSP->Slots[slot].datab[addr]);
		//DebugLog("Slot %02X Reg %02X Read word %04X",slot,addr,v);
	}
	else if(addr<0x600)
	{
		SCSP_UpdateRegR(addr&0xff);
		v= *(unsigned short *) &(SCSP->datab[addr&0xff]);
		//ErrorLogMessage("SCSP Reg %02X Read word %04X",addr&0xff,v);
	}	
	else if(addr<0x700)
		v=SCSP->RINGBUF[(addr-0x600)/2];
	return v;
}

unsigned int SCSP_r32(unsigned int addr)
{
	return 0xffffffff;
}

#define REVSIGN(v) ((~v)+1)


void SCSP_TimersAddTicks2(int ticks)
{
		//Update timers
		WORD cnt;
		WORD step;
		//Timer A
		
		if(!TimPris[0])
		{
			//cnt=SCSPs[0].data[0x18/2]&0xff;
			cnt=TimCnt[0];
			if(cnt==0xffff)
				goto noTA;
			++cnt;
			++TimCnt[0];
			if(cnt>=TIMER_LIMITSA)
			{
				/*if((SCSPs[0].data[0x20/2]&SCSPs[0].data[0x1e/2])&0x40)	//timer pending ack
					int a=1;*/
				SCSPs[0].data[0x20/2]|=0x40;
				/*if(SCSP.data[0x1e/2]&0x40)
					Int68kCB(IrqTimA);*/
				cnt=0xff;
				TimCnt[0]=0xffff;
			}
			step=1<<((SCSPs[0].data[0x18/2]>>8)&0x7);
			TimPris[0]=step;
			SCSPs[0].data[0x18/2]&=0xff00;
			SCSPs[0].data[0x18/2]|=cnt;
		}
//		else
			TimPris[0]--;
noTA:
;
		//Timer B
		
		if(!TimPris[1])
		{
			//cnt=SCSPs[0].data[0x1a/2]&0xff;
			cnt=TimCnt[1];
			if(cnt==0xffff)
				goto noTB;
			++cnt;
			++TimCnt[1];
			if(cnt>=TIMER_LIMITSB)
			{
				/*if((SCSP.data[0x20/2]&SCSP.data[0x1e/2])&0x80)	//timer pending ack
					int a=1;*/
				SCSPs[0].data[0x20/2]|=0x80;
				/*if(SCSP.data[0x1e/2]&0x80)
					Int68kCB(IrqTimBC);*/
				cnt=0xff;
				TimCnt[1]=0xffff;
			}
			step=1<<((SCSP[0].data[0x1a/2]>>8)&0x7);
			TimPris[1]=step;
			SCSPs[0].data[0x1a/2]&=0xff00;
			SCSPs[0].data[0x1a/2]|=cnt;
		}
//		else
			TimPris[1]--;
noTB:
;
		//Timer C
		
		if(!TimPris[2])
		{
			//cnt=SCSPs[0].data[0x1c/2]&0xff;
			cnt=TimCnt[2];
			if(cnt==0xffff)
				goto noTC;
			++cnt;
			++TimCnt[2];
			if(cnt>=TIMER_LIMITSC)
			{
				/*if((SCSP.data[0x20/2]&SCSP.data[0x1e/2])&0x100)	//timer pending ack
					int a=1;*/
				SCSP[0].data[0x20/2]|=0x100;
				/*if(SCSP.data[0x1e/2]&0x100)
					Int68kCB(IrqTimBC);*/
				cnt=0xff;
				TimCnt[2]=0xffff;
			}
			step=1<<((SCSPs[0].data[0x1c/2]>>8)&0x7);
			TimPris[2]=step;
			SCSPs[0].data[0x1c/2]&=0xff00;
			SCSPs[0].data[0x1c/2]|=cnt;
		}
//		else
			TimPris[2]--;
noTC:
;
}

void SCSP_TimersAddTicks(int ticks)
{
	if(TimCnt[0]<=0xff00)
	{
		TimCnt[0]+=ticks << (8-((SCSPs[0].data[0x18/2]>>8)&0x7));
		if (TimCnt[0] > 0xFE00)
		{
			TimCnt[0] = 0xFFFF;
			SCSPs[0].data[0x20/2]|=0x40;
		}
		SCSPs[0].data[0x18/2]&=0xff00;
		SCSPs[0].data[0x18/2]|=TimCnt[0]>>8;
	}
	if(TimCnt[1]<=0xff00)
	{
		TimCnt[1]+=ticks << (8-((SCSPs[0].data[0x1a/2]>>8)&0x7));
		if (TimCnt[1] > 0xFE00)
		{
			TimCnt[1] = 0xFFFF;
			SCSPs[0].data[0x20/2]|=0x80;
		}
		SCSPs[0].data[0x1a/2]&=0xff00;
		SCSPs[0].data[0x1a/2]|=TimCnt[1]>>8;

	}
	if(TimCnt[2]<=0xff00)
	{
		TimCnt[2]+=ticks << (8-((SCSPs[0].data[0x1c/2]>>8)&0x7));
		if (TimCnt[2] > 0xFE00)
		{
			TimCnt[2] = 0xFFFF;
			SCSPs[0].data[0x20/2]|=0x100;
		}
		SCSPs[0].data[0x1c/2]&=0xff00;
		SCSPs[0].data[0x1c/2]|=TimCnt[2]>>8;
	}

}


#ifdef NEWSCSP

#ifdef USEDSP
const bool hasDSP=true;

#else
const bool hasDSP=false;
#endif
signed short *bufmix;

signed int *bufl1,*bufr1;
#define SCSPNAME(_8bit,lfo,alfo,loop) \
void SCSP_Update##_8bit##lfo##alfo##loop(_SLOT *slot,unsigned int Enc,unsigned int nsamples)

//TRUST ON THE COMPILER OPTIMIZATIONS
#define SCSPTMPL(_8bit,lfo,alfo,loop) \
SCSPNAME(_8bit,lfo,alfo,loop)\
{\
	signed int sample;\
	DWORD addr;\
	for(unsigned int s=0;s<nsamples;++s)\
	{\
		int step=slot->step;\
		if(!slot->active)\
			return;\
		if(lfo) \
		{\
			step=step*PLFO_Step(&(slot->PLFO));\
			step>>=SHIFT; \
		}\
		if(_8bit)\
		{\
			unsigned int offs=(slot->cur_addr>>SHIFT);\
			signed char *p=(signed char *) (slot->base);\
			int s;\
			signed int fpart=slot->cur_addr&((1<<SHIFT)-1);\
			s=(int) p[offs^1]*((1<<SHIFT)-fpart)+(int) p[(offs+1)^1]*fpart;\
			sample=(s>>SHIFT)<<8;\
		}\
		else\
		{\
			signed short *p=(signed short *) &(slot->base[(slot->cur_addr>>(SHIFT-1))&(~1)]);\
			signed int fpart=slot->cur_addr&((1<<SHIFT)-1);\
			sample=(p[0]);\
		}\
		if(loop==0)\
		{\
			slot->cur_addr+=step;\
			addr=slot->cur_addr>>SHIFT;\
			if(addr>LEA(slot))\
			{\
				SCSP_StopSlot(slot,0);\
			}\
		}\
		if(loop==1)\
		{\
			slot->cur_addr+=step;\
			addr=slot->cur_addr>>SHIFT;\
			if(addr>LEA(slot))\
				slot->cur_addr=(LSA(slot)+1)<<SHIFT;\
		}\
		if(loop==2)\
		{\
			if(slot->Back)\
				slot->cur_addr+=REVSIGN(step);\
			else\
				slot->cur_addr+=step;\
			addr=slot->cur_addr>>SHIFT;\
			if(addr>=LEA(slot))\
			{\
				slot->cur_addr=LEA(slot)<<SHIFT;\
				slot->Back=1;\
			}\
			if((addr<LSA(slot) || (addr&0x80000000)) && slot->Back)\
				slot->cur_addr=LEA(slot)<<SHIFT;\
		}\
		if(loop==3)\
		{\
			if(slot->Back)\
				slot->cur_addr+=REVSIGN(step);\
			else\
				slot->cur_addr+=step;\
			addr=slot->cur_addr>>SHIFT;\
			if(addr>=LEA(slot)) /*reached end, reverse till start*/ \
			{\
				slot->cur_addr=LEA(slot)<<SHIFT;\
				slot->Back=1;\
			}\
			if((addr<=LSA(slot) || (addr&0x80000000)) && slot->Back) /*reached start or negative*/\
			{\
				slot->cur_addr=LSA(slot)<<SHIFT;\
				slot->Back=0;\
			}\
		}\
		if(alfo)\
		{\
			sample=sample*ALFO_Step(&(slot->ALFO));\
			sample>>=SHIFT;\
		}\
		*RBUFDST=sample;\
		\
		sample=(sample*EG_Update(slot))>>SHIFT;\
		if(hasDSP)\
			*bufmix++=((sample*LPANTABLE[(Enc|0xE0)&0xFFE0])>>(SHIFT+3));\
	\
		*bufl1=*bufl1 + ((sample*LPANTABLE[Enc])>>SHIFT);\
		*bufr1=*bufr1 + ((sample*RPANTABLE[Enc])>>SHIFT);\
		++bufl1;\
		++bufr1;\
	}\
}

SCSPTMPL(0,0,0,0) SCSPTMPL(0,0,0,1) SCSPTMPL(0,0,0,2) SCSPTMPL(0,0,0,3)
SCSPTMPL(0,0,1,0) SCSPTMPL(0,0,1,1) SCSPTMPL(0,0,1,2) SCSPTMPL(0,0,1,3)
SCSPTMPL(0,1,0,0) SCSPTMPL(0,1,0,1) SCSPTMPL(0,1,0,2) SCSPTMPL(0,1,0,3)
SCSPTMPL(0,1,1,0) SCSPTMPL(0,1,1,1) SCSPTMPL(0,1,1,2) SCSPTMPL(0,1,1,3)
SCSPTMPL(1,0,0,0) SCSPTMPL(1,0,0,1) SCSPTMPL(1,0,0,2) SCSPTMPL(1,0,0,3)
SCSPTMPL(1,0,1,0) SCSPTMPL(1,0,1,1) SCSPTMPL(1,0,1,2) SCSPTMPL(1,0,1,3)
SCSPTMPL(1,1,0,0) SCSPTMPL(1,1,0,1) SCSPTMPL(1,1,0,2) SCSPTMPL(1,1,0,3)
SCSPTMPL(1,1,1,0) SCSPTMPL(1,1,1,1) SCSPTMPL(1,1,1,2) SCSPTMPL(1,1,1,3)

#undef SCSPTMPL
#define SCSPTMPL(_8bit,lfo,alfo,loop) \
 SCSP_Update##_8bit##lfo##alfo##loop ,


typedef void (*_SCSPUpdateModes)(_SLOT *,unsigned int,unsigned int);

_SCSPUpdateModes SCSPUpdateModes[]=
{
	SCSPTMPL(0,0,0,0) SCSPTMPL(0,0,0,1) SCSPTMPL(0,0,0,2) SCSPTMPL(0,0,0,3)
	SCSPTMPL(0,0,1,0) SCSPTMPL(0,0,1,1) SCSPTMPL(0,0,1,2) SCSPTMPL(0,0,1,3)
	SCSPTMPL(0,1,0,0) SCSPTMPL(0,1,0,1) SCSPTMPL(0,1,0,2) SCSPTMPL(0,1,0,3)
	SCSPTMPL(0,1,1,0) SCSPTMPL(0,1,1,1) SCSPTMPL(0,1,1,2) SCSPTMPL(0,1,1,3)
	SCSPTMPL(1,0,0,0) SCSPTMPL(1,0,0,1) SCSPTMPL(1,0,0,2) SCSPTMPL(1,0,0,3)
	SCSPTMPL(1,0,1,0) SCSPTMPL(1,0,1,1) SCSPTMPL(1,0,1,2) SCSPTMPL(1,0,1,3)
	SCSPTMPL(1,1,0,0) SCSPTMPL(1,1,0,1) SCSPTMPL(1,1,0,2) SCSPTMPL(1,1,0,3)
	SCSPTMPL(1,1,1,0) SCSPTMPL(1,1,1,1) SCSPTMPL(1,1,1,2) SCSPTMPL(1,1,1,3)

};

#define SCANLINES	210

void SCSP_CpuRunScanline()
{
	int slice=12000000/(44100);
	static unsigned int smp=0;
	smp+=(unsigned int) ((256.0*44100.0)/((float) SCANLINES*SysFPS));
	int lastdiff=0;
	for(;smp&0xffffff00;)
	{
		lastdiff=Run68kCB(slice+lastdiff);
		/*while(slice>0)
		{
			lastdiff=Run68kCB(1);
			slice+=lastdiff;
			if(M68000_regs.pc==0x0602620)
				int a=1;
		}*/
		SCSP_TimersAddTicks(1);
		CheckPendingIRQ();
		smp-=0x100;
	}
}

void SCSP_DoMasterSamples(int nsamples)
{

	static int lastdiff=0;
	signed short *bufr,*bufl;
	
	
	for(int sl=0;sl<32;++sl)
	{
		bufr1=buffertmpr;
		bufl1=buffertmpl;
//		if(sl!=0x3)
//			continue;

		if(SCSPs[0].Slots[sl].active)
		{
			_SLOT *slot=SCSPs[0].Slots+sl;
			unsigned int disdl=DISDL(slot);
			unsigned int efsdl=EFSDL(slot);
			unsigned int tl=TL(slot);
			unsigned short Enc=((TL(slot))<<0x8)|((DIPAN(slot))<<0x0)|((DISDL(slot))<<0x5);
			//unsigned short Enc=(0x00)|((DIPAN(slot))<<0x8)|((0x7)<<0xd);
			unsigned int mode=LPCTL(slot);

/*			if(SSCTL(slot)!=0)	//no FM or noise yet
				int a=1;
			int MDX=MDXSL(slot);
			int MDY=MDYSL(slot);
			int LEV=MDL(slot);
			if(LEV!=0 || MDX!=0 || MDY!=0)
				goto norender;
			if(SBCTL(slot))
				int a=1;

*/
			RBUFDST=SCSPs[0].RINGBUF+SCSPs[0].BUFPTR;
			if(sl==0x15)
				int a=1;
			if(PLFOS(slot))
				mode|=8;
			if(ALFOS(slot))
				mode|=4;
			if(PCM8B(slot))
				mode|=0x10;
#ifdef USEDSP
			bufmix=SCSPs[0].MIXBuf+0x300*slot->slot;
#endif

			SCSPUpdateModes[mode](slot,Enc,nsamples);
//norender:
//;
			
		}
		++SCSPs[0].BUFPTR;
		SCSPs[0].BUFPTR&=63;
	}
	if(HasSlaveSCSP)
	{
		for(int sl=0;sl<32;++sl)
		{
			bufr1=buffertmpr;
			bufl1=buffertmpl;
			if(SCSPs[1].Slots[sl].active)
			{
				_SLOT *slot=SCSPs[1].Slots+sl;
				//unsigned short Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((0x7)<<0xd);
				unsigned short Enc=((TL(slot))<<0x8)|((DIPAN(slot))<<0x0)|((DISDL(slot))<<0x5);
				unsigned int mode=LPCTL(slot);
				
				if(PLFOS(slot))
					mode|=8;
				if(ALFOS(slot))
					mode|=4;
				if(PCM8B(slot))
					mode|=0x10;
				RBUFDST=SCSPs[1].RINGBUF+SCSPs[1].BUFPTR;
#ifdef USEDSP
				bufmix=SCSPs[1].MIXBuf+0x300*slot->slot;
#endif
				SCSPUpdateModes[mode](slot,Enc,nsamples);
				
			}
			++SCSPs[1].BUFPTR;
			SCSPs[1].BUFPTR&=63;
		}
	}
	bufr=bufferr;
	bufl=bufferl;
	bufr1=buffertmpr;
	bufl1=buffertmpl;
	for(int s=0;s<nsamples;++s)
	{
#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x)
		signed int smpl=*bufl1;
		signed int smpr=*bufr1;
#ifdef USEDSP
		signed short *pt=SCSPs[0].MIXBuf+s;
		for(int sl=0;sl<32;++sl)
		{
			_SLOT *slot=SCSPs[0].Slots+sl;
			if(slot->active)
			{
				SCSPDSP_SetSample(&SCSP[0].DSP,pt[0],ISEL(slot),IMXL(slot));
			}
			pt+=0x300;
		}
		SCSPDSP_Step(&SCSP[0].DSP);
		if(HasSlaveSCSP)
		{
			pt=SCSPs[1].MIXBuf+s;
			for(int sl=0;sl<32;++sl)
			{
				_SLOT *slot=SCSPs[1].Slots+sl;
				if(slot->active)
				{
					SCSPDSP_SetSample(&SCSP[1].DSP,pt[0],ISEL(slot),IMXL(slot));
				}
				pt+=0x300;
			}
			SCSPDSP_Step(&SCSP[1].DSP);
		}

		//		smpl=0;
		//		smpr=0;
		for(int i=0;i<16;++i)
		{
			_SLOT *slot=SCSPs[0].Slots+i;
			int ef=EFSDL(slot);
			if(ef)
			{
				unsigned short Enc=0|((EFPAN(slot))<<0x0)|((EFSDL(slot))<<0x5);
				smpl+=(SCSPs[0].DSP.EFREG[i]*LPANTABLE[Enc])>>SHIFT;
				smpr+=(SCSPs[0].DSP.EFREG[i]*RPANTABLE[Enc])>>SHIFT;
			}
			
			if(HasSlaveSCSP)
			{
				_SLOT *slot=SCSPs[1].Slots+i;
				ef=EFSDL(slot);
				if(ef)
				{
					unsigned short Enc=0|((EFPAN(slot))<<0x0)|((EFSDL(slot))<<0x5);
					smpl+=(SCSPs[1].DSP.EFREG[i]*LPANTABLE[Enc])>>SHIFT;
					smpr+=(SCSPs[1].DSP.EFREG[i]*RPANTABLE[Enc])>>SHIFT;
				}
			}
		}
#endif
#ifdef REVERB
		smpl+=bufferrevl[RevR];
		smpr+=bufferrevr[RevR];
		bufferrevl[RevW]=((smpl<<0)/REVERB_DIV)>>0;
		bufferrevr[RevW]=((smpr<<0)/REVERB_DIV)>>0;
		++RevW;
		if(RevW==REVERB_LEN)
			RevW=0;
		++RevR;
		if(RevR==REVERB_LEN)
			RevR=0;
#endif
		*bufl=ICLIP16(smpl);
		*bufr=ICLIP16(smpr);
		*bufl1=0;
		*bufr1=0;
		++bufl;
		++bufr;
		++bufl1;
		++bufr1;

	}
}


#else

signed int inline SCSP_UpdateSlot(_SLOT *slot)
{
	signed int sample;
	int step=slot->step;
	DWORD addr;

	if(SSCTL(slot)!=0)	//no FM or noise yet
		return 0;
	
	if(PLFOS(slot)!=0)
	{
		step=step*PLFO_Step(&(slot->PLFO));
		step>>=SHIFT;
	}

	if(PCM8B(slot))
		addr=slot->cur_addr>>SHIFT;
	else
		addr=(slot->cur_addr>>(SHIFT-1))&(~1);

	if(MDL(slot)!=0 || MDXSL(slot)!=0 || MDYSL(slot)!=0)
	{
//TODO: is this correct? SCSP is not necessarily set to the slave SCSP. ElSemi may not have noticed this.
		unsigned char v;
		signed int smp=(SCSP->RINGBUF[(SCSP->BUFPTR+MDXSL(slot))&63]+SCSP->RINGBUF[(SCSP->BUFPTR+MDYSL(slot))&63])/2;
		
		smp>>=11;
		// Check for underflow before adding to addr
		if (smp >= 0 || (DWORD)(-smp) < addr)
			addr+=smp;
		else 
			addr = 0;
		if(!PCM8B(slot))
			addr&=~1;
	}

	if(PCM8B(slot))	//8 bit signed
	{	
		signed char *p=(signed char *) &(slot->base[addr^1]);
		int s;
		signed int fpart=slot->cur_addr&((1<<SHIFT)-1);
		sample=(p[0])<<8;
		
		//if (p>=(signed char *) &SCSP->SCSPRAM[0x200000])
		//	printf("%X %X %X %p %p %p\n", addr, SA(slot), LEA(slot), p, slot->base, SCSP->SCSPRAM);
		
		/*s=(int) p[0]*((1<<SHIFT)-fpart)+(int) p[1]*fpart;
		sample=(s>>SHIFT)<<8;
		*/
/*		if(SBCTL(slot)&1)	//reverse data
			sample^=0x7f;
		if(SBCTL(slot)&2)	//reverse sign
			sample^=0x80;
*/		
	}
	else	//16 bit signed (endianness?)
	{
		signed short *p=(signed short *) &(slot->base[addr]);
		int s;
		signed int fpart=slot->cur_addr&((1<<SHIFT)-1);
		sample=(p[0]);
		//sample=((p[0]>>8)&0xFF)|(p[0]<<8);
		//s=(int) p[0]*((1<<SHIFT)-fpart)+(int) p[1]*fpart;
		//sample=s>>SHIFT;

		/*		if(SBCTL(slot)&1)	//reverse data
		sample^=0x7fff;
		if(SBCTL(slot)&2)	//reverse sign
		sample^=0x8000;
		*/
	}

	
	switch(LPCTL(slot))
	{
	case 0:	//no loop
		slot->cur_addr+=step;
		addr=slot->cur_addr>>SHIFT;
		if(addr>LEA(slot))
		{
			//slot->active=0;
			SCSP_StopSlot(slot,0);
		}
		break;
	case 1: //normal loop
		slot->cur_addr+=step;
		addr=slot->cur_addr>>SHIFT;
		if(addr>LEA(slot))
			slot->cur_addr=LSA(slot)<<SHIFT;
		break;
	case 2:	//reverse loop
		if(slot->Back)
			slot->cur_addr+=REVSIGN(step);
		else
			slot->cur_addr+=step;
		addr=slot->cur_addr>>SHIFT;
		if(addr>LEA(slot))
		{
			slot->cur_addr=LEA(slot)<<SHIFT;
			slot->Back=1;
		}
		if((addr<LSA(slot) || (addr&0x80000000)) && slot->Back)
			slot->cur_addr=LEA(slot)<<SHIFT;
		break;
	case 3: //ping-pong
		if(slot->Back)
			slot->cur_addr+=REVSIGN(step);
		else
			slot->cur_addr+=step;
		addr=slot->cur_addr>>SHIFT;
		if(addr>LEA(slot)) //reached end, reverse till start
		{
			slot->cur_addr=LEA(slot)<<SHIFT;
			//slot->step=REVSIGN(slot->step);
			slot->Back=1;
		}
		if((addr<LSA(slot) || (addr&0x80000000)) && (slot->Back)) //reached start or negative
		{
			slot->cur_addr=LSA(slot)<<SHIFT;
			//slot->step=REVSIGN(slot->step);
			slot->Back=0;
		}
		break;
	}

	if(ALFOS(slot)!=0)
	{
		sample=sample*ALFO_Step(&(slot->ALFO));
		sample>>=SHIFT;
	}

	if(!STWINH(slot))
		*RBUFDST=sample;
	else 
		int a=1;

	sample=(sample*EG_Update(slot))>>SHIFT;

	return sample;
}

void SCSP_CpuRunScanline()
{

}

void SCSP_DoMasterSamples(int nsamples)
{
	int slice=12000000/(SysFPS*nsamples);	// 68K cycles/sample
	static int lastdiff=0;
	
	for(int s=0;s<nsamples;++s)
	{
		signed int smpl=0;
		signed int smpr=0;

		for(int sl=0;sl<32;++sl)
		{
			if(SCSPs[0].Slots[sl].active)
			{
				_SLOT *slot=SCSPs[0].Slots+sl;
				unsigned short Enc=((TL(slot))<<0x8)|((DIPAN(slot))<<0x0)|((DISDL(slot))<<0x5);
				RBUFDST=SCSPs[0].RINGBUF+SCSPs[0].BUFPTR;
				signed int sample;
				//signed int sample=SCSP_UpdateSlot(slot);
				//if(SA(slot)!=0x2ccf4)
				/*if(SA(slot)!=0x1c77e)
					sample=0;
				else*/
					sample=SCSP_UpdateSlot(slot);

				
				/*unsigned char ef=EFSDL(slot);
				ef+=DISDL(slot);
				if(ef>0xf) ef=0xf;
				unsigned short Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((ef)<<0xd);
				*/
#ifdef USEDSP
				SCSPDSP_SetSample(&SCSPs[0].DSP,/*sample>>5*/(sample*LPANTABLE[(Enc|0xE0)&0xFFE0])>>(SHIFT+3)/*>>SHIFT*/,ISEL(slot),IMXL(slot));
#endif

#ifdef RB_VOLUME
				smpl += (sample * volume[TL(slot)+pan_left [DIPAN(slot)]])>>17;
				smpr += (sample * volume[TL(slot)+pan_right[DIPAN(slot)]])>>17;
#else				

				//if(sl==cnts)
				{
					smpl+=(sample*LPANTABLE[Enc])>>SHIFT;
					smpr+=(sample*RPANTABLE[Enc])>>SHIFT;
				}
#endif
			}
			++SCSPs[0].BUFPTR;
			SCSPs[0].BUFPTR&=63;
			if(HasSlaveSCSP)
			{
				if(SCSPs[1].Slots[sl].active)
				{
					_SLOT *slot=SCSPs[1].Slots+sl;
					unsigned short Enc=((TL(slot))<<0x8)|((DIPAN(slot))<<0x0)|((DISDL(slot))<<0x5);
					RBUFDST=SCSPs[1].RINGBUF+SCSPs[1].BUFPTR;
					signed int sample=SCSP_UpdateSlot(slot);
#ifdef USEDSP
					SCSPDSP_SetSample(&SCSPs[1].DSP,(sample*LPANTABLE[(Enc|0xE0)&0xFFE0])>>(SHIFT+3),ISEL(slot),IMXL(slot));
#endif
#ifdef RB_VOLUME
					smpl += (sample * volume[TL(slot)+pan_left [DIPAN(slot)]])>>17;
					smpr += (sample * volume[TL(slot)+pan_right[DIPAN(slot)]])>>17;
#else				
					smpl+=(sample*LPANTABLE[Enc])>>SHIFT;
					smpr+=(sample*RPANTABLE[Enc])>>SHIFT;
#endif
				}
				++SCSPs[1].BUFPTR;
				SCSPs[1].BUFPTR&=63;
			}
		}
#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x)
#ifdef USEDSP
		SCSPDSP_Step(&SCSPs[0].DSP);
		if(HasSlaveSCSP)
			SCSPDSP_Step(&SCSPs[1].DSP);

//		smpl=0;
//		smpr=0;
		for(int i=0;i<16;++i)
		{
			_SLOT *slot=SCSPs[0].Slots+i;
			int ef=EFSDL(slot);
			if(ef)
			{
				unsigned short Enc=0|((EFPAN(slot))<<0x0)|((EFSDL(slot))<<0x5);
				smpl+=(SCSPs[0].DSP.EFREG[i]*LPANTABLE[Enc])>>SHIFT;
				smpr+=(SCSPs[0].DSP.EFREG[i]*RPANTABLE[Enc])>>SHIFT;
			}
			if(HasSlaveSCSP)
			{
				_SLOT *slot=SCSPs[1].Slots+i;
				ef=EFSDL(slot);
				if(ef)
				{
					unsigned short Enc=0|((EFPAN(slot))<<0x0)|((EFSDL(slot))<<0x5);
					smpl+=(SCSPs[1].DSP.EFREG[i]*LPANTABLE[Enc])>>SHIFT;
					smpr+=(SCSPs[1].DSP.EFREG[i]*RPANTABLE[Enc])>>SHIFT;
				}
			}
		}
#endif
#ifdef REVERB
		smpl+=bufferrevl[RevR];
		smpr+=bufferrevr[RevR];
		bufferrevl[RevW]=((smpl<<0)/REVERB_DIV)>>0;
		bufferrevr[RevW]=((smpr<<0)/REVERB_DIV)>>0;
		++RevW;
		if(RevW==REVERB_LEN)
			RevW=0;
		++RevR;
		if(RevR==REVERB_LEN)
			RevR=0;
#endif
		if(smpl<-32768)
			smpl=-32768;
		else if(smpl>32767)
			smpl=32767;
		bufferl[s]=smpl;
		//bufferl[s]=ICLIP16(smpl);
		bufferr[s]=ICLIP16(smpr);

		SCSP_TimersAddTicks(1);
		CheckPendingIRQ();

		/*for(int nc=slice;nc;nc--)
		{
		Run68kCB(1);		
		if(M68000_regs.pc==0x6035A6)
		{

		int a=1;
		}
		}
		*/


		lastdiff=Run68kCB(slice-lastdiff);
	}
}
#endif

void SCSP_Update()
{
	SCSP_DoMasterSamples(length);
}

void SCSP_SetCB(int (*Run68k)(int cycles),void (*Int68k)(int irq))
{
	Int68kCB=Int68k;
	Run68kCB=Run68k;
}

void SCSP_MidiIn(BYTE val)
{
	/*
	 * MIDI FIFO critical section
	 */
	if (g_Config.multiThreaded)
		MIDILock->Lock();
		
	//DebugLog("Midi Buffer push %02X",val);
	MidiStack[MidiW++]=val;
	MidiW&=MIDI_STACK_SIZE_MASK;
	MidiInFill++;
	//Int68kCB(IrqMidi);
//	SCSP.data[0x20/2]|=0x8;

	if (g_Config.multiThreaded)
		MIDILock->Unlock();
}

void SCSP_MidiOutW(BYTE val)
{
	/*
	 * MIDI FIFO critical section
	 */
	if (g_Config.multiThreaded)
		MIDILock->Lock();

	//printf("68K: MIDI out\n");
	//DebugLog("Midi Out Buffer push %02X",val);
	MidiStack[MidiOutW++]=val;
	MidiOutW&=7;
	++MidiOutFill;
	
	if (g_Config.multiThreaded)
		MIDILock->Unlock();
}


unsigned char SCSP_MidiOutR()
{
	unsigned char val;

	if(MidiOutR==MidiOutW)	// I don't think this needs to be a critical section...
		return 0xff;
		
	/*
	 * MIDI FIFO critical section
	 */
	if (g_Config.multiThreaded)
		MIDILock->Lock();

	val=MidiStack[MidiOutR++];
	//DebugLog("Midi Out Buffer pop %02X",val);
	MidiOutR&=7;
	--MidiOutFill;
	
	if (g_Config.multiThreaded)
		MIDILock->Unlock();
		
	return val;
}

unsigned char SCSP_MidiOutFill()
{
	unsigned char v;
	
	/*
	 * MIDI FIFO critical section
	 */
	if (g_Config.multiThreaded)
		MIDILock->Lock();

	v = MidiOutFill;
	
	if (g_Config.multiThreaded)
		MIDILock->Unlock();
	
	return v;
}

unsigned char SCSP_MidiInFill()
{
	unsigned char v;
	
	/*
	 * MIDI FIFO critical section
	 */
	if (g_Config.multiThreaded)
		MIDILock->Lock();

	v = MidiInFill;
	
	if (g_Config.multiThreaded)
		MIDILock->Unlock();
	
	return v;
}

void SCSP_RTECheck()
{
/*	unsigned short pend=SCSP.data[0x20/2]&0xfff;
	if(pend)
	{
		if(pend&0x40)
		{
			Int68kCB(IrqTimA);
			return;
		}
		if(pend&(0x80|0x100))
		{
			Int68kCB(IrqTimBC);
			return;
		}
		if(pend&0x8)
			Int68kCB(IrqMidi);
	}
*/
}

int SCSP_IRQCB(int)
{
	CheckPendingIRQ();
	return -1;
}

void SCSP_Master_w8(unsigned int addr,unsigned char val)
{
	SCSP=SCSPs+0;
	SCSP_w8(addr,val);
}

void SCSP_Master_w16(unsigned int addr,unsigned short val)
{
	SCSP=SCSPs+0;
	SCSP_w16(addr,val);
}

void SCSP_Master_w32(unsigned int addr,unsigned int val)
{
	SCSP=SCSPs+0;
	SCSP_w32(addr,val);
}

void SCSP_Slave_w8(unsigned int addr,unsigned char val)
{
	SCSP=SCSPs+1;
	SCSP_w8(addr,val);
}

void SCSP_Slave_w16(unsigned int addr,unsigned short val)
{
	SCSP=SCSPs+1;
	SCSP_w16(addr,val);
}

void SCSP_Slave_w32(unsigned int addr,unsigned int val)
{
	SCSP=SCSPs+1;
	SCSP_w32(addr,val);
}

unsigned char SCSP_Master_r8(unsigned int addr)
{
	SCSP=SCSPs+0;
	return SCSP_r8(addr);
}

unsigned short SCSP_Master_r16(unsigned int addr)
{
	SCSP=SCSPs+0;
	return SCSP_r16(addr);
}

unsigned int SCSP_Master_r32(unsigned int addr)
{
	SCSP=SCSPs+0;
	return SCSP_r32(addr);
}

unsigned char SCSP_Slave_r8(unsigned int addr)
{
	SCSP=SCSPs+1;
	return SCSP_r8(addr);
}

unsigned short SCSP_Slave_r16(unsigned int addr)
{
	SCSP=SCSPs+1;
	return SCSP_r16(addr);
}

unsigned int SCSP_Slave_r32(unsigned int addr)
{
	SCSP=SCSPs+1;
	return SCSP_r32(addr);
}


/******************************************************************************
 Supermodel Interface Functions
******************************************************************************/

void SCSP_SaveState(CBlockFile *StateFile)
{
	StateFile->NewBlock("SCSP x 2", __FILE__);
	
	/*
	 * Save global variables.
	 *
	 * Difficult to say exactly what is necessary given that many things are
	 * commented out and should not be enabled but I try to save as much as
	 * possible. 
	 *
	 * Things not saved:
	 *
	 *	- Reverb buffers and pointers
	 *	- FNS table (populated by SCSP_Init() and only read)
	 *	- RB_VOLUME stuff
	 * 	- ARTABLE, DRTABLE
	 *	- RBUFDST
	 */
	StateFile->Write(&IrqTimA, sizeof(IrqTimA));
	StateFile->Write(&IrqTimBC, sizeof(IrqTimBC));
	StateFile->Write(&IrqMidi, sizeof(IrqMidi));
	StateFile->Write(MidiOutStack, sizeof(MidiOutStack));
	StateFile->Write(&MidiOutW, sizeof(MidiOutW));
	StateFile->Write(&MidiOutR, sizeof(MidiOutR));
	StateFile->Write(MidiStack, sizeof(MidiStack));
	StateFile->Write(&MidiOutFill, sizeof(MidiOutFill));
	StateFile->Write(&MidiInFill, sizeof(MidiInFill));
	StateFile->Write(&MidiW, sizeof(MidiW));
	StateFile->Write(&MidiR, sizeof(MidiR));
	StateFile->Write(TimPris, sizeof(TimPris));
	StateFile->Write(TimCnt, sizeof(TimCnt));
	
	// Save both SCSP states
	for (int i = 0; i < 2; i++)
	{
		StateFile->Write(SCSPs[i].datab, sizeof(SCSPs[i].datab));
		StateFile->Write(&(SCSPs[i].BUFPTR), sizeof(SCSPs[i].BUFPTR));
		StateFile->Write(&(SCSPs[i].Master), sizeof(SCSPs[i].Master));
		
		// Save each slot
		for (int j = 0; j < 32; j++)
		{
			UINT64	baseOffset;
			UINT8	egState;
			
			StateFile->Write(SCSPs[i].Slots[j].datab, sizeof(SCSPs[i].Slots[j].datab));
			StateFile->Write(&(SCSPs[i].Slots[j].active), sizeof(SCSPs[i].Slots[j].active));
			baseOffset = (UINT64) (SCSPs[i].Slots[j].base - SCSPs[i].SCSPRAM);
			StateFile->Write(&baseOffset, sizeof(baseOffset));
			StateFile->Write(&(SCSPs[i].Slots[j].cur_addr), sizeof(SCSPs[i].Slots[j].cur_addr));
			StateFile->Write(&(SCSPs[i].Slots[j].step), sizeof(SCSPs[i].Slots[j].step));
			StateFile->Write(&(SCSPs[i].Slots[j].Back), sizeof(SCSPs[i].Slots[j].Back));
			StateFile->Write(&(SCSPs[i].Slots[j].slot), sizeof(SCSPs[i].Slots[j].slot));
			StateFile->Write(&(SCSPs[i].Slots[j].Prev), sizeof(SCSPs[i].Slots[j].Prev));
			
			// EG
			StateFile->Write(&(SCSPs[i].Slots[j].EG.volume), sizeof(SCSPs[i].Slots[j].EG.volume));
			egState = SCSPs[i].Slots[j].EG.state;
			StateFile->Write(&egState, sizeof(egState));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.step), sizeof(SCSPs[i].Slots[j].EG.step));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.AR), sizeof(SCSPs[i].Slots[j].EG.AR));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.D1R), sizeof(SCSPs[i].Slots[j].EG.D1R));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.D2R), sizeof(SCSPs[i].Slots[j].EG.D2R));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.RR), sizeof(SCSPs[i].Slots[j].EG.RR));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.DL), sizeof(SCSPs[i].Slots[j].EG.DL));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.EGHOLD), sizeof(SCSPs[i].Slots[j].EG.EGHOLD));
			StateFile->Write(&(SCSPs[i].Slots[j].EG.LPLINK), sizeof(SCSPs[i].Slots[j].EG.LPLINK));
			
			// PLFO
			StateFile->Write(&(SCSPs[i].Slots[j].PLFO.phase), sizeof(SCSPs[i].Slots[j].PLFO.phase));
			StateFile->Write(&(SCSPs[i].Slots[j].PLFO.phase_step), sizeof(SCSPs[i].Slots[j].PLFO.phase_step));
			
			// ALFO
			StateFile->Write(&(SCSPs[i].Slots[j].ALFO.phase), sizeof(SCSPs[i].Slots[j].ALFO.phase));
			StateFile->Write(&(SCSPs[i].Slots[j].ALFO.phase_step), sizeof(SCSPs[i].Slots[j].ALFO.phase_step));
			
			//when loading, make sure to compute lfo
		}
		
		// DSP
		StateFile->Write(&(SCSPs[i].DSP.RBP), sizeof(SCSPs[i].DSP.RBP));
		StateFile->Write(&(SCSPs[i].DSP.RBL), sizeof(SCSPs[i].DSP.RBL));
		StateFile->Write(SCSPs[i].DSP.COEF, sizeof(SCSPs[i].DSP.COEF));
		StateFile->Write(SCSPs[i].DSP.MADRS, sizeof(SCSPs[i].DSP.MADRS));
		StateFile->Write(SCSPs[i].DSP.MPRO, sizeof(SCSPs[i].DSP.MPRO));
		StateFile->Write(SCSPs[i].DSP.TEMP, sizeof(SCSPs[i].DSP.TEMP));
		StateFile->Write(SCSPs[i].DSP.MEMS, sizeof(SCSPs[i].DSP.MEMS));
		StateFile->Write(&(SCSPs[i].DSP.DEC), sizeof(SCSPs[i].DSP.DEC));
		StateFile->Write(SCSPs[i].DSP.MIXS, sizeof(SCSPs[i].DSP.MIXS));
		StateFile->Write(SCSPs[i].DSP.EXTS, sizeof(SCSPs[i].DSP.EXTS));
		StateFile->Write(SCSPs[i].DSP.EFREG, sizeof(SCSPs[i].DSP.EFREG));
		StateFile->Write(&(SCSPs[i].DSP.Stopped), sizeof(SCSPs[i].DSP.Stopped));
		StateFile->Write(&(SCSPs[i].DSP.LastStep), sizeof(SCSPs[i].DSP.LastStep));
	}
}

void SCSP_LoadState(CBlockFile *StateFile)
{
	if (OKAY != StateFile->FindBlock("SCSP x 2"))
	{
		ErrorLog("Unable to load SCSP state. Save state file is corrupt.");
		return;
	}
	
	// Load global variables
	StateFile->Read(&IrqTimA, sizeof(IrqTimA));
	StateFile->Read(&IrqTimBC, sizeof(IrqTimBC));
	StateFile->Read(&IrqMidi, sizeof(IrqMidi));
	StateFile->Read(MidiOutStack, sizeof(MidiOutStack));
	StateFile->Read(&MidiOutW, sizeof(MidiOutW));
	StateFile->Read(&MidiOutR, sizeof(MidiOutR));
	StateFile->Read(MidiStack, sizeof(MidiStack));
	StateFile->Read(&MidiOutFill, sizeof(MidiOutFill));
	StateFile->Read(&MidiInFill, sizeof(MidiInFill));
	StateFile->Read(&MidiW, sizeof(MidiW));
	StateFile->Read(&MidiR, sizeof(MidiR));
	StateFile->Read(TimPris, sizeof(TimPris));
	StateFile->Read(TimCnt, sizeof(TimCnt));
	
	// Load both SCSP states
	for (int i = 0; i < 2; i++)
	{
		StateFile->Read(SCSPs[i].datab, sizeof(SCSPs[i].datab));
		StateFile->Read(&(SCSPs[i].BUFPTR), sizeof(SCSPs[i].BUFPTR));
		StateFile->Read(&(SCSPs[i].Master), sizeof(SCSPs[i].Master));
		
		// Load each slot
		for (int j = 0; j < 32; j++)
		{
			UINT64	baseOffset;
			UINT8	egState;
			
			StateFile->Read(SCSPs[i].Slots[j].datab, sizeof(SCSPs[i].Slots[j].datab));
			StateFile->Read(&(SCSPs[i].Slots[j].active), sizeof(SCSPs[i].Slots[j].active));
			StateFile->Read(&baseOffset, sizeof(baseOffset));
			SCSPs[i].Slots[j].base = &(SCSPs[i].SCSPRAM[baseOffset&0xFFFFF]);	// clamp to 1 MB
			StateFile->Read(&(SCSPs[i].Slots[j].cur_addr), sizeof(SCSPs[i].Slots[j].cur_addr));
			StateFile->Read(&(SCSPs[i].Slots[j].step), sizeof(SCSPs[i].Slots[j].step));
			StateFile->Read(&(SCSPs[i].Slots[j].Back), sizeof(SCSPs[i].Slots[j].Back));
			StateFile->Read(&(SCSPs[i].Slots[j].slot), sizeof(SCSPs[i].Slots[j].slot));
			StateFile->Read(&(SCSPs[i].Slots[j].Prev), sizeof(SCSPs[i].Slots[j].Prev));
			
			// EG
			StateFile->Read(&(SCSPs[i].Slots[j].EG.volume), sizeof(SCSPs[i].Slots[j].EG.volume));
			StateFile->Read(&egState, sizeof(egState));
			SCSPs[i].Slots[j].EG.state = (_STATE) egState;
			StateFile->Read(&(SCSPs[i].Slots[j].EG.step), sizeof(SCSPs[i].Slots[j].EG.step));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.AR), sizeof(SCSPs[i].Slots[j].EG.AR));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.D1R), sizeof(SCSPs[i].Slots[j].EG.D1R));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.D2R), sizeof(SCSPs[i].Slots[j].EG.D2R));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.RR), sizeof(SCSPs[i].Slots[j].EG.RR));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.DL), sizeof(SCSPs[i].Slots[j].EG.DL));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.EGHOLD), sizeof(SCSPs[i].Slots[j].EG.EGHOLD));
			StateFile->Read(&(SCSPs[i].Slots[j].EG.LPLINK), sizeof(SCSPs[i].Slots[j].EG.LPLINK));
			
			// PLFO
			StateFile->Read(&(SCSPs[i].Slots[j].PLFO.phase), sizeof(SCSPs[i].Slots[j].PLFO.phase));
			StateFile->Read(&(SCSPs[i].Slots[j].PLFO.phase_step), sizeof(SCSPs[i].Slots[j].PLFO.phase_step));
			
			// ALFO
			StateFile->Read(&(SCSPs[i].Slots[j].ALFO.phase), sizeof(SCSPs[i].Slots[j].ALFO.phase));
			StateFile->Read(&(SCSPs[i].Slots[j].ALFO.phase_step), sizeof(SCSPs[i].Slots[j].ALFO.phase_step));

			// Recompute LFOs
			Compute_LFO(&(SCSPs[i].Slots[j]));
		}
		
		// DSP
		StateFile->Read(&(SCSPs[i].DSP.RBP), sizeof(SCSPs[i].DSP.RBP));
		StateFile->Read(&(SCSPs[i].DSP.RBL), sizeof(SCSPs[i].DSP.RBL));
		StateFile->Read(SCSPs[i].DSP.COEF, sizeof(SCSPs[i].DSP.COEF));
		StateFile->Read(SCSPs[i].DSP.MADRS, sizeof(SCSPs[i].DSP.MADRS));
		StateFile->Read(SCSPs[i].DSP.MPRO, sizeof(SCSPs[i].DSP.MPRO));
		StateFile->Read(SCSPs[i].DSP.TEMP, sizeof(SCSPs[i].DSP.TEMP));
		StateFile->Read(SCSPs[i].DSP.MEMS, sizeof(SCSPs[i].DSP.MEMS));
		StateFile->Read(&(SCSPs[i].DSP.DEC), sizeof(SCSPs[i].DSP.DEC));
		StateFile->Read(SCSPs[i].DSP.MIXS, sizeof(SCSPs[i].DSP.MIXS));
		StateFile->Read(SCSPs[i].DSP.EXTS, sizeof(SCSPs[i].DSP.EXTS));
		StateFile->Read(SCSPs[i].DSP.EFREG, sizeof(SCSPs[i].DSP.EFREG));
		StateFile->Read(&(SCSPs[i].DSP.Stopped), sizeof(SCSPs[i].DSP.Stopped));
		StateFile->Read(&(SCSPs[i].DSP.LastStep), sizeof(SCSPs[i].DSP.LastStep));
	}
}

void SCSP_SetBuffers(INT16 *leftBufferPtr, INT16 *rightBufferPtr, int bufferLength)
{
	SysFPS = 60.0;	// should this be updated to reflect actual FPS?
	bufferl = leftBufferPtr;
	bufferr = rightBufferPtr;
	length = bufferLength;
	
	cnts = 0;		// what is this for? seems unimportant but need to find out
}

void SCSP_Deinit(void)
{
#ifdef USEDSP
	free(SCSP->MIXBuf);
#endif
	free(buffertmpl);
	free(buffertmpr);
	delete MIDILock;
	buffertmpl = NULL;
	buffertmpr = NULL;
	MIDILock = NULL;
}