added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / drv_ss.c
1 /*\r
2 \r
3 Name:\r
4 DRV_SS.C\r
5 \r
6 Description:\r
7 Mikmod driver for output on Ensoniq Soundscape / Soundscape ELITE\r
8 \r
9 Portability:\r
10 \r
11 MSDOS:  BC(y)   Watcom(y)       DJGPP(y)\r
12 Win95:  n\r
13 Os2:    n\r
14 Linux:  n\r
15 \r
16 (y) - yes\r
17 (n) - no (not possible or not useful)\r
18 (?) - may be possible, but not tested\r
19 \r
20 */\r
21 #include <stdio.h>\r
22 #include <stdlib.h>\r
23 #include <dos.h>\r
24 #include <malloc.h>\r
25 #include <conio.h>\r
26 #include <string.h>\r
27 \r
28 #include "mikmod.h"\r
29 #include "mdma.h"\r
30 #include "mirq.h"\r
31 \r
32 /***************************************************************************\r
33 >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SS stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
34 ***************************************************************************/\r
35 \r
36 /* Ensoniq gate-array chip defines ... */\r
37 \r
38 #define ODIE            0       /* ODIE gate array */\r
39 #define OPUS            1       /* OPUS gate array */\r
40 #define MMIC            2       /* MiMIC gate array */\r
41 \r
42 /* relevant direct register defines - offsets from base address */\r
43 #define GA_HOSTCTL_OFF  2       /* host port ctrl/stat reg */\r
44 #define GA_ADDR_OFF     4       /* indirect address reg */\r
45 #define GA_DATA_OFF     5       /* indirect data reg */\r
46 #define GA_CODEC_OFF    8       /* for some boards CoDec is fixed from base */\r
47 \r
48 /* relevant indirect register defines */\r
49 #define GA_DMAB_REG     3       /* DMA chan B assign reg */\r
50 #define GA_INTCFG_REG   4       /* interrupt configuration reg */\r
51 #define GA_DMACFG_REG   5       /* DMA configuration reg */\r
52 #define GA_CDCFG_REG    6       /* CD-ROM/CoDec config reg */\r
53 #define GA_HMCTL_REG    9       /* host master control reg */\r
54 \r
55 \r
56 /* AD-1848 or compatible CoDec defines ... */\r
57 /* relevant direct register defines - offsets from base */\r
58 #define CD_ADDR_OFF     0       /* indirect address reg */\r
59 #define CD_DATA_OFF     1       /* indirect data reg */\r
60 #define CD_STATUS_OFF   2       /* status register */\r
61 \r
62 /* relevant indirect register defines */\r
63 #define CD_ADCL_REG             0       /* left DAC input control reg */\r
64 #define CD_ADCR_REG             1       /* right DAC input control reg */\r
65 #define CD_CDAUXL_REG   2       /* left DAC output control reg */\r
66 #define CD_CDAUXR_REG   3       /* right DAC output control reg */\r
67 #define CD_DACL_REG     6       /* left DAC output control reg */\r
68 #define CD_DACR_REG     7       /* right DAC output control reg */\r
69 #define CD_FORMAT_REG   8       /* clock and data format reg */\r
70 #define CD_CONFIG_REG   9       /* interface config register */\r
71 #define CD_PINCTL_REG   10      /* external pin control reg */\r
72 #define CD_UCOUNT_REG   14      /* upper count reg */\r
73 #define CD_LCOUNT_REG   15      /* lower count reg */\r
74 #define CD_XFORMAT_REG  28      /* extended format reg - 1845 record */\r
75 #define CD_XUCOUNT_REG  30      /* extended upper count reg - 1845 record */\r
76 #define CD_XLCOUNT_REG  31      /* extended lower count reg - 1845 record */\r
77 \r
78 #define CD_MODE_CHANGE  0x40    /* mode change mask for addr reg */\r
79 \r
80 /****************************************************************************\r
81 hardware config info ...\r
82 ****************************************************************************/\r
83 \r
84 static UWORD    BasePort;       /* Gate Array/MPU-401 base port */\r
85 static UWORD    MidiIrq;        /* the MPU-401 IRQ */\r
86 static UWORD    WavePort;       /* the AD-1848 base port */\r
87 static UWORD    WaveIrq;        /* the PCM IRQ */\r
88 static UWORD    DmaChan;        /* the PCM DMA channel */\r
89 \r
90 /****************************************************************************\r
91 all kinds of stuff ...\r
92 ****************************************************************************/\r
93 \r
94 static UWORD Windx;             /* Wave IRQ index - for reg writes */\r
95 static UWORD Mindx;             /* MIDI IRQ index - for reg writes */\r
96 \r
97 static UBYTE IcType;            /* the Ensoniq chip type */\r
98 static UBYTE CdCfgSav;          /* gate array register save area */\r
99 static UBYTE DmaCfgSav;         /* gate array register save area */\r
100 static UBYTE IntCfgSav;         /* gate array register save area */\r
101 \r
102 static UWORD const SsIrqs[4] = { 9, 5, 7, 10 };  /* Soundscape IRQs */\r
103 static UWORD const RsIrqs[4] = { 9, 7, 5, 15 };  /* an older IRQ set */\r
104 static UWORD const *Irqs;           /* pointer to one of the IRQ sets */\r
105 \r
106 static UBYTE DacSavL;        /* DAC left volume save */\r
107 static UBYTE DacSavR;        /* DAC right volume save */\r
108 static UBYTE CdxSavL;        /* CD/Aux left volume save */\r
109 static UBYTE CdxSavR;        /* CD/Aux right volume save */\r
110 static UBYTE AdcSavL;        /* ADC left volume save */\r
111 static UBYTE AdcSavR;        /* ADC right volume save */\r
112 \r
113 static DMAMEM *SS_DMAMEM;\r
114 static char   *SS_DMABUF;\r
115 \r
116 \r
117 /***************************************************************************\r
118 >>>>>>>>>>>>>>>>>>>>>>>>> The actual SS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
119 ***************************************************************************/\r
120 \r
121 \r
122 \r
123 UBYTE GaRead(UWORD rnum)\r
124 /*\r
125   This function is used to read the indirect addressed registers in the\r
126   Ensoniq Soundscape gate array.\r
127 \r
128   INPUTS:\r
129         rnum  - the numner of the indirect register to be read\r
130 \r
131   RETURNS:\r
132         the contents of the indirect register are returned\r
133 */\r
134 {\r
135         outportb(BasePort + GA_ADDR_OFF, rnum);\r
136         return inportb(BasePort + GA_DATA_OFF);\r
137 }\r
138 \r
139 \r
140 \r
141 void GaWrite(UWORD rnum,UBYTE value)\r
142 /*\r
143   This function is used to write the indirect addressed registers in the\r
144   Ensoniq Soundscape gate array.\r
145 \r
146   INPUTS:\r
147         rnum   - the numner of the indirect register to be read\r
148         value  - the byte value to be written to the indirect register\r
149 \r
150   RETURNS:\r
151         Nothing\r
152 */\r
153 {\r
154         outportb(BasePort + GA_ADDR_OFF, rnum);\r
155         outportb(BasePort + GA_DATA_OFF, value);\r
156 }\r
157 \r
158 \r
159 UBYTE CdRead(UWORD rnum)\r
160 /*\r
161   This function is used to read the indirect addressed registers in the\r
162   AD-1848 or compatible CoDec. It will preserve the special function bits\r
163   in the upper-nibble of the indirect address register.\r
164 \r
165   INPUTS:\r
166         rnum  - the numner of the indirect register to be read\r
167 \r
168   RETURNS:\r
169         the contents of the indirect register are returned\r
170 \r
171 */\r
172 {\r
173         outportb(WavePort + CD_ADDR_OFF,\r
174                 (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);\r
175         return inportb(WavePort+CD_DATA_OFF);\r
176 }\r
177 \r
178 \r
179 \r
180 void CdWrite(UWORD rnum,UBYTE value)\r
181 /*\r
182   This function is used to write the indirect addressed registers in the\r
183   Ad-1848 or compatible CoDec. It will preserve the special function bits\r
184   in the upper-nibble of the indirect address register.\r
185 \r
186   INPUTS:\r
187         rnum   - the numner of the indirect register to be read\r
188         value  - the byte value to be written to the indirect register\r
189 \r
190   RETURNS:\r
191         Nothing\r
192 */\r
193 {\r
194         outportb(WavePort + CD_ADDR_OFF,\r
195                 (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);\r
196         outportb(WavePort + CD_DATA_OFF, value);\r
197 }\r
198 \r
199 \r
200 \r
201 \r
202 void SetDacVol(UBYTE lvol,UBYTE rvol)\r
203 /*\r
204   This function sets the left and right DAC output level in the CoDec.\r
205 \r
206   INPUTS:\r
207         lvol  - left volume, 0-127\r
208         rvol  - right volume, 0-127\r
209 \r
210   RETURNS:\r
211         Nothing\r
212 \r
213 */\r
214 {\r
215         CdWrite(CD_DACL_REG, ~(lvol >> 1) & 0x3f);\r
216         CdWrite(CD_DACR_REG, ~(rvol >> 1) & 0x3f);\r
217 }\r
218 \r
219 \r
220 \r
221 void SetCdRomVol(UBYTE lvol,UBYTE rvol)\r
222 /*\r
223   This function sets the left and right CD-ROM output level in the CoDec.\r
224 \r
225   INPUTS:\r
226         lvol  - left volume, 0-127\r
227         rvol  - right volume, 0-127\r
228 \r
229   RETURNS:\r
230         Nothing\r
231 */\r
232 {\r
233         CdWrite(CD_CDAUXL_REG, ~(lvol >> 2) & 0x1f);\r
234         CdWrite(CD_CDAUXR_REG, ~(rvol >> 2) & 0x1f);\r
235 }\r
236 \r
237 \r
238 void SetAdcVol(UBYTE lvol,UBYTE rvol)\r
239 /*\r
240   This function sets the left and right ADC input level in the CoDec.\r
241 \r
242   INPUTS:\r
243         lvol  - left volume, 0-127\r
244         rvol  - right volume, 0-127\r
245 \r
246   RETURNS:\r
247         Nothing\r
248 \r
249 */\r
250 {\r
251         CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0xf0) | (lvol & 0x7f) >> 3);\r
252         CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0xf0) | (rvol & 0x7f) >> 3);\r
253 }\r
254 \r
255 \r
256 void StopCoDec(void)\r
257 {\r
258         UWORD i;\r
259 \r
260         CdWrite(CD_CONFIG_REG,CdRead(CD_CONFIG_REG)&0xfc);\r
261 \r
262         /* Let the CoDec receive its last DACK(s). The DMAC must not be */\r
263         /*  masked while the CoDec has DRQs pending. */\r
264 /*      for(i=0; i<256; ++i )\r
265                 if(!(inportb(DmacRegP->status) & (0x10 << DmaChan))) break;\r
266 */\r
267 }\r
268 \r
269 \r
270 \r
271 BOOL GetConfigEntry(char *entry, char *dest, FILE *fp)\r
272 /*\r
273   This function parses a file (SNDSCAPE.INI) for a left-hand string and,\r
274   if found, writes its associated right-hand value to a destination buffer.\r
275   This function is case-insensitive.\r
276 \r
277   INPUTS:\r
278         fp  - a file pointer to the open SNDSCAPE.INI config file\r
279         dst - the destination buffer pointer\r
280         lhp - a pointer to the right-hand string\r
281 \r
282   RETURNS:\r
283         1   - if successful\r
284         0   - if the right-hand string is not found or has no equate\r
285 */\r
286 {\r
287         char static str[83];\r
288         char static tokstr[33];\r
289         char *p;\r
290 \r
291         /* make a local copy of the entry, upper-case it */\r
292         strcpy(tokstr, entry);\r
293         strupr(tokstr);\r
294 \r
295         /* rewind the file and try to find it ... */\r
296         rewind(fp);\r
297 \r
298         for( ;; ) {\r
299                 /* get the next string from the file */\r
300 \r
301                 fgets(str, 83, fp);\r
302                 if(feof(fp)) return 0;\r
303 \r
304                 /* properly terminate the string */\r
305                 for( p = str; *p != '\0'; ++p ) {\r
306                         if( *p == ' ' || *p == '\t' || *p == 0x0a || *p == 0x0d ) {\r
307                                 *p = '\0';\r
308                                 break;\r
309                         }\r
310                 }\r
311 \r
312                 /* see if it's an 'equate' string; if so, zero the '=' */\r
313                 if( !(p = strchr(str, '=')) ) continue;\r
314                 *p = '\0';\r
315 \r
316                 /* upper-case the current string and test it */\r
317                 strupr(str);\r
318                 if( strcmp(str, tokstr) )\r
319                         continue;\r
320 \r
321                 /* it's our string - copy the right-hand value to buffer */\r
322                 for( p = str + strlen(str) + 1; (*dest++ = *p++) != '\0'; );\r
323                 break;\r
324         }\r
325         return 1;\r
326 }\r
327 \r
328 \r
329 static BOOL SS_IsThere(void)\r
330 {\r
331         static char str[78];\r
332         char *envptr;\r
333         FILE *fp;\r
334         UBYTE tmp;\r
335 \r
336         if((envptr=getenv("SNDSCAPE"))==NULL) return 0;\r
337 \r
338         strcpy(str, envptr);\r
339         if( str[strlen(str) - 1] == '\\' )\r
340                 str[strlen(str) - 1] = '\0';\r
341 \r
342         strcat(str, "\\SNDSCAPE.INI");\r
343 \r
344         if(!(fp=fopen(str, "r"))) return 0;\r
345 \r
346         /* read all of the necessary config info ... */\r
347         if(!GetConfigEntry("Product",str,fp)){\r
348                 fclose(fp);\r
349                 return 0;\r
350         }\r
351 \r
352         /* if an old product name is read, set the IRQs accordingly */\r
353         strupr(str);\r
354         if(strstr(str,"SOUNDFX") || strstr(str,"MEDIA_FX"))\r
355                 Irqs = RsIrqs;\r
356         else\r
357                 Irqs = SsIrqs;\r
358 \r
359         if(!GetConfigEntry("Port", str, fp)){\r
360                 fclose(fp);\r
361                 return 0;\r
362         }\r
363 \r
364         BasePort=strtol(str,NULL,16);\r
365 \r
366         if(!GetConfigEntry("WavePort",str,fp)){\r
367                 fclose(fp);\r
368                 return 0;\r
369         }\r
370 \r
371         WavePort=strtol(str,NULL,16);\r
372 \r
373         if(!GetConfigEntry("IRQ",str,fp)){\r
374                 fclose(fp);\r
375                 return 0;\r
376         }\r
377 \r
378         MidiIrq=strtol(str,NULL,10);\r
379         if(MidiIrq==2) MidiIrq = 9;\r
380 \r
381         if(!GetConfigEntry("SBIRQ",str,fp)){\r
382                 fclose(fp);\r
383                 return 0;\r
384         }\r
385 \r
386         WaveIrq=strtol(str,NULL,10);\r
387         if(WaveIrq==2) WaveIrq=9;\r
388 \r
389         if(!GetConfigEntry("DMA",str,fp)){\r
390                 fclose(fp);\r
391                 return 0;\r
392         }\r
393 \r
394         DmaChan=strtol(str,NULL,10);\r
395 \r
396         fclose(fp);\r
397 \r
398         /* see if Soundscape is there by reading HW ... */\r
399         if((inportb(BasePort+GA_HOSTCTL_OFF)&0x78) != 0x00)     return 0;\r
400         if((inportb(BasePort+GA_ADDR_OFF)&0xf0)==0xf0) return 0;\r
401 \r
402         outportb(BasePort+GA_ADDR_OFF,0xf5);\r
403 \r
404         tmp=inportb(BasePort+GA_ADDR_OFF);\r
405 \r
406         if((tmp & 0xf0)==0xf0) return 0;\r
407         if((tmp & 0x0f)!=0x05) return 0;\r
408 \r
409         /* formulate the chip ID */\r
410         if( (tmp & 0x80) != 0x00 )\r
411                 IcType = MMIC;\r
412         else if((tmp & 0x70) != 0x00)\r
413                 IcType = OPUS;\r
414         else\r
415                 IcType = ODIE;\r
416 \r
417         /* now do a quick check to make sure the CoDec is there too */\r
418         if((inportb(WavePort)&0x80)!=0x00) return 0;\r
419         return 1;\r
420 }\r
421 \r
422 \r
423 #ifdef NEVER\r
424 \r
425 static void interrupt newhandler(void)\r
426 {\r
427         if(WaveIrq==7){\r
428                 outportb(0x20,0x0b);\r
429                 inportb(0x21);\r
430                 if(!(inportb(0x20)&0x80)) return;\r
431         }\r
432         else if(WaveIrq==15){\r
433                 outportb(0xa0,0x0b);\r
434                 inportb(0xa1);\r
435                 if(!(inportb(0xa0)&0x80)) return;\r
436         }\r
437 \r
438         interruptcount++;\r
439 \r
440         /* if the CoDec is interrupting clear the AD-1848 interrupt */\r
441         if(inportb(WavePort+CD_STATUS_OFF)&0x01)\r
442                 outportb(WavePort+CD_STATUS_OFF,0x00);\r
443 \r
444         MIrq_EOI(WaveIrq);\r
445 }\r
446 \r
447 \r
448 static PVI oldhandler;\r
449 \r
450 #endif\r
451 \r
452 \r
453 static UWORD codecfreqs[14]={\r
454         5512, 6615, 8000, 9600,11025,16000,18900,\r
455         22050,27428,32000,33075,37800,44100,48000\r
456 };\r
457 \r
458 \r
459 static UWORD codecformats[14]={\r
460         0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, 0x05,\r
461         0x07, 0x04,     0x06, 0x0d,     0x09, 0x0b,     0x0c\r
462 };\r
463 \r
464 \r
465 static UBYTE codecformat;\r
466 \r
467 \r
468 static BOOL SS_Init(void)\r
469 {\r
470         int t;\r
471 \r
472         if(!SS_IsThere()){\r
473                 myerr="No such hardware detected, check your 'SNDSCAPE' env. variable";\r
474                 return 0;\r
475         }\r
476 \r
477         printf("Ensoniq Soundscape at port 0x%x, irq %d, dma %d\n",WavePort,WaveIrq,DmaChan);\r
478 \r
479         /* find closest codec frequency */\r
480 \r
481         for(t=0;t<14;t++){\r
482                 if(t==13 || md_mixfreq<=codecfreqs[t]){\r
483                         md_mixfreq=codecfreqs[t];\r
484                         break;\r
485                 }\r
486         }\r
487 \r
488         codecformat=codecformats[t];\r
489         if(md_mode & DMODE_STEREO) codecformat|=0x10;\r
490         if(md_mode & DMODE_16BITS) codecformat|=0x40;\r
491 \r
492         if(!VC_Init()) return 0;\r
493 \r
494         SS_DMAMEM=MDma_AllocMem(md_dmabufsize);\r
495 \r
496         if(SS_DMAMEM==NULL){\r
497                 VC_Exit();\r
498                 myerr="Couldn't allocate page-contiguous dma-buffer";\r
499                 return 0;\r
500         }\r
501 \r
502         SS_DMABUF=(char *)MDma_GetPtr(SS_DMAMEM);\r
503 \r
504         /* In case the CoDec is running, stop it */\r
505         StopCoDec();\r
506 \r
507         /* Clear possible CoDec and SoundBlaster emulation interrupts */\r
508         outportb(WavePort+CD_STATUS_OFF,0x00);\r
509         inportb(0x22e);\r
510 \r
511         /* If necessary, save some regs, do some resource re-routing */\r
512         if( IcType != MMIC) {\r
513 \r
514                 /* derive the MIDI and Wave IRQ indices (0-3) for reg writes */\r
515                 for( Mindx = 0; Mindx < 4; ++Mindx )\r
516                         if( MidiIrq == *(Irqs + Mindx) )\r
517                                 break;\r
518                 for( Windx = 0; Windx < 4; ++Windx )\r
519                         if( WaveIrq == *(Irqs + Windx) )\r
520                                 break;\r
521 \r
522                 /* setup the CoDec DMA polarity */\r
523                 GaWrite(GA_DMACFG_REG, 0x50);\r
524 \r
525                 /* give the CoDec control of the DMA and Wave IRQ resources */\r
526                         CdCfgSav = GaRead(GA_CDCFG_REG);\r
527                 GaWrite(GA_CDCFG_REG, 0x89 | (DmaChan << 4) | (Windx << 1));\r
528 \r
529                 /* pull the Sound Blaster emulation off of those resources */\r
530                 DmaCfgSav = GaRead(GA_DMAB_REG);\r
531                 GaWrite(GA_DMAB_REG, 0x20);\r
532                 IntCfgSav = GaRead(GA_INTCFG_REG);\r
533                 GaWrite(GA_INTCFG_REG, 0xf0 | (Mindx << 2) | Mindx);\r
534         }\r
535 \r
536         /* Save all volumes that we might use, init some levels */\r
537         DacSavL = CdRead(CD_DACL_REG);\r
538         DacSavR = CdRead(CD_DACR_REG);\r
539         CdxSavL = CdRead(CD_CDAUXL_REG);\r
540         CdxSavR = CdRead(CD_CDAUXL_REG);\r
541         AdcSavL = CdRead(CD_ADCL_REG);\r
542         AdcSavR = CdRead(CD_ADCR_REG);\r
543 \r
544         SetDacVol(127, 127);\r
545         SetAdcVol(96, 96);\r
546 \r
547         /* Select the mic/line input to the record mux; */\r
548         /* if not ODIE, set the mic gain bit too */\r
549         CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0x3f) |\r
550                 (IcType == ODIE ? 0x80 : 0xa0));\r
551         CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0x3f) |\r
552                 (IcType == ODIE ? 0x80 : 0xa0));\r
553 \r
554         /* Put the CoDec into mode change state */\r
555         outportb(WavePort + CD_ADDR_OFF, 0x40);\r
556 \r
557         /* Setup CoDec mode - single DMA chan, AutoCal on */\r
558         CdWrite(CD_CONFIG_REG, 0x0c);\r
559 \r
560 #ifdef NEVER\r
561         /* enable the CoDec interrupt pin */\r
562         CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02);\r
563         oldhandler=MIrq_SetHandler(WaveIrq,newhandler);\r
564         MIrq_OnOff(WaveIrq,1);\r
565 #else\r
566         /* disable the interrupt for mikmod */\r
567         CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);\r
568 #endif\r
569         return 1;\r
570 }\r
571 \r
572 \r
573 \r
574 static void SS_Exit(void)\r
575 {\r
576         /* in case the CoDec is running, stop it */\r
577         StopCoDec();\r
578 \r
579         /* mask the PC DMA Controller */\r
580 /*      outportb(DmacRegP->mask, 0x04 | DmaChan); */\r
581 \r
582 #ifdef NEVER\r
583         /* disable the CoDec interrupt pin */\r
584         CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);\r
585         MIrq_OnOff(WaveIrq,0);\r
586         MIrq_SetHandler(WaveIrq,oldhandler);\r
587 #endif\r
588 \r
589         /* restore all volumes ... */\r
590         CdWrite(CD_DACL_REG, DacSavL);\r
591         CdWrite(CD_DACR_REG, DacSavR);\r
592         CdWrite(CD_CDAUXL_REG, CdxSavL);\r
593         CdWrite(CD_CDAUXL_REG, CdxSavR);\r
594         CdWrite(CD_ADCL_REG, AdcSavL);\r
595         CdWrite(CD_ADCR_REG, AdcSavR);\r
596 \r
597         /* if necessary, restore gate array resource registers */\r
598         if(IcType!=MMIC){\r
599                 GaWrite(GA_INTCFG_REG, IntCfgSav);\r
600                 GaWrite(GA_DMAB_REG, DmaCfgSav);\r
601                 GaWrite(GA_CDCFG_REG, CdCfgSav);\r
602         }\r
603 \r
604         MDma_FreeMem(SS_DMAMEM);\r
605         VC_Exit();\r
606 }\r
607 \r
608 \r
609 static UWORD last=0;\r
610 static UWORD curr=0;\r
611 \r
612 \r
613 static void SS_Update(void)\r
614 {\r
615         UWORD todo,index;\r
616 \r
617         curr=(md_dmabufsize-MDma_Todo(DmaChan))&0xfffc;\r
618 \r
619         if(curr>=md_dmabufsize) return;\r
620         if(curr==last) return;\r
621 \r
622         if(curr>last){\r
623                 todo=curr-last; index=last;\r
624                 last+=VC_WriteBytes(&SS_DMABUF[index],todo);\r
625                 MDma_Commit(SS_DMAMEM,index,todo);\r
626                 if(last>=md_dmabufsize) last=0;\r
627         }\r
628         else{\r
629                 todo=md_dmabufsize-last;\r
630                 VC_WriteBytes(&SS_DMABUF[last],todo);\r
631                 MDma_Commit(SS_DMAMEM,last,todo);\r
632                 last=VC_WriteBytes(SS_DMABUF,curr);\r
633                 MDma_Commit(SS_DMAMEM,0,curr);\r
634         }\r
635 }\r
636 \r
637 \r
638 static void SS_PlayStart(void)\r
639 {\r
640         int direction=0;\r
641         long i;\r
642         UWORD tmp;\r
643 \r
644         VC_PlayStart();\r
645 \r
646         /* make sure the the CoDec is in mode change state */\r
647         outportb(WavePort + CD_ADDR_OFF, 0x40);\r
648 \r
649         /* and write the format register */\r
650         CdWrite(CD_FORMAT_REG, codecformat);\r
651 \r
652         /* if not using ODIE and recording, setup extended format register */\r
653         if( IcType != ODIE && direction )\r
654                 CdWrite(CD_XFORMAT_REG, codecformat & 0x70);\r
655 \r
656         /* delay for internal re-synch */\r
657         for( i = 0; i < 200000UL; ++i )\r
658                 inportb(BasePort + GA_ADDR_OFF);\r
659 \r
660         /* clear the dma buffer */\r
661 \r
662         VC_SilenceBytes(SS_DMABUF,md_dmabufsize);\r
663         MDma_Commit(SS_DMAMEM,0,md_dmabufsize);\r
664 \r
665         /* Write the CoDec interrupt count - sample frames per half-buffer. */\r
666         /* If not using ODIE and recording, use extended count regs */\r
667         tmp = md_dmabufsize;\r
668         if(md_mode&DMODE_STEREO) tmp>>=1;\r
669         if(md_mode&DMODE_16BITS) tmp>>=1;\r
670         tmp--;\r
671 \r
672         if( IcType != ODIE && direction ) {\r
673                 CdWrite(CD_XLCOUNT_REG, tmp);\r
674                 CdWrite(CD_XUCOUNT_REG, tmp >> 8);\r
675         }\r
676         else {\r
677                 CdWrite(CD_LCOUNT_REG, tmp);\r
678                 CdWrite(CD_UCOUNT_REG, tmp >> 8);\r
679         }\r
680 \r
681         if(!MDma_Start(DmaChan,SS_DMAMEM,md_dmabufsize,INDEF_WRITE)){\r
682                 return;\r
683         }\r
684 \r
685         /* disable mode change state and start the CoDec */\r
686         outportb(WavePort + CD_ADDR_OFF, 0x00);\r
687         CdWrite(CD_CONFIG_REG, direction ? 0x02 : 0x01);\r
688 }\r
689 \r
690 \r
691 static void SS_PlayStop(void)\r
692 {\r
693         StopCoDec();\r
694         VC_PlayStop();\r
695 }\r
696 \r
697 \r
698 DRIVER drv_ss={\r
699         NULL,\r
700         "Ensoniq Soundscape",\r
701         "MikMod Ensoniq Soundscape Driver v0.0 - Thanks to CyberCerus",\r
702         SS_IsThere,\r
703         VC_SampleLoad,\r
704         VC_SampleUnload,\r
705         SS_Init,\r
706         SS_Exit,\r
707         SS_PlayStart,\r
708         SS_PlayStop,\r
709         SS_Update,\r
710         VC_VoiceSetVolume,\r
711         VC_VoiceSetFrequency,\r
712         VC_VoiceSetPanning,\r
713         VC_VoicePlay\r
714 };\r