/** ** 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 . **/ /* * 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 * ---------- * - 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 #include #include #include #include "Sound/SCSPDSP.h" static const Util::Config::Node *s_config = 0; static bool s_multiThreaded = false; //#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<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<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.state=DECAY1; if(slot->EG.D1R>=(1024<EG.state=DECAY2; slot->EG.volume=0x3ff<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<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<>=(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(const Util::Config::Node &config, int n) { s_config = &config; s_multiThreaded = config["MultiThreaded"].ValueAs(); 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<>(10-SHIFT))|(1<>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<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 (s_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 (s_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("\tSlot %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("\tSlot %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("\tSlot %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;sstep;\ 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)<<8;\ }\ else\ {\ signed short *p=(signed short *) &(slot->base[(slot->cur_addr>>(SHIFT-1))&(~1)]);\ signed int fpart=slot->cur_addr&((1<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)<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)<Back=1;\ }\ if((addrBack)\ slot->cur_addr=LEA(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)<Back=1;\ }\ if((addr<=LSA(slot) || (addr&0x80000000)) && slot->Back) /*reached start or negative*/\ {\ slot->cur_addr=LSA(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;s32767)?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<=(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)<<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<>8)&0xFF)|(p[0]<<8); //s=(int) p[0]*((1<>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)<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)<Back=1; } if((addrBack) slot->cur_addr=LEA(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)<step=REVSIGN(slot->step); slot->Back=1; } if((addrBack)) //reached start or negative { slot->cur_addr=LSA(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; /* * Compute relative master/slave SCSP balance (note: master is often used * for the front speakers). Equal balance is a 1.0 scale factor for both. * When one SCSP is fully attenuated, the other's samples will be multiplied * by 2. */ float balance = (float) s_config->Get("Balance").ValueAs(); if (balance < -100.0f) balance = -100.0f; else if (balance > 100.0f) balance = 100.0f; balance /= 100.0f; float masterBalance = 1.0f+balance; float slaveBalance = 1.0f-balance; /* * Generate samples */ for(int s=0;s0xf) 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=(int) (slaveBalance*(float)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); signed int leftSample = (int) (masterBalance*(float)((SCSPs[0].DSP.EFREG[i]*LPANTABLE[Enc])>>SHIFT)); signed int rightSample = (int) (masterBalance*(float)((SCSPs[0].DSP.EFREG[i]*RPANTABLE[Enc])>>SHIFT)); smpl+=leftSample; smpr+=rightSample; } if(HasSlaveSCSP) { _SLOT *slot=SCSPs[1].Slots+i; ef=EFSDL(slot); if(ef) { unsigned short Enc=0|((EFPAN(slot))<<0x0)|((EFSDL(slot))<<0x5); signed int leftSample = (int) (slaveBalance*(float)((SCSPs[1].DSP.EFREG[i]*LPANTABLE[Enc])>>SHIFT)); signed int rightSample = (int) (slaveBalance*(float)((SCSPs[1].DSP.EFREG[i]*RPANTABLE[Enc])>>SHIFT)); smpl+=leftSample; smpr+=rightSample; } } } #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 (s_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 (s_multiThreaded) MIDILock->Unlock(); } void SCSP_MidiOutW(BYTE val) { /* * MIDI FIFO critical section */ if (s_multiThreaded) MIDILock->Lock(); //printf("68K: MIDI out\n"); //DebugLog("Midi Out Buffer push %02X",val); MidiStack[MidiOutW++]=val; MidiOutW&=7; ++MidiOutFill; if (s_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 (s_multiThreaded) MIDILock->Lock(); val=MidiStack[MidiOutR++]; //DebugLog("Midi Out Buffer pop %02X",val); MidiOutR&=7; --MidiOutFill; if (s_multiThreaded) MIDILock->Unlock(); return val; } unsigned char SCSP_MidiOutFill() { unsigned char v; /* * MIDI FIFO critical section */ if (s_multiThreaded) MIDILock->Lock(); v = MidiOutFill; if (s_multiThreaded) MIDILock->Unlock(); return v; } unsigned char SCSP_MidiInFill() { unsigned char v; /* * MIDI FIFO critical section */ if (s_multiThreaded) MIDILock->Lock(); v = MidiInFill; if (s_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; }