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