--- /dev/null
+/*\r
+\r
+Name:\r
+DRV_SS.C\r
+\r
+Description:\r
+Mikmod driver for output on Ensoniq Soundscape / Soundscape ELITE\r
+\r
+Portability:\r
+\r
+MSDOS: BC(y) Watcom(y) DJGPP(y)\r
+Win95: n\r
+Os2: n\r
+Linux: n\r
+\r
+(y) - yes\r
+(n) - no (not possible or not useful)\r
+(?) - may be possible, but not tested\r
+\r
+*/\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <dos.h>\r
+#include <malloc.h>\r
+#include <conio.h>\r
+#include <string.h>\r
+\r
+#include "mikmod.h"\r
+#include "mdma.h"\r
+#include "mirq.h"\r
+\r
+/***************************************************************************\r
+>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SS stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
+***************************************************************************/\r
+\r
+/* Ensoniq gate-array chip defines ... */\r
+\r
+#define ODIE 0 /* ODIE gate array */\r
+#define OPUS 1 /* OPUS gate array */\r
+#define MMIC 2 /* MiMIC gate array */\r
+\r
+/* relevant direct register defines - offsets from base address */\r
+#define GA_HOSTCTL_OFF 2 /* host port ctrl/stat reg */\r
+#define GA_ADDR_OFF 4 /* indirect address reg */\r
+#define GA_DATA_OFF 5 /* indirect data reg */\r
+#define GA_CODEC_OFF 8 /* for some boards CoDec is fixed from base */\r
+\r
+/* relevant indirect register defines */\r
+#define GA_DMAB_REG 3 /* DMA chan B assign reg */\r
+#define GA_INTCFG_REG 4 /* interrupt configuration reg */\r
+#define GA_DMACFG_REG 5 /* DMA configuration reg */\r
+#define GA_CDCFG_REG 6 /* CD-ROM/CoDec config reg */\r
+#define GA_HMCTL_REG 9 /* host master control reg */\r
+\r
+\r
+/* AD-1848 or compatible CoDec defines ... */\r
+/* relevant direct register defines - offsets from base */\r
+#define CD_ADDR_OFF 0 /* indirect address reg */\r
+#define CD_DATA_OFF 1 /* indirect data reg */\r
+#define CD_STATUS_OFF 2 /* status register */\r
+\r
+/* relevant indirect register defines */\r
+#define CD_ADCL_REG 0 /* left DAC input control reg */\r
+#define CD_ADCR_REG 1 /* right DAC input control reg */\r
+#define CD_CDAUXL_REG 2 /* left DAC output control reg */\r
+#define CD_CDAUXR_REG 3 /* right DAC output control reg */\r
+#define CD_DACL_REG 6 /* left DAC output control reg */\r
+#define CD_DACR_REG 7 /* right DAC output control reg */\r
+#define CD_FORMAT_REG 8 /* clock and data format reg */\r
+#define CD_CONFIG_REG 9 /* interface config register */\r
+#define CD_PINCTL_REG 10 /* external pin control reg */\r
+#define CD_UCOUNT_REG 14 /* upper count reg */\r
+#define CD_LCOUNT_REG 15 /* lower count reg */\r
+#define CD_XFORMAT_REG 28 /* extended format reg - 1845 record */\r
+#define CD_XUCOUNT_REG 30 /* extended upper count reg - 1845 record */\r
+#define CD_XLCOUNT_REG 31 /* extended lower count reg - 1845 record */\r
+\r
+#define CD_MODE_CHANGE 0x40 /* mode change mask for addr reg */\r
+\r
+/****************************************************************************\r
+hardware config info ...\r
+****************************************************************************/\r
+\r
+static UWORD BasePort; /* Gate Array/MPU-401 base port */\r
+static UWORD MidiIrq; /* the MPU-401 IRQ */\r
+static UWORD WavePort; /* the AD-1848 base port */\r
+static UWORD WaveIrq; /* the PCM IRQ */\r
+static UWORD DmaChan; /* the PCM DMA channel */\r
+\r
+/****************************************************************************\r
+all kinds of stuff ...\r
+****************************************************************************/\r
+\r
+static UWORD Windx; /* Wave IRQ index - for reg writes */\r
+static UWORD Mindx; /* MIDI IRQ index - for reg writes */\r
+\r
+static UBYTE IcType; /* the Ensoniq chip type */\r
+static UBYTE CdCfgSav; /* gate array register save area */\r
+static UBYTE DmaCfgSav; /* gate array register save area */\r
+static UBYTE IntCfgSav; /* gate array register save area */\r
+\r
+static UWORD const SsIrqs[4] = { 9, 5, 7, 10 }; /* Soundscape IRQs */\r
+static UWORD const RsIrqs[4] = { 9, 7, 5, 15 }; /* an older IRQ set */\r
+static UWORD const *Irqs; /* pointer to one of the IRQ sets */\r
+\r
+static UBYTE DacSavL; /* DAC left volume save */\r
+static UBYTE DacSavR; /* DAC right volume save */\r
+static UBYTE CdxSavL; /* CD/Aux left volume save */\r
+static UBYTE CdxSavR; /* CD/Aux right volume save */\r
+static UBYTE AdcSavL; /* ADC left volume save */\r
+static UBYTE AdcSavR; /* ADC right volume save */\r
+\r
+static DMAMEM *SS_DMAMEM;\r
+static char *SS_DMABUF;\r
+\r
+\r
+/***************************************************************************\r
+>>>>>>>>>>>>>>>>>>>>>>>>> The actual SS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
+***************************************************************************/\r
+\r
+\r
+\r
+UBYTE GaRead(UWORD rnum)\r
+/*\r
+ This function is used to read the indirect addressed registers in the\r
+ Ensoniq Soundscape gate array.\r
+\r
+ INPUTS:\r
+ rnum - the numner of the indirect register to be read\r
+\r
+ RETURNS:\r
+ the contents of the indirect register are returned\r
+*/\r
+{\r
+ outportb(BasePort + GA_ADDR_OFF, rnum);\r
+ return inportb(BasePort + GA_DATA_OFF);\r
+}\r
+\r
+\r
+\r
+void GaWrite(UWORD rnum,UBYTE value)\r
+/*\r
+ This function is used to write the indirect addressed registers in the\r
+ Ensoniq Soundscape gate array.\r
+\r
+ INPUTS:\r
+ rnum - the numner of the indirect register to be read\r
+ value - the byte value to be written to the indirect register\r
+\r
+ RETURNS:\r
+ Nothing\r
+*/\r
+{\r
+ outportb(BasePort + GA_ADDR_OFF, rnum);\r
+ outportb(BasePort + GA_DATA_OFF, value);\r
+}\r
+\r
+\r
+UBYTE CdRead(UWORD rnum)\r
+/*\r
+ This function is used to read the indirect addressed registers in the\r
+ AD-1848 or compatible CoDec. It will preserve the special function bits\r
+ in the upper-nibble of the indirect address register.\r
+\r
+ INPUTS:\r
+ rnum - the numner of the indirect register to be read\r
+\r
+ RETURNS:\r
+ the contents of the indirect register are returned\r
+\r
+*/\r
+{\r
+ outportb(WavePort + CD_ADDR_OFF,\r
+ (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);\r
+ return inportb(WavePort+CD_DATA_OFF);\r
+}\r
+\r
+\r
+\r
+void CdWrite(UWORD rnum,UBYTE value)\r
+/*\r
+ This function is used to write the indirect addressed registers in the\r
+ Ad-1848 or compatible CoDec. It will preserve the special function bits\r
+ in the upper-nibble of the indirect address register.\r
+\r
+ INPUTS:\r
+ rnum - the numner of the indirect register to be read\r
+ value - the byte value to be written to the indirect register\r
+\r
+ RETURNS:\r
+ Nothing\r
+*/\r
+{\r
+ outportb(WavePort + CD_ADDR_OFF,\r
+ (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);\r
+ outportb(WavePort + CD_DATA_OFF, value);\r
+}\r
+\r
+\r
+\r
+\r
+void SetDacVol(UBYTE lvol,UBYTE rvol)\r
+/*\r
+ This function sets the left and right DAC output level in the CoDec.\r
+\r
+ INPUTS:\r
+ lvol - left volume, 0-127\r
+ rvol - right volume, 0-127\r
+\r
+ RETURNS:\r
+ Nothing\r
+\r
+*/\r
+{\r
+ CdWrite(CD_DACL_REG, ~(lvol >> 1) & 0x3f);\r
+ CdWrite(CD_DACR_REG, ~(rvol >> 1) & 0x3f);\r
+}\r
+\r
+\r
+\r
+void SetCdRomVol(UBYTE lvol,UBYTE rvol)\r
+/*\r
+ This function sets the left and right CD-ROM output level in the CoDec.\r
+\r
+ INPUTS:\r
+ lvol - left volume, 0-127\r
+ rvol - right volume, 0-127\r
+\r
+ RETURNS:\r
+ Nothing\r
+*/\r
+{\r
+ CdWrite(CD_CDAUXL_REG, ~(lvol >> 2) & 0x1f);\r
+ CdWrite(CD_CDAUXR_REG, ~(rvol >> 2) & 0x1f);\r
+}\r
+\r
+\r
+void SetAdcVol(UBYTE lvol,UBYTE rvol)\r
+/*\r
+ This function sets the left and right ADC input level in the CoDec.\r
+\r
+ INPUTS:\r
+ lvol - left volume, 0-127\r
+ rvol - right volume, 0-127\r
+\r
+ RETURNS:\r
+ Nothing\r
+\r
+*/\r
+{\r
+ CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0xf0) | (lvol & 0x7f) >> 3);\r
+ CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0xf0) | (rvol & 0x7f) >> 3);\r
+}\r
+\r
+\r
+void StopCoDec(void)\r
+{\r
+ UWORD i;\r
+\r
+ CdWrite(CD_CONFIG_REG,CdRead(CD_CONFIG_REG)&0xfc);\r
+\r
+ /* Let the CoDec receive its last DACK(s). The DMAC must not be */\r
+ /* masked while the CoDec has DRQs pending. */\r
+/* for(i=0; i<256; ++i )\r
+ if(!(inportb(DmacRegP->status) & (0x10 << DmaChan))) break;\r
+*/\r
+}\r
+\r
+\r
+\r
+BOOL GetConfigEntry(char *entry, char *dest, FILE *fp)\r
+/*\r
+ This function parses a file (SNDSCAPE.INI) for a left-hand string and,\r
+ if found, writes its associated right-hand value to a destination buffer.\r
+ This function is case-insensitive.\r
+\r
+ INPUTS:\r
+ fp - a file pointer to the open SNDSCAPE.INI config file\r
+ dst - the destination buffer pointer\r
+ lhp - a pointer to the right-hand string\r
+\r
+ RETURNS:\r
+ 1 - if successful\r
+ 0 - if the right-hand string is not found or has no equate\r
+*/\r
+{\r
+ char static str[83];\r
+ char static tokstr[33];\r
+ char *p;\r
+\r
+ /* make a local copy of the entry, upper-case it */\r
+ strcpy(tokstr, entry);\r
+ strupr(tokstr);\r
+\r
+ /* rewind the file and try to find it ... */\r
+ rewind(fp);\r
+\r
+ for( ;; ) {\r
+ /* get the next string from the file */\r
+\r
+ fgets(str, 83, fp);\r
+ if(feof(fp)) return 0;\r
+\r
+ /* properly terminate the string */\r
+ for( p = str; *p != '\0'; ++p ) {\r
+ if( *p == ' ' || *p == '\t' || *p == 0x0a || *p == 0x0d ) {\r
+ *p = '\0';\r
+ break;\r
+ }\r
+ }\r
+\r
+ /* see if it's an 'equate' string; if so, zero the '=' */\r
+ if( !(p = strchr(str, '=')) ) continue;\r
+ *p = '\0';\r
+\r
+ /* upper-case the current string and test it */\r
+ strupr(str);\r
+ if( strcmp(str, tokstr) )\r
+ continue;\r
+\r
+ /* it's our string - copy the right-hand value to buffer */\r
+ for( p = str + strlen(str) + 1; (*dest++ = *p++) != '\0'; );\r
+ break;\r
+ }\r
+ return 1;\r
+}\r
+\r
+\r
+static BOOL SS_IsThere(void)\r
+{\r
+ static char str[78];\r
+ char *envptr;\r
+ FILE *fp;\r
+ UBYTE tmp;\r
+\r
+ if((envptr=getenv("SNDSCAPE"))==NULL) return 0;\r
+\r
+ strcpy(str, envptr);\r
+ if( str[strlen(str) - 1] == '\\' )\r
+ str[strlen(str) - 1] = '\0';\r
+\r
+ strcat(str, "\\SNDSCAPE.INI");\r
+\r
+ if(!(fp=fopen(str, "r"))) return 0;\r
+\r
+ /* read all of the necessary config info ... */\r
+ if(!GetConfigEntry("Product",str,fp)){\r
+ fclose(fp);\r
+ return 0;\r
+ }\r
+\r
+ /* if an old product name is read, set the IRQs accordingly */\r
+ strupr(str);\r
+ if(strstr(str,"SOUNDFX") || strstr(str,"MEDIA_FX"))\r
+ Irqs = RsIrqs;\r
+ else\r
+ Irqs = SsIrqs;\r
+\r
+ if(!GetConfigEntry("Port", str, fp)){\r
+ fclose(fp);\r
+ return 0;\r
+ }\r
+\r
+ BasePort=strtol(str,NULL,16);\r
+\r
+ if(!GetConfigEntry("WavePort",str,fp)){\r
+ fclose(fp);\r
+ return 0;\r
+ }\r
+\r
+ WavePort=strtol(str,NULL,16);\r
+\r
+ if(!GetConfigEntry("IRQ",str,fp)){\r
+ fclose(fp);\r
+ return 0;\r
+ }\r
+\r
+ MidiIrq=strtol(str,NULL,10);\r
+ if(MidiIrq==2) MidiIrq = 9;\r
+\r
+ if(!GetConfigEntry("SBIRQ",str,fp)){\r
+ fclose(fp);\r
+ return 0;\r
+ }\r
+\r
+ WaveIrq=strtol(str,NULL,10);\r
+ if(WaveIrq==2) WaveIrq=9;\r
+\r
+ if(!GetConfigEntry("DMA",str,fp)){\r
+ fclose(fp);\r
+ return 0;\r
+ }\r
+\r
+ DmaChan=strtol(str,NULL,10);\r
+\r
+ fclose(fp);\r
+\r
+ /* see if Soundscape is there by reading HW ... */\r
+ if((inportb(BasePort+GA_HOSTCTL_OFF)&0x78) != 0x00) return 0;\r
+ if((inportb(BasePort+GA_ADDR_OFF)&0xf0)==0xf0) return 0;\r
+\r
+ outportb(BasePort+GA_ADDR_OFF,0xf5);\r
+\r
+ tmp=inportb(BasePort+GA_ADDR_OFF);\r
+\r
+ if((tmp & 0xf0)==0xf0) return 0;\r
+ if((tmp & 0x0f)!=0x05) return 0;\r
+\r
+ /* formulate the chip ID */\r
+ if( (tmp & 0x80) != 0x00 )\r
+ IcType = MMIC;\r
+ else if((tmp & 0x70) != 0x00)\r
+ IcType = OPUS;\r
+ else\r
+ IcType = ODIE;\r
+\r
+ /* now do a quick check to make sure the CoDec is there too */\r
+ if((inportb(WavePort)&0x80)!=0x00) return 0;\r
+ return 1;\r
+}\r
+\r
+\r
+#ifdef NEVER\r
+\r
+static void interrupt newhandler(void)\r
+{\r
+ if(WaveIrq==7){\r
+ outportb(0x20,0x0b);\r
+ inportb(0x21);\r
+ if(!(inportb(0x20)&0x80)) return;\r
+ }\r
+ else if(WaveIrq==15){\r
+ outportb(0xa0,0x0b);\r
+ inportb(0xa1);\r
+ if(!(inportb(0xa0)&0x80)) return;\r
+ }\r
+\r
+ interruptcount++;\r
+\r
+ /* if the CoDec is interrupting clear the AD-1848 interrupt */\r
+ if(inportb(WavePort+CD_STATUS_OFF)&0x01)\r
+ outportb(WavePort+CD_STATUS_OFF,0x00);\r
+\r
+ MIrq_EOI(WaveIrq);\r
+}\r
+\r
+\r
+static PVI oldhandler;\r
+\r
+#endif\r
+\r
+\r
+static UWORD codecfreqs[14]={\r
+ 5512, 6615, 8000, 9600,11025,16000,18900,\r
+ 22050,27428,32000,33075,37800,44100,48000\r
+};\r
+\r
+\r
+static UWORD codecformats[14]={\r
+ 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, 0x05,\r
+ 0x07, 0x04, 0x06, 0x0d, 0x09, 0x0b, 0x0c\r
+};\r
+\r
+\r
+static UBYTE codecformat;\r
+\r
+\r
+static BOOL SS_Init(void)\r
+{\r
+ int t;\r
+\r
+ if(!SS_IsThere()){\r
+ myerr="No such hardware detected, check your 'SNDSCAPE' env. variable";\r
+ return 0;\r
+ }\r
+\r
+ printf("Ensoniq Soundscape at port 0x%x, irq %d, dma %d\n",WavePort,WaveIrq,DmaChan);\r
+\r
+ /* find closest codec frequency */\r
+\r
+ for(t=0;t<14;t++){\r
+ if(t==13 || md_mixfreq<=codecfreqs[t]){\r
+ md_mixfreq=codecfreqs[t];\r
+ break;\r
+ }\r
+ }\r
+\r
+ codecformat=codecformats[t];\r
+ if(md_mode & DMODE_STEREO) codecformat|=0x10;\r
+ if(md_mode & DMODE_16BITS) codecformat|=0x40;\r
+\r
+ if(!VC_Init()) return 0;\r
+\r
+ SS_DMAMEM=MDma_AllocMem(md_dmabufsize);\r
+\r
+ if(SS_DMAMEM==NULL){\r
+ VC_Exit();\r
+ myerr="Couldn't allocate page-contiguous dma-buffer";\r
+ return 0;\r
+ }\r
+\r
+ SS_DMABUF=(char *)MDma_GetPtr(SS_DMAMEM);\r
+\r
+ /* In case the CoDec is running, stop it */\r
+ StopCoDec();\r
+\r
+ /* Clear possible CoDec and SoundBlaster emulation interrupts */\r
+ outportb(WavePort+CD_STATUS_OFF,0x00);\r
+ inportb(0x22e);\r
+\r
+ /* If necessary, save some regs, do some resource re-routing */\r
+ if( IcType != MMIC) {\r
+\r
+ /* derive the MIDI and Wave IRQ indices (0-3) for reg writes */\r
+ for( Mindx = 0; Mindx < 4; ++Mindx )\r
+ if( MidiIrq == *(Irqs + Mindx) )\r
+ break;\r
+ for( Windx = 0; Windx < 4; ++Windx )\r
+ if( WaveIrq == *(Irqs + Windx) )\r
+ break;\r
+\r
+ /* setup the CoDec DMA polarity */\r
+ GaWrite(GA_DMACFG_REG, 0x50);\r
+\r
+ /* give the CoDec control of the DMA and Wave IRQ resources */\r
+ CdCfgSav = GaRead(GA_CDCFG_REG);\r
+ GaWrite(GA_CDCFG_REG, 0x89 | (DmaChan << 4) | (Windx << 1));\r
+\r
+ /* pull the Sound Blaster emulation off of those resources */\r
+ DmaCfgSav = GaRead(GA_DMAB_REG);\r
+ GaWrite(GA_DMAB_REG, 0x20);\r
+ IntCfgSav = GaRead(GA_INTCFG_REG);\r
+ GaWrite(GA_INTCFG_REG, 0xf0 | (Mindx << 2) | Mindx);\r
+ }\r
+\r
+ /* Save all volumes that we might use, init some levels */\r
+ DacSavL = CdRead(CD_DACL_REG);\r
+ DacSavR = CdRead(CD_DACR_REG);\r
+ CdxSavL = CdRead(CD_CDAUXL_REG);\r
+ CdxSavR = CdRead(CD_CDAUXL_REG);\r
+ AdcSavL = CdRead(CD_ADCL_REG);\r
+ AdcSavR = CdRead(CD_ADCR_REG);\r
+\r
+ SetDacVol(127, 127);\r
+ SetAdcVol(96, 96);\r
+\r
+ /* Select the mic/line input to the record mux; */\r
+ /* if not ODIE, set the mic gain bit too */\r
+ CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0x3f) |\r
+ (IcType == ODIE ? 0x80 : 0xa0));\r
+ CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0x3f) |\r
+ (IcType == ODIE ? 0x80 : 0xa0));\r
+\r
+ /* Put the CoDec into mode change state */\r
+ outportb(WavePort + CD_ADDR_OFF, 0x40);\r
+\r
+ /* Setup CoDec mode - single DMA chan, AutoCal on */\r
+ CdWrite(CD_CONFIG_REG, 0x0c);\r
+\r
+#ifdef NEVER\r
+ /* enable the CoDec interrupt pin */\r
+ CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02);\r
+ oldhandler=MIrq_SetHandler(WaveIrq,newhandler);\r
+ MIrq_OnOff(WaveIrq,1);\r
+#else\r
+ /* disable the interrupt for mikmod */\r
+ CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);\r
+#endif\r
+ return 1;\r
+}\r
+\r
+\r
+\r
+static void SS_Exit(void)\r
+{\r
+ /* in case the CoDec is running, stop it */\r
+ StopCoDec();\r
+\r
+ /* mask the PC DMA Controller */\r
+/* outportb(DmacRegP->mask, 0x04 | DmaChan); */\r
+\r
+#ifdef NEVER\r
+ /* disable the CoDec interrupt pin */\r
+ CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);\r
+ MIrq_OnOff(WaveIrq,0);\r
+ MIrq_SetHandler(WaveIrq,oldhandler);\r
+#endif\r
+\r
+ /* restore all volumes ... */\r
+ CdWrite(CD_DACL_REG, DacSavL);\r
+ CdWrite(CD_DACR_REG, DacSavR);\r
+ CdWrite(CD_CDAUXL_REG, CdxSavL);\r
+ CdWrite(CD_CDAUXL_REG, CdxSavR);\r
+ CdWrite(CD_ADCL_REG, AdcSavL);\r
+ CdWrite(CD_ADCR_REG, AdcSavR);\r
+\r
+ /* if necessary, restore gate array resource registers */\r
+ if(IcType!=MMIC){\r
+ GaWrite(GA_INTCFG_REG, IntCfgSav);\r
+ GaWrite(GA_DMAB_REG, DmaCfgSav);\r
+ GaWrite(GA_CDCFG_REG, CdCfgSav);\r
+ }\r
+\r
+ MDma_FreeMem(SS_DMAMEM);\r
+ VC_Exit();\r
+}\r
+\r
+\r
+static UWORD last=0;\r
+static UWORD curr=0;\r
+\r
+\r
+static void SS_Update(void)\r
+{\r
+ UWORD todo,index;\r
+\r
+ curr=(md_dmabufsize-MDma_Todo(DmaChan))&0xfffc;\r
+\r
+ if(curr>=md_dmabufsize) return;\r
+ if(curr==last) return;\r
+\r
+ if(curr>last){\r
+ todo=curr-last; index=last;\r
+ last+=VC_WriteBytes(&SS_DMABUF[index],todo);\r
+ MDma_Commit(SS_DMAMEM,index,todo);\r
+ if(last>=md_dmabufsize) last=0;\r
+ }\r
+ else{\r
+ todo=md_dmabufsize-last;\r
+ VC_WriteBytes(&SS_DMABUF[last],todo);\r
+ MDma_Commit(SS_DMAMEM,last,todo);\r
+ last=VC_WriteBytes(SS_DMABUF,curr);\r
+ MDma_Commit(SS_DMAMEM,0,curr);\r
+ }\r
+}\r
+\r
+\r
+static void SS_PlayStart(void)\r
+{\r
+ int direction=0;\r
+ long i;\r
+ UWORD tmp;\r
+\r
+ VC_PlayStart();\r
+\r
+ /* make sure the the CoDec is in mode change state */\r
+ outportb(WavePort + CD_ADDR_OFF, 0x40);\r
+\r
+ /* and write the format register */\r
+ CdWrite(CD_FORMAT_REG, codecformat);\r
+\r
+ /* if not using ODIE and recording, setup extended format register */\r
+ if( IcType != ODIE && direction )\r
+ CdWrite(CD_XFORMAT_REG, codecformat & 0x70);\r
+\r
+ /* delay for internal re-synch */\r
+ for( i = 0; i < 200000UL; ++i )\r
+ inportb(BasePort + GA_ADDR_OFF);\r
+\r
+ /* clear the dma buffer */\r
+\r
+ VC_SilenceBytes(SS_DMABUF,md_dmabufsize);\r
+ MDma_Commit(SS_DMAMEM,0,md_dmabufsize);\r
+\r
+ /* Write the CoDec interrupt count - sample frames per half-buffer. */\r
+ /* If not using ODIE and recording, use extended count regs */\r
+ tmp = md_dmabufsize;\r
+ if(md_mode&DMODE_STEREO) tmp>>=1;\r
+ if(md_mode&DMODE_16BITS) tmp>>=1;\r
+ tmp--;\r
+\r
+ if( IcType != ODIE && direction ) {\r
+ CdWrite(CD_XLCOUNT_REG, tmp);\r
+ CdWrite(CD_XUCOUNT_REG, tmp >> 8);\r
+ }\r
+ else {\r
+ CdWrite(CD_LCOUNT_REG, tmp);\r
+ CdWrite(CD_UCOUNT_REG, tmp >> 8);\r
+ }\r
+\r
+ if(!MDma_Start(DmaChan,SS_DMAMEM,md_dmabufsize,INDEF_WRITE)){\r
+ return;\r
+ }\r
+\r
+ /* disable mode change state and start the CoDec */\r
+ outportb(WavePort + CD_ADDR_OFF, 0x00);\r
+ CdWrite(CD_CONFIG_REG, direction ? 0x02 : 0x01);\r
+}\r
+\r
+\r
+static void SS_PlayStop(void)\r
+{\r
+ StopCoDec();\r
+ VC_PlayStop();\r
+}\r
+\r
+\r
+DRIVER drv_ss={\r
+ NULL,\r
+ "Ensoniq Soundscape",\r
+ "MikMod Ensoniq Soundscape Driver v0.0 - Thanks to CyberCerus",\r
+ SS_IsThere,\r
+ VC_SampleLoad,\r
+ VC_SampleUnload,\r
+ SS_Init,\r
+ SS_Exit,\r
+ SS_PlayStart,\r
+ SS_PlayStop,\r
+ SS_Update,\r
+ VC_VoiceSetVolume,\r
+ VC_VoiceSetFrequency,\r
+ VC_VoiceSetPanning,\r
+ VC_VoicePlay\r
+};\r