1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "dosisms.h"
23
24 int BLASTER_GetDMAPos(void);
25
26 /*
27 ===============================================================================
28 GUS SUPPORT
29
30 ===============================================================================
31 */
32
33 qboolean GUS_Init (void);
34 int GUS_GetDMAPos (void);
35 void GUS_Shutdown (void);
36
37
38 /*
39 ===============================================================================
40
41 BLASTER SUPPORT
42
43 ===============================================================================
44 */
45
46 short *dma_buffer=0;
47 static int dma_size;
48 static int dma;
49
50 static int dsp_port;
51 static int irq;
52 static int low_dma;
53 static int high_dma;
54 static int mixer_port;
55 static int mpu401_port;
56
57 int dsp_version;
58 int dsp_minor_version;
59
60 int timeconstant=-1;
61
62
PrintBits(byte b)63 void PrintBits (byte b)
64 {
65 int i;
66 char str[9];
67
68 for (i=0 ; i<8 ; i++)
69 str[i] = '0' + ((b & (1<<(7-i))) > 0);
70
71 str[8] = 0;
72 Con_Printf ("%s (%i)", str, b);
73 }
74
SB_Info_f(void)75 void SB_Info_f(void)
76 {
77 Con_Printf ("BLASTER=%s\n", getenv("BLASTER"));
78 Con_Printf("dsp version=%d.%d\n", dsp_version, dsp_minor_version);
79 Con_Printf("dma=%d\n", dma);
80 if (timeconstant != -1)
81 Con_Printf("timeconstant=%d\n", timeconstant);
82 Con_Printf("dma position:%i\n", BLASTER_GetDMAPos ());
83 }
84
85 // =======================================================================
86 // Interprets BLASTER variable
87 // =======================================================================
88
GetBLASTER(void)89 int GetBLASTER(void)
90 {
91 char *BLASTER;
92 char *param;
93
94 BLASTER = getenv("BLASTER");
95 if (!BLASTER)
96 return 0;
97
98 param = strchr(BLASTER, 'A');
99 if (!param)
100 param = strchr(BLASTER, 'a');
101 if (!param)
102 return 0;
103 sscanf(param+1, "%x", &dsp_port);
104
105 param = strchr(BLASTER, 'I');
106 if (!param)
107 param = strchr(BLASTER, 'i');
108 if (!param)
109 return 0;
110 sscanf(param+1, "%d", &irq);
111
112 param = strchr(BLASTER, 'D');
113 if (!param)
114 param = strchr(BLASTER, 'd');
115 if (!param)
116 return 0;
117 sscanf(param+1, "%d", &low_dma);
118
119 param = strchr(BLASTER, 'H');
120 if (!param)
121 param = strchr(BLASTER, 'h');
122 if (param)
123 sscanf(param+1, "%d", &high_dma);
124
125 param = strchr(BLASTER, 'M');
126 if (!param)
127 param = strchr(BLASTER, 'm');
128 if (param)
129 sscanf(param+1, "%x", &mixer_port);
130 else
131 mixer_port = dsp_port;
132
133 param = strchr(BLASTER, 'P');
134 if (!param)
135 param = strchr(BLASTER, 'p');
136 if (param)
137 sscanf(param+1, "%x", &mpu401_port);
138
139 return 1;
140
141 }
142
143 // ==================================================================
144 // Resets DSP. Returns 0 on success.
145 // ==================================================================
146
ResetDSP(void)147 int ResetDSP(void)
148 {
149 volatile int i;
150
151 dos_outportb(dsp_port + 6, 1);
152 for (i=65536 ; i ; i--) ;
153 dos_outportb(dsp_port + 6, 0);
154 for (i=65536 ; i ; i--)
155 {
156 if (!(dos_inportb(dsp_port + 0xe) & 0x80)) continue;
157 if (dos_inportb(dsp_port + 0xa) == 0xaa) break;
158 }
159 if (i) return 0;
160 else return 1;
161
162 }
163
ReadDSP(void)164 int ReadDSP(void)
165 {
166 while (!(dos_inportb(dsp_port+0xe)&0x80)) ;
167 return dos_inportb(dsp_port+0xa);
168 }
169
WriteDSP(int val)170 void WriteDSP(int val)
171 {
172 while ((dos_inportb(dsp_port+0xc)&0x80)) ;
173 dos_outportb(dsp_port+0xc, val);
174 }
175
ReadMixer(int addr)176 int ReadMixer(int addr)
177 {
178 dos_outportb(mixer_port+4, addr);
179 return dos_inportb(mixer_port+5);
180 }
181
WriteMixer(int addr,int val)182 void WriteMixer(int addr, int val)
183 {
184 dos_outportb(mixer_port+4, addr);
185 dos_outportb(mixer_port+5, val);
186 }
187
188 int oldmixervalue;
189
190 /*
191 ================
192 StartSB
193
194 ================
195 */
StartSB(void)196 void StartSB(void)
197 {
198 int i;
199
200 // version 4.xx startup code
201 if (dsp_version >= 4)
202 {
203 Con_Printf("Version 4 SB startup\n");
204 WriteDSP(0xd1); // turn on speaker
205
206 WriteDSP(0x41);
207
208 WriteDSP(shm->speed>>8);
209 WriteDSP(shm->speed&0xff);
210
211 WriteDSP(0xb6); // 16-bit output
212 WriteDSP(0x30); // stereo
213 WriteDSP((shm->samples-1) & 0xff); // # of samples - 1
214 WriteDSP((shm->samples-1) >> 8);
215 }
216 // version 3.xx startup code
217 else if (dsp_version == 3)
218 {
219 Con_Printf("Version 3 SB startup\n");
220 WriteDSP(0xd1); // turn on speaker
221
222 oldmixervalue = ReadMixer (0xe);
223 WriteMixer (0xe, oldmixervalue | 0x2);// turn on stereo
224
225 WriteDSP(0x14); // send one byte
226 WriteDSP(0x0);
227 WriteDSP(0x0);
228
229 for (i=0 ; i<0x10000 ; i++)
230 dos_inportb(dsp_port+0xe); // ack the dsp
231
232 timeconstant = 65536-(256000000/(shm->channels*shm->speed));
233 WriteDSP(0x40);
234 WriteDSP(timeconstant>>8);
235
236 WriteMixer (0xe, ReadMixer(0xe) | 0x20);// turn off filter
237
238 WriteDSP(0x48);
239 WriteDSP((shm->samples-1) & 0xff); // # of samples - 1
240 WriteDSP((shm->samples-1) >> 8);
241
242 WriteDSP(0x90); // high speed 8 bit stereo
243 }
244 // normal speed mono
245 else
246 {
247 Con_Printf("Version 2 SB startup\n");
248 WriteDSP(0xd1); // turn on speaker
249
250 timeconstant = 65536-(256000000/(shm->channels*shm->speed));
251 WriteDSP(0x40);
252 WriteDSP(timeconstant>>8);
253
254 WriteDSP(0x48);
255 WriteDSP((shm->samples-1) & 0xff); // # of samples - 1
256 WriteDSP((shm->samples-1) >> 8);
257
258 WriteDSP(0x1c); // normal speed 8 bit mono
259 }
260 }
261
262 static int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
263 static int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
264 static int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
265
266 static int mode_reg;
267 static int flipflop_reg;
268 static int disable_reg;
269 static int clear_reg;
270
271 /*
272 ================
273 StartDMA
274
275 ================
276 */
StartDMA(void)277 void StartDMA(void)
278 {
279 int mode;
280 int realaddr;
281
282 realaddr = ptr2real(dma_buffer);
283
284 // use a high dma channel if specified
285 if (high_dma && dsp_version >= 4) // 8 bit snd can never use 16 bit dma
286 dma = high_dma;
287 else
288 dma = low_dma;
289
290 Con_Printf ("Using DMA channel %i\n", dma);
291
292 if (dma > 3)
293 {
294 mode_reg = 0xd6;
295 flipflop_reg = 0xd8;
296 disable_reg = 0xd4;
297 clear_reg = 0xdc;
298 }
299 else
300 {
301 mode_reg = 0xb;
302 flipflop_reg = 0xc;
303 disable_reg = 0xa;
304 clear_reg = 0xe;
305 }
306
307 dos_outportb(disable_reg, dma|4); // disable channel
308 // set mode- see "undocumented pc", p.876
309 mode = (1<<6) // single-cycle
310 +(0<<5) // address increment
311 +(1<<4) // auto-init dma
312 +(2<<2) // read
313 +(dma&3); // channel #
314 dos_outportb(mode_reg, mode);
315
316 // set address
317 // set page
318 dos_outportb(page_reg[dma], realaddr >> 16);
319
320 if (dma > 3)
321 { // address is in words
322 dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
323 dos_outportb(addr_reg[dma], (realaddr>>1) & 0xff);
324 dos_outportb(addr_reg[dma], (realaddr>>9) & 0xff);
325
326 dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
327 dos_outportb(count_reg[dma], ((dma_size>>1)-1) & 0xff);
328 dos_outportb(count_reg[dma], ((dma_size>>1)-1) >> 8);
329 }
330 else
331 { // address is in bytes
332 dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
333 dos_outportb(addr_reg[dma], realaddr & 0xff);
334 dos_outportb(addr_reg[dma], (realaddr>>8) & 0xff);
335
336 dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
337 dos_outportb(count_reg[dma], (dma_size-1) & 0xff);
338 dos_outportb(count_reg[dma], (dma_size-1) >> 8);
339 }
340
341 dos_outportb(clear_reg, 0); // clear write mask
342 dos_outportb(disable_reg, dma&~4);
343 }
344
345
346 /*
347 ==================
348 BLASTER_Init
349
350 Returns false if nothing is found.
351 ==================
352 */
BLASTER_Init(void)353 qboolean BLASTER_Init(void)
354 {
355 int size;
356 int realaddr;
357 int rc;
358 int p;
359
360 shm = 0;
361 rc = 0;
362
363 //
364 // must have a blaster variable set
365 //
366 if (!GetBLASTER())
367 {
368 Con_NotifyBox (
369 "The BLASTER environment variable\n"
370 "is not set, sound effects are\n"
371 "disabled. See README.TXT for help.\n"
372 );
373 return 0;
374 }
375
376 if (ResetDSP())
377 {
378 Con_Printf("Could not reset SB");
379 return 0;
380 }
381
382 //
383 // get dsp version
384 //
385 WriteDSP(0xe1);
386 dsp_version = ReadDSP();
387 dsp_minor_version = ReadDSP();
388
389 // we need at least v2 for auto-init dma
390 if (dsp_version < 2)
391 {
392 Con_Printf ("Sound blaster must be at least v2.0\n");
393 return 0;
394 }
395
396 // allow command line parm to set quality down
397 p = COM_CheckParm ("-dsp");
398 if (p && p < com_argc - 1)
399 {
400 p = Q_atoi (com_argv[p+1]);
401 if (p < 2 || p > 4)
402 Con_Printf ("-dsp parameter can only be 2, 3, or 4\n");
403 else if (p > dsp_version)
404 Con_Printf ("Can't -dsp %i on v%i hardware\n", p, dsp_version);
405 else
406 dsp_version = p;
407 }
408
409
410 // everyone does 11khz sampling rate unless told otherwise
411 shm = &sn;
412 shm->speed = 11025;
413 rc = COM_CheckParm("-sspeed");
414 if (rc)
415 shm->speed = Q_atoi(com_argv[rc+1]);
416
417 // version 4 cards (sb 16) do 16 bit stereo
418 if (dsp_version >= 4)
419 {
420 shm->channels = 2;
421 shm->samplebits = 16;
422 }
423 // version 3 cards (sb pro) do 8 bit stereo
424 else if (dsp_version == 3)
425 {
426 shm->channels = 2;
427 shm->samplebits = 8;
428 }
429 // v2 cards do 8 bit mono
430 else
431 {
432 shm->channels = 1;
433 shm->samplebits = 8;
434 }
435
436
437 Cmd_AddCommand("sbinfo", SB_Info_f);
438 size = 4096;
439
440 // allocate 8k and get a 4k-aligned buffer from it
441 dma_buffer = dos_getmemory(size*2);
442 if (!dma_buffer)
443 {
444 Con_Printf("Couldn't allocate sound dma buffer");
445 return false;
446 }
447
448 realaddr = ptr2real(dma_buffer);
449 realaddr = (realaddr + size) & ~(size-1);
450 dma_buffer = (short *) real2ptr(realaddr);
451 dma_size = size;
452
453 memset(dma_buffer, 0, dma_size);
454
455 shm->soundalive = true;
456 shm->splitbuffer = false;
457
458 shm->samples = size/(shm->samplebits/8);
459 shm->samplepos = 0;
460 shm->submission_chunk = 1;
461 shm->buffer = (unsigned char *) dma_buffer;
462 shm->samples = size/(shm->samplebits/8);
463
464 StartDMA();
465 StartSB();
466
467 return true;
468 }
469
470
471 /*
472 ==============
473 BLASTER_GetDMAPos
474
475 return the current sample position (in mono samples read)
476 inside the recirculating dma buffer, so the mixing code will know
477 how many sample are required to fill it up.
478 ===============
479 */
BLASTER_GetDMAPos(void)480 int BLASTER_GetDMAPos(void)
481 {
482 int count;
483
484 // this function is called often. acknowledge the transfer completions
485 // all the time so that it loops
486 if (dsp_version >= 4)
487 dos_inportb(dsp_port+0xf); // 16 bit audio
488 else
489 dos_inportb(dsp_port+0xe); // 8 bit audio
490
491 // clear 16-bit reg flip-flop
492 // load the current dma count register
493 if (dma < 4)
494 {
495 dos_outportb(0xc, 0);
496 count = dos_inportb(dma*2+1);
497 count += dos_inportb(dma*2+1) << 8;
498 if (shm->samplebits == 16)
499 count /= 2;
500 count = shm->samples - (count+1);
501 }
502 else
503 {
504 dos_outportb(0xd8, 0);
505 count = dos_inportb(0xc0+(dma-4)*4+2);
506 count += dos_inportb(0xc0+(dma-4)*4+2) << 8;
507 if (shm->samplebits == 8)
508 count *= 2;
509 count = shm->samples - (count+1);
510 }
511
512 // Con_Printf("DMA pos = 0x%x\n", count);
513
514 shm->samplepos = count & (shm->samples-1);
515 return shm->samplepos;
516
517 }
518
519 /*
520 ==============
521 BLASTER_Shutdown
522
523 Reset the sound device for exiting
524 ===============
525 */
BLASTER_Shutdown(void)526 void BLASTER_Shutdown(void)
527 {
528 if (dsp_version >= 4)
529 {
530 }
531 else if (dsp_version == 3)
532 {
533 ResetDSP (); // stop high speed mode
534 WriteMixer (0xe, oldmixervalue); // turn stereo off and filter on
535 }
536 else
537 {
538
539 }
540
541 WriteDSP(0xd3); // turn off speaker
542 ResetDSP ();
543
544 dos_outportb(disable_reg, dma|4); // disable dma channel
545 }
546
547
548
549 /*
550 ===============================================================================
551
552 INTERFACE
553
554 ===============================================================================
555 */
556
557 typedef enum
558 {
559 dma_none,
560 dma_blaster,
561 dma_gus
562 } dmacard_t;
563
564 dmacard_t dmacard;
565
566 /*
567 ==================
568 SNDDM_Init
569
570 Try to find a sound device to mix for.
571 Returns false if nothing is found.
572 Returns true and fills in the "shm" structure with information for the mixer.
573 ==================
574 */
SNDDMA_Init(void)575 qboolean SNDDMA_Init(void)
576 {
577 if (GUS_Init ())
578 {
579 dmacard = dma_gus;
580 return true;
581 }
582 if (BLASTER_Init ())
583 {
584 dmacard = dma_blaster;
585 return true;
586 }
587
588 dmacard = dma_none;
589
590 return false;
591 }
592
593
594 /*
595 ==============
596 SNDDMA_GetDMAPos
597
598 return the current sample position (in mono samples, not stereo)
599 inside the recirculating dma buffer, so the mixing code will know
600 how many sample are required to fill it up.
601 ===============
602 */
SNDDMA_GetDMAPos(void)603 int SNDDMA_GetDMAPos(void)
604 {
605 switch (dmacard)
606 {
607 case dma_blaster:
608 return BLASTER_GetDMAPos ();
609 case dma_gus:
610 return GUS_GetDMAPos ();
611 case dma_none:
612 break;
613 }
614
615 return 0;
616 }
617
618 /*
619 ==============
620 SNDDMA_Shutdown
621
622 Reset the sound device for exiting
623 ===============
624 */
SNDDMA_Shutdown(void)625 void SNDDMA_Shutdown(void)
626 {
627 switch (dmacard)
628 {
629 case dma_blaster:
630 BLASTER_Shutdown ();
631 break;
632 case dma_gus:
633 GUS_Shutdown ();
634 break;
635 case dma_none:
636 break;
637 }
638
639 dmacard = dma_none;
640 return;
641 }
642
643 /*
644 ==============
645 SNDDMA_Submit
646
647 Send sound to device if buffer isn't really the dma buffer
648 ===============
649 */
SNDDMA_Submit(void)650 void SNDDMA_Submit(void)
651 {
652 }
653
654