added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / drv_ss.c
diff --git a/libs/oldmik/src/drv_ss.c b/libs/oldmik/src/drv_ss.c
new file mode 100644 (file)
index 0000000..4d0ff26
--- /dev/null
@@ -0,0 +1,714 @@
+/*\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