6d85d471564aff8a1070bff2c0f892492d038a5f
[dosdemo] / libs / oldmik / src / drv_sb.c
1 /*\r
2 \r
3 Name:\r
4 DRV_SB.C\r
5 \r
6 Description:\r
7 Mikmod driver for output on Creative Labs Soundblasters & compatibles\r
8 (through DSP)\r
9 \r
10 Portability:\r
11 \r
12 MSDOS:  BC(y)   Watcom(y)       DJGPP(y)\r
13 Win95:  n\r
14 Os2:    n\r
15 Linux:  n\r
16 \r
17 (y) - yes\r
18 (n) - no (not possible or not useful)\r
19 (?) - may be possible, but not tested\r
20 \r
21 */\r
22 #include <stdio.h>\r
23 #include <stdlib.h>\r
24 #include <dos.h>\r
25 #include <malloc.h>\r
26 #include <conio.h>\r
27 #ifndef __DJGPP__\r
28 #include <mem.h>\r
29 #endif\r
30 \r
31 #include "mikmod.h"\r
32 #include "mdma.h"\r
33 #include "mirq.h"\r
34 \r
35 /***************************************************************************\r
36 >>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SB stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
37 ***************************************************************************/\r
38 \r
39 static UWORD sb_port;          /* sb base port */\r
40 \r
41 /*\r
42         Define some important SB i/o ports:\r
43 */\r
44 \r
45 #define MIXER_ADDRESS           (sb_port+0x4)\r
46 #define MIXER_DATA                      (sb_port+0x5)\r
47 #define DSP_RESET                       (sb_port+0x6)\r
48 #define DSP_READ_DATA           (sb_port+0xa)\r
49 #define DSP_WRITE_DATA          (sb_port+0xc)\r
50 #define DSP_WRITE_STATUS        (sb_port+0xc)\r
51 #define DSP_DATA_AVAIL          (sb_port+0xe)\r
52 \r
53 \r
54 static void SB_MixerStereo(void)\r
55 /*\r
56         Enables stereo output for DSP versions 3.00 >= ver < 4.00\r
57 */\r
58 {\r
59         outportb(MIXER_ADDRESS,0xe);\r
60         outportb(MIXER_DATA,inportb(MIXER_DATA)|2);\r
61 }\r
62 \r
63 \r
64 \r
65 static void SB_MixerMono(void)\r
66 /*\r
67         Disables stereo output for DSP versions 3.00 >= ver < 4.00\r
68 */\r
69 {\r
70         outportb(MIXER_ADDRESS,0xe);\r
71         outportb(MIXER_DATA,inportb(MIXER_DATA)&0xfd);\r
72 }\r
73 \r
74 \r
75 \r
76 static BOOL SB_WaitDSPWrite(void)\r
77 /*\r
78         Waits until the DSP is ready to be written to.\r
79 \r
80         returns FALSE on timeout\r
81 */\r
82 {\r
83         UWORD timeout=32767;\r
84 \r
85         while(timeout--){\r
86                 if(!(inportb(DSP_WRITE_STATUS)&0x80)) return 1;\r
87         }\r
88         return 0;\r
89 }\r
90 \r
91 \r
92 \r
93 static BOOL SB_WaitDSPRead(void)\r
94 /*\r
95         Waits until the DSP is ready to read from.\r
96 \r
97         returns FALSE on timeout\r
98 */\r
99 {\r
100         UWORD timeout=32767;\r
101 \r
102         while(timeout--){\r
103                 if(inportb(DSP_DATA_AVAIL)&0x80) return 1;\r
104         }\r
105         return 0;\r
106 }\r
107 \r
108 \r
109 \r
110 static BOOL SB_WriteDSP(UBYTE data)\r
111 /*\r
112         Writes byte 'data' to the DSP.\r
113 \r
114         returns FALSE on timeout.\r
115 */\r
116 {\r
117         if(!SB_WaitDSPWrite()) return 0;\r
118         outportb(DSP_WRITE_DATA,data);\r
119         return 1;\r
120 }\r
121 \r
122 \r
123 \r
124 static UWORD SB_ReadDSP(void)\r
125 /*\r
126         Reads a byte from the DSP.\r
127 \r
128         returns 0xffff on timeout.\r
129 */\r
130 {\r
131         if(!SB_WaitDSPRead()) return 0xffff;\r
132         return(inportb(DSP_READ_DATA));\r
133 }\r
134 \r
135 \r
136 \r
137 static void SB_SpeakerOn(void)\r
138 /*\r
139         Enables DAC speaker output.\r
140 */\r
141 {\r
142         SB_WriteDSP(0xd1);\r
143 }\r
144 \r
145 \r
146 \r
147 static void SB_SpeakerOff(void)\r
148 /*\r
149         Disables DAC speaker output\r
150 */\r
151 {\r
152         SB_WriteDSP(0xd3);\r
153 }\r
154 \r
155 \r
156 \r
157 static void SB_ResetDSP(void)\r
158 /*\r
159         Resets the DSP.\r
160 */\r
161 {\r
162         int t;\r
163         /* reset the DSP by sending 1, (delay), then 0 */\r
164         outportb(DSP_RESET,1);\r
165         for(t=0;t<8;t++) inportb(DSP_RESET);\r
166         outportb(DSP_RESET,0);\r
167 }\r
168 \r
169 \r
170 \r
171 static BOOL SB_Ping(void)\r
172 /*\r
173         Checks if a SB is present at the current baseport by\r
174         resetting the DSP and checking if it returned the value 0xaa.\r
175 \r
176         returns: TRUE   => SB is present\r
177                          FALSE  => No SB detected\r
178 */\r
179 {\r
180         SB_ResetDSP();\r
181         return(SB_ReadDSP()==0xaa);\r
182 }\r
183 \r
184 \r
185 \r
186 static UWORD SB_GetDSPVersion(void)\r
187 /*\r
188         Gets SB-dsp version. returns 0xffff if dsp didn't respond.\r
189 */\r
190 {\r
191         UWORD hi,lo;\r
192 \r
193         if(!SB_WriteDSP(0xe1)) return 0xffff;\r
194 \r
195         hi=SB_ReadDSP();\r
196         lo=SB_ReadDSP();\r
197 \r
198         return((hi<<8)|lo);\r
199 }\r
200 \r
201 \r
202 \r
203 /***************************************************************************\r
204 >>>>>>>>>>>>>>>>>>>>>>>>> The actual SB driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
205 ***************************************************************************/\r
206 \r
207 static DMAMEM *SB_DMAMEM;\r
208 static signed char *SB_DMABUF;\r
209 \r
210 static UBYTE SB_TIMECONSTANT;\r
211 \r
212 static UBYTE PIC1MSK;\r
213 static UBYTE PIC2MSK;\r
214 \r
215 static UWORD sb_int;           /* interrupt vector that belongs to sb_irq */\r
216 static UWORD sb_ver;           /* DSP version number */\r
217 static UBYTE sb_irq;           /* sb irq */\r
218 static UBYTE sb_lodma;         /* 8 bit dma channel (1.0/2.0/pro) */\r
219 static UBYTE sb_hidma;         /* 16 bit dma channel (16/16asp) */\r
220 static UBYTE sb_dma;           /* current dma channel */\r
221 \r
222 \r
223 static BOOL SB_IsThere(void)\r
224 {\r
225         char *envptr,c;\r
226         static char *endptr;\r
227 \r
228         sb_port =0xffff;\r
229         sb_irq  =0xff;\r
230         sb_lodma=0xff;\r
231         sb_hidma=0xff;\r
232 \r
233         if((envptr=getenv("BLASTER"))==NULL) return 0;\r
234 \r
235         while(1){\r
236 \r
237                 /* skip whitespace */\r
238 \r
239                 do c=*(envptr++); while(c==' ' || c=='\t');\r
240 \r
241                 /* reached end of string? -> exit */\r
242 \r
243                 if(c==0) break;\r
244 \r
245                 switch(c){\r
246 \r
247                         case 'a':\r
248                         case 'A':\r
249                                 sb_port=strtol(envptr,&endptr,16);\r
250                                 break;\r
251 \r
252                         case 'i':\r
253                         case 'I':\r
254                                 sb_irq=strtol(envptr,&endptr,10);\r
255                                 break;\r
256 \r
257                         case 'd':\r
258                         case 'D':\r
259                                 sb_lodma=strtol(envptr,&endptr,10);\r
260                                 break;\r
261 \r
262                         case 'h':\r
263                         case 'H':\r
264                                 sb_hidma=strtol(envptr,&endptr,10);\r
265                                 break;\r
266 \r
267                         default:\r
268                                 strtol(envptr,&endptr,16);\r
269                                 break;\r
270                 }\r
271                 envptr=endptr;\r
272         }\r
273 \r
274         if(sb_port==0xffff || sb_irq==0xff || sb_lodma==0xff) return 0;\r
275 \r
276         /* determine interrupt vector */\r
277 \r
278         sb_int = (sb_irq>7) ? sb_irq+104 : sb_irq+8;\r
279 \r
280         if(!SB_Ping()) return 0;\r
281 \r
282         /* get dsp version. */\r
283 \r
284         if((sb_ver=SB_GetDSPVersion())==0xffff) return 0;\r
285 \r
286         return 1;\r
287 }\r
288 \r
289 \r
290 static void interrupt newhandler(MIRQARGS)\r
291 {\r
292         if(sb_ver<0x200){\r
293                 SB_WriteDSP(0x14);\r
294                 SB_WriteDSP(0xff);\r
295                 SB_WriteDSP(0xfe);\r
296         }\r
297 \r
298         if(md_mode & DMODE_16BITS)\r
299                 inportb(sb_port+0xf);\r
300         else\r
301                 inportb(DSP_DATA_AVAIL);\r
302 \r
303         MIrq_EOI(sb_irq);\r
304 }\r
305 \r
306 \r
307 static PVI oldhandler;\r
308 \r
309 \r
310 static BOOL SB_Init(void)\r
311 {\r
312         ULONG t;\r
313 \r
314         if(!SB_IsThere()){\r
315                 myerr="No such hardware detected, check your 'BLASTER' env. variable";\r
316                 return 0;\r
317         }\r
318 \r
319 /*      printf("SB version %x\n",sb_ver); */\r
320 /*      if(sb_ver>0x200) sb_ver=0x200; */\r
321 \r
322         if(sb_ver>=0x400 && sb_hidma==0xff){\r
323                 myerr="High-dma setting in 'BLASTER' variable is required for SB-16";\r
324                 return 0;\r
325         }\r
326 \r
327         if(sb_ver<0x400 && md_mode&DMODE_16BITS){\r
328                 /* DSP versions below 4.00 can't do 16 bit sound. */\r
329                 md_mode&=~DMODE_16BITS;\r
330         }\r
331 \r
332         if(sb_ver<0x300 && md_mode&DMODE_STEREO){\r
333                 /* DSP versions below 3.00 can't do stereo sound. */\r
334                 md_mode&=~DMODE_STEREO;\r
335         }\r
336 \r
337         /* Use low dma channel for 8 bit, high dma for 16 bit */\r
338 \r
339         sb_dma=(md_mode & DMODE_16BITS) ? sb_hidma : sb_lodma;\r
340 \r
341         if(sb_ver<0x400){\r
342 \r
343                 t=md_mixfreq;\r
344                 if(md_mode & DMODE_STEREO) t<<=1;\r
345 \r
346                 SB_TIMECONSTANT=256-(1000000L/t);\r
347 \r
348                 if(sb_ver<0x201){\r
349                         if(SB_TIMECONSTANT>210) SB_TIMECONSTANT=210;\r
350                 }\r
351                 else{\r
352                         if(SB_TIMECONSTANT>233) SB_TIMECONSTANT=233;\r
353                 }\r
354 \r
355                 md_mixfreq=1000000L/(256-SB_TIMECONSTANT);\r
356                 if(md_mode & DMODE_STEREO) md_mixfreq>>=1;\r
357         }\r
358 \r
359         if(!VC_Init()) return 0;\r
360 \r
361         SB_DMAMEM=MDma_AllocMem(md_dmabufsize);\r
362 \r
363         if(SB_DMAMEM==NULL){\r
364                 VC_Exit();\r
365                 myerr="Couldn't allocate page-contiguous dma-buffer";\r
366                 return 0;\r
367         }\r
368 \r
369         SB_DMABUF=(char *)MDma_GetPtr(SB_DMAMEM);\r
370 \r
371         oldhandler=MIrq_SetHandler(sb_irq,newhandler);\r
372         return 1;\r
373 }\r
374 \r
375 \r
376 \r
377 static void SB_Exit(void)\r
378 {\r
379         MIrq_SetHandler(sb_irq,oldhandler);\r
380         MDma_FreeMem(SB_DMAMEM);\r
381         VC_Exit();\r
382 }\r
383 \r
384 \r
385 static UWORD last=0;\r
386 static UWORD curr=0;\r
387 \r
388 \r
389 static void SB_Update(void)\r
390 {\r
391         UWORD todo,index;\r
392 \r
393         curr=(md_dmabufsize-MDma_Todo(sb_dma))&0xfffc;\r
394 \r
395         if(curr==last) return;\r
396 \r
397         if(curr>last){\r
398                 todo=curr-last; index=last;\r
399                 last+=VC_WriteBytes(&SB_DMABUF[index],todo);\r
400                 MDma_Commit(SB_DMAMEM,index,todo);\r
401                 if(last>=md_dmabufsize) last=0;\r
402         }\r
403         else{\r
404                 todo=md_dmabufsize-last;\r
405                 VC_WriteBytes(&SB_DMABUF[last],todo);\r
406                 MDma_Commit(SB_DMAMEM,last,todo);\r
407                 last=VC_WriteBytes(SB_DMABUF,curr);\r
408                 MDma_Commit(SB_DMAMEM,0,curr);\r
409         }\r
410 }\r
411 \r
412 \r
413 \r
414 \r
415 static void SB_PlayStart(void)\r
416 {\r
417         VC_PlayStart();\r
418 \r
419         MIrq_OnOff(sb_irq,1);\r
420 \r
421         if(sb_ver>=0x300 && sb_ver<0x400){\r
422                 if(md_mode & DMODE_STEREO){\r
423                         SB_MixerStereo();\r
424                 }\r
425                 else{\r
426                         SB_MixerMono();\r
427                 }\r
428         }\r
429 \r
430         /* clear the dma buffer */\r
431 \r
432         VC_SilenceBytes(SB_DMABUF,md_dmabufsize);\r
433         MDma_Commit(SB_DMAMEM,0,md_dmabufsize);\r
434 \r
435         if(!MDma_Start(sb_dma,SB_DMAMEM,md_dmabufsize,INDEF_WRITE)){\r
436                 return;\r
437         }\r
438 \r
439         if(sb_ver<0x400){\r
440                 SB_SpeakerOn();\r
441 \r
442                 SB_WriteDSP(0x40);\r
443                 SB_WriteDSP(SB_TIMECONSTANT);\r
444 \r
445                 if(sb_ver<0x200){\r
446                         SB_WriteDSP(0x14);\r
447                         SB_WriteDSP(0xff);\r
448                         SB_WriteDSP(0xfe);\r
449                 }\r
450                 else if(sb_ver==0x200){\r
451                         SB_WriteDSP(0x48);\r
452                         SB_WriteDSP(0xff);\r
453                         SB_WriteDSP(0xfe);\r
454                         SB_WriteDSP(0x1c);\r
455                 }\r
456                 else{\r
457                         SB_WriteDSP(0x48);\r
458                         SB_WriteDSP(0xff);\r
459                         SB_WriteDSP(0xfe);\r
460                         SB_WriteDSP(0x90);\r
461                 }\r
462         }\r
463         else{\r
464                 SB_WriteDSP(0x41);\r
465 \r
466                 SB_WriteDSP(md_mixfreq>>8);\r
467                 SB_WriteDSP(md_mixfreq&0xff);\r
468 \r
469                 if(md_mode & DMODE_16BITS){\r
470                         SB_WriteDSP(0xb6);\r
471                         SB_WriteDSP((md_mode & DMODE_STEREO) ? 0x30 : 0x10);\r
472                 }\r
473                 else{\r
474                         SB_WriteDSP(0xc6);\r
475                         SB_WriteDSP((md_mode & DMODE_STEREO) ? 0x20 : 0x00);\r
476                 }\r
477 \r
478                 SB_WriteDSP(0xff);\r
479                 SB_WriteDSP(0xef);\r
480         }\r
481 }\r
482 \r
483 \r
484 static void SB_PlayStop(void)\r
485 {\r
486         VC_PlayStop();\r
487         SB_SpeakerOff();\r
488         SB_ResetDSP();\r
489         SB_ResetDSP();\r
490         MDma_Stop(sb_dma);\r
491         MIrq_OnOff(sb_irq,0);\r
492 }\r
493 \r
494 \r
495 DRIVER drv_sb={\r
496         NULL,\r
497         "Soundblaster & compatibles",\r
498         "MikMod Soundblaster Driver v2.1 for 1.0 / 2.0 / Pro / 16",\r
499         SB_IsThere,\r
500         VC_SampleLoad,\r
501         VC_SampleUnload,\r
502         SB_Init,\r
503         SB_Exit,\r
504         SB_PlayStart,\r
505         SB_PlayStop,\r
506         SB_Update,\r
507         VC_VoiceSetVolume,\r
508         VC_VoiceSetFrequency,\r
509         VC_VoiceSetPanning,\r
510         VC_VoicePlay\r
511 };\r