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