|
|
1.1 root 1: //=============================================================================
2: // Routines for GUS support in QUAKE
3: //
4: // Author(s): Jayeson Lee-Steere
5: //=============================================================================
6:
7: #include "quakedef.h"
8: #include "dosisms.h"
9:
10: //=============================================================================
11: // Author(s): Jayeson Lee-Steere
12:
13: #define INI_STRING_SIZE 0x100
14:
15: FILE *ini_fopen(const char *filename, const char *modes);
16: int ini_fclose(FILE *f);
17: void ini_fgets(FILE *f, const char *section, const char *field, char *s);
18:
19: // Routines for reading from .INI files
20: // The read routines are fairly efficient.
21: //
22: // Author(s): Jayeson Lee-Steere
23:
24: #define MAX_SECTION_WIDTH 20
25: #define MAX_FIELD_WIDTH 20
26:
27: #define NUM_SECTION_BUFFERS 10
28: #define NUM_FIELD_BUFFERS 20
29:
30: struct section_buffer
31: {
32: long offset;
33: char name[MAX_SECTION_WIDTH+1];
34: };
35:
36: struct field_buffer
37: {
38: long offset;
39: int section;
40: char name[MAX_FIELD_WIDTH+1];
41: };
42:
43: static FILE *current_file=NULL;
44: static int current_section;
45:
46: static int current_section_buffer=0;
47: static int current_field_buffer=0;
48:
49: static struct section_buffer section_buffers[NUM_SECTION_BUFFERS];
50: static struct field_buffer field_buffers[NUM_FIELD_BUFFERS];
51: //***************************************************************************
52: // Internal routines
53: //***************************************************************************
54: static char toupper(char c)
55: {
56: if (c>='a' && c<='z')
57: c-=('a'-'A');
58: return(c);
59: }
60:
61: static void reset_buffer(FILE *f)
62: {
63: int i;
64:
65: for (i=0;i<NUM_SECTION_BUFFERS;i++)
66: section_buffers[i].name[0]=0;
67: for (i=0;i<NUM_FIELD_BUFFERS;i++)
68: field_buffers[i].name[0]=0;
69:
70: current_file=f;
71: }
72:
73: // Sees if the current string is section "name" (i.e. ["name"]).
74: // If "name"=="*", sees if the current string is any section
75: // (i.e. [....]). Returns 1 if true else 0 if false.
76: static int is_section(char *s,const char *name)
77: {
78: int wild=0;
79:
80: // See if wild search
81: if (strcmp("*",name)==0)
82: wild=1;
83:
84: // Skip leading spaces
85: while (s[0]==' ')
86: s++;
87: // Look for leading "["
88: if (s[0]!='[')
89: return(0);
90: s++;
91: // Make sure name matches
92: while (s[0]!=']' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0)
93: {
94: if (!wild)
95: if (toupper(s[0])!=toupper(name[0]))
96: return(0);
97: s++;
98: if (!wild)
99: name++;
100: }
101: if (!wild)
102: if (name[0]!=0)
103: return(0);
104: // Skip trailing spaces
105: while (s[0]==' ')
106: s++;
107: // Make sure we have trailing "]"
108: if (s[0]!=']')
109: return(0);
110: return(1);
111: }
112:
113: // Sees if the current string is field "name" (i.e. "name"=...).
114: // If "name"=="*", sees if the current string is any field
115: // (i.e. ...=...). Returns 1 if true else 0 if false.
116: static int is_field(char *s,const char *name)
117: {
118: int wild=0;
119:
120: // See if wild search
121: if (strcmp("*",name)==0)
122: wild=1;
123:
124: // Skip leading spaces
125: while (s[0]==' ')
126: s++;
127:
128: // Make sure name matches
129: while (s[0]!='=' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0)
130: {
131: if (!wild)
132: if (toupper(s[0])!=toupper(name[0]))
133: return(0);
134: s++;
135: if (!wild)
136: name++;
137: }
138: if (!wild)
139: if (name[0]!=0)
140: return(0);
141: // Skip trailing spaces
142: while (s[0]==' ')
143: s++;
144: // Make sure we have an "="
145: if (s[0]!='=')
146: return(0);
147:
148: return(1);
149: }
150:
151: // Extracts the section name from a section heading
152: // e.g. in="[hey man]" gives out="hey man"
153: static void get_section_name(char *out, char *in)
154: {
155: int i=0;
156:
157: // Skip spaces before '['
158: while (in[0]==' ')
159: in++;
160: // Make sure there is a '['
161: if (in[0]!='[')
162: {
163: out[0]=0;
164: return;
165: }
166: // Skip past '['
167: in++;
168: // Copy string if any to output string.
169: while (in[0]!=']' && in[0]!=13 && in[0]!=10 && in[0]!=0)
170: {
171: if (i<MAX_SECTION_WIDTH)
172: {
173: out[i]=in[0];
174: i++;
175: }
176: in++;
177: }
178: // Make sure string was terminated with ']'
179: if (in[0]!=']')
180: {
181: out[0]=0;
182: return;
183: }
184: // Remove trailing spaces
185: while (i>0 && out[i-1]==' ')
186: i--;
187: // Null terminate the output string.
188: out[i]=0;
189: }
190:
191: // Extracts the field name from a field line
192: // e.g. in="sooty=life be in it" gives out="sooty"
193: static void get_field_name(char *out, char *in)
194: {
195: int i=0;
196:
197: // Skip leading spaces
198: while (in[0]==' ')
199: in++;
200: // Copy name to output string
201: while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0)
202: {
203: if (i<MAX_FIELD_WIDTH)
204: {
205: out[i]=in[0];
206: i++;
207: }
208: in++;
209: }
210: // Make sure we stopped on "="
211: if (in[0]!='=')
212: {
213: out[0]=0;
214: return;
215: }
216: // Remove trailing spaces
217: while (i>0 && out[i-1]==' ')
218: i--;
219: // Null terminate the output string.
220: out[i]=0;
221: }
222:
223: // Returns the field data from string s.
224: // e.g. in="wally = golly man" gives out="golly man"
225: static void get_field_string(char *out, char *in)
226: {
227: int i=0;
228:
229: // Find '=' if it exists
230: while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0)
231: in++;
232: // If there is an '=', skip past it.
233: if (in[0]=='=')
234: in++;
235: // Skip any spaces between the '=' and string.
236: while (in[0]==' ' || in[0]=='[')
237: in++;
238: // Copy string, if there is one, to the output string.
239: while (in[0]!=13 && in[0]!=10 && in[0]!=0 && i<(INI_STRING_SIZE-1))
240: {
241: out[i]=in[0];
242: in++;
243: i++;
244: }
245: // Null terminate the output string.
246: out[i]=0;
247: }
248:
249: // Adds a section to the buffer
250: static int add_section(char *instring, long offset)
251: {
252: int i;
253: char section[MAX_SECTION_WIDTH+1];
254:
255: // Extract section name
256: get_section_name(section,instring);
257: // See if section already exists.
258: for (i=0;i<NUM_SECTION_BUFFERS;i++)
259: if (stricmp(section,section_buffers[i].name)==0)
260: return(i);
261: // Increment current_section_buffer
262: current_section_buffer++;
263: if (current_section_buffer>NUM_SECTION_BUFFERS)
264: current_section_buffer=0;
265: // Delete any field buffers that correspond to this section
266: for (i=0;i<NUM_FIELD_BUFFERS;i++)
267: if (field_buffers[i].section==current_section_buffer)
268: field_buffers[i].name[0]=0;
269: // Set buffer information
270: strcpy(section_buffers[current_section_buffer].name,section);
271: section_buffers[current_section_buffer].offset=offset;
272: return(current_section_buffer);
273: }
274:
275: // Adds a field to the buffer
276: static void add_field(char *instring, int section, long offset)
277: {
278: int i;
279: char field[MAX_FIELD_WIDTH+1];
280:
281: // Extract field name
282: get_field_name(field,instring);
283: // See if field already exists
284: for (i=0;i<NUM_FIELD_BUFFERS;i++)
285: if (field_buffers[i].section==section)
286: if (stricmp(field_buffers[i].name,field)==0)
287: return;
288: // Increment current_field_buffer
289: current_field_buffer++;
290: if (current_field_buffer>NUM_FIELD_BUFFERS)
291: current_field_buffer=0;
292: // Set buffer information
293: strcpy(field_buffers[current_field_buffer].name,field);
294: field_buffers[current_field_buffer].section=section;
295: field_buffers[current_field_buffer].offset=offset;
296: }
297:
298: // Identical to fgets except the string is trucated at the first ';',
299: // carriage return or line feed.
300: static char *stripped_fgets(char *s, int n, FILE *f)
301: {
302: int i=0;
303:
304: if (fgets(s,n,f)==NULL)
305: return(NULL);
306:
307: while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0)
308: i++;
309: s[i]=0;
310:
311: return(s);
312: }
313:
314: //***************************************************************************
315: // Externally accessable routines
316: //***************************************************************************
317: // Opens an .INI file. Works like fopen
318: FILE *ini_fopen(const char *filename, const char *modes)
319: {
320: return(fopen(filename,modes));
321: }
322:
323: // Closes a .INI file. Works like fclose
324: int ini_fclose(FILE *f)
325: {
326: if (f==current_file)
327: reset_buffer(NULL);
328: return(fclose(f));
329: }
330:
331: // Puts "field" from "section" from .ini file "f" into "s".
332: // If "section" does not exist or "field" does not exist in
333: // section then s="";
334: void ini_fgets(FILE *f, const char *section, const char *field, char *s)
335: {
336: int i;
337: long start_pos,string_start_pos;
338: char ts[INI_STRING_SIZE*2];
339:
340: if (f!=current_file)
341: reset_buffer(f);
342:
343: // Default to "Not found"
344: s[0]=0;
345:
346: // See if section is in buffer
347: for (i=0;i<NUM_SECTION_BUFFERS;i++)
348: if (strnicmp(section_buffers[i].name,section,MAX_SECTION_WIDTH)==0)
349: break;
350:
351: // If section is in buffer, seek to it if necessary
352: if (i<NUM_SECTION_BUFFERS)
353: {
354: if (i!=current_section)
355: {
356: current_section=i;
357: fseek(f,section_buffers[i].offset,SEEK_SET);
358: }
359: }
360: // else look through .ini file for it.
361: else
362: {
363: // Make sure we are not at eof or this will cause trouble.
364: if (feof(f))
365: rewind(f);
366: start_pos=ftell(f);
367: while (1)
368: {
369: stripped_fgets(ts,INI_STRING_SIZE*2,f);
370: // If it is a section, add it to the section buffer
371: if (is_section(ts,"*"))
372: current_section=add_section(ts,ftell(f));
373: // If it is the section we are looking for, break.
374: if (is_section(ts,section))
375: break;
376: // If we reach the end of the file, rewind to the start.
377: if (feof(f))
378: rewind(f);
379: if (ftell(f)==start_pos)
380: return;
381: }
382: }
383:
384: // See if field is in buffer
385: for (i=0;i<NUM_FIELD_BUFFERS;i++)
386: if (field_buffers[i].section==current_section)
387: if (strnicmp(field_buffers[i].name,field,MAX_FIELD_WIDTH)==0)
388: break;
389:
390: // If field is in buffer, seek to it and read it
391: if (i<NUM_FIELD_BUFFERS)
392: {
393: fseek(f,field_buffers[i].offset,SEEK_SET);
394: stripped_fgets(ts,INI_STRING_SIZE*2,f);
395: get_field_string(s,ts);
396: }
397: else
398: // else search through section for field.
399: {
400: // Make sure we do not start at eof or this will cause problems.
401: if (feof(f))
402: fseek(f,section_buffers[current_section].offset,SEEK_SET);
403: start_pos=ftell(f);
404: while (1)
405: {
406: string_start_pos=ftell(f);
407: stripped_fgets(ts,INI_STRING_SIZE*2,f);
408: // If it is a field, add it to the buffer
409: if (is_field(ts,"*"))
410: add_field(ts,current_section,string_start_pos);
411: // If it is the field we are looking for, save it
412: if (is_field(ts,field))
413: {
414: get_field_string(s,ts);
415: break;
416: }
417: // If we reach the end of the section, start over
418: if (feof(f) || is_section(ts,"*"))
419: fseek(f,section_buffers[current_section].offset,SEEK_SET);
420: if (ftell(f)==start_pos)
421: return;
422: }
423: }
424: }
425:
426: //=============================================================================
427:
428: #define BYTE unsigned char
429: #define WORD unsigned short
430: #define DWORD unsigned long
431:
432: #define BUFFER_SIZE 4096
433:
434: #define CODEC_ADC_INPUT_CONTROL_LEFT 0x00
435: #define CODEC_ADC_INPUT_CONTROL_RIGHT 0x01
436: #define CODEC_AUX1_INPUT_CONTROL_LEFT 0x02
437: #define CODEC_AUX1_INPUT_CONTROL_RIGHT 0x03
438: #define CODEC_AUX2_INPUT_CONTROL_LEFT 0x04
439: #define CODEC_AUX2_INPUT_CONTROL_RIGHT 0x05
440: #define CODEC_DAC_OUTPUT_CONTROL_LEFT 0x06
441: #define CODEC_DAC_OUTPUT_CONTROL_RIGHT 0x07
442: #define CODEC_FS_FORMAT 0x08
443: #define CODEC_INTERFACE_CONFIG 0x09
444: #define CODEC_PIN_CONTROL 0x0A
445: #define CODEC_ERROR_STATUS_AND_INIT 0x0B
446: #define CODEC_MODE_AND_ID 0x0C
447: #define CODEC_LOOPBACK_CONTROL 0x0D
448: #define CODEC_PLAYBACK_UPPER_BASE_COUNT 0x0E
449: #define CODEC_PLAYBACK_LOWER_BASE_COUNT 0x0F
450:
1.1.1.2 ! root 451: #define SET_CONTROL 0x00
! 452: #define SET_FREQUENCY 0x01
! 453: #define SET_START_HIGH 0x02
! 454: #define SET_START_LOW 0x03
! 455: #define SET_END_HIGH 0x04
! 456: #define SET_END_LOW 0x05
! 457: #define SET_VOLUME_RATE 0x06
! 458: #define SET_VOLUME_START 0x07
! 459: #define SET_VOLUME_END 0x08
! 460: #define SET_CURR_VOLUME 0x09
! 461: #define SET_VOLUME 0x09
! 462: #define SET_ACC_HIGH 0x0A
! 463: #define SET_ACC_LOW 0x0B
! 464: #define SET_BALANCE 0x0C
! 465: #define SET_VOLUME_CONTROL 0x0D
! 466: #define SET_VOICES 0x0E
! 467:
! 468: #define DMA_CONTROL 0x41
! 469: #define SET_DMA_ADDRESS 0x42
! 470: #define SET_DRAM_LOW 0x43
! 471: #define SET_DRAM_HIGH 0x44
! 472: #define ADLIB_CONTROL 0x45
! 473: #define ADLIB_TIMER1 0x46
! 474: #define ADLIB_TIMER2 0x47
! 475: #define SET_RECORD_RATE 0x48
! 476: #define RECORD_CONTROL 0x49
! 477: #define SET_JOYSTICK 0x4B
! 478: #define MASTER_RESET 0x4C
! 479:
! 480: #define GET_CONTROL 0x80
! 481: #define GET_FREQUENCY 0x81
! 482: #define GET_START_HIGH 0x82
! 483: #define GET_START_LOW 0x83
! 484: #define GET_END_HIGH 0x84
! 485: #define GET_END_LOW 0x85
! 486: #define GET_VOLUME_RATE 0x86
! 487: #define GET_VOLUME_START 0x87
! 488: #define GET_VOLUME_END 0x88
! 489: #define GET_VOLUME 0x89
! 490: #define GET_ACC_HIGH 0x8A
! 491: #define GET_ACC_LOW 0x8B
! 492: #define GET_BALANCE 0x8C
! 493: #define GET_VOLUME_CONTROL 0x8D
! 494: #define GET_VOICES 0x8E
! 495: #define GET_IRQV 0x8F
! 496:
1.1 root 497: struct CodecRateStruct
498: {
499: WORD Rate;
500: BYTE FSVal;
501: };
502:
1.1.1.2 ! root 503: struct Gf1RateStruct
! 504: {
! 505: WORD Rate;
! 506: BYTE Voices;
! 507: };
! 508:
1.1 root 509: //=============================================================================
510: // Reference variables in SND_DOS.C
511: //=============================================================================
512: extern short *dma_buffer;
513:
514: //=============================================================================
515: // GUS-only variables
516: //=============================================================================
1.1.1.2 ! root 517: static BYTE HaveCodec=0;
! 518:
1.1 root 519: static WORD CodecRegisterSelect;
520: static WORD CodecData;
521: static WORD CodecStatus;
1.1.1.2 ! root 522: static WORD Gf1TimerControl;
! 523: static WORD Gf1PageRegister;
! 524: static WORD Gf1RegisterSelect;
! 525: static WORD Gf1DataLow;
! 526: static WORD Gf1DataHigh;
1.1 root 527:
528: static BYTE DmaChannel;
529:
530: static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
531: static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
532: static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
533:
534: static WORD AddrReg;
535: static WORD CountReg;
536: static WORD ModeReg;
537: static WORD DisableReg;
538: static WORD ClearReg;
539:
540: static struct CodecRateStruct CodecRates[]=
541: {
542: { 5512,0x01},
543: { 6620,0x0F},
544: { 8000,0x00},
545: { 9600,0x0E},
546: {11025,0x03},
547: {16000,0x02},
548: {18900,0x05},
549: {22050,0x07},
550: {27420,0x04},
551: {32000,0x06},
552: {33075,0x0D},
553: {37800,0x09},
554: {44100,0x0B},
555: {48000,0x0C},
556: { 0,0x00} // End marker
557: };
558:
1.1.1.2 ! root 559: static struct Gf1RateStruct Gf1Rates[]=
! 560: {
! 561: {19293,32},
! 562: {19916,31},
! 563: {20580,30},
! 564: {21289,29},
! 565: {22050,28},
! 566: {22866,27},
! 567: {23746,26},
! 568: {24696,25},
! 569: {25725,24},
! 570: {26843,23},
! 571: {28063,22},
! 572: {29400,21},
! 573: {30870,20},
! 574: {32494,19},
! 575: {34300,18},
! 576: {36317,17},
! 577: {38587,16},
! 578: {41160,15},
! 579: {44100,14},
! 580: {0,0}
! 581: };
! 582:
! 583: //=============================================================================
! 584: // Basic GF1 functions
! 585: //=============================================================================
! 586: void SetGf18(BYTE reg,BYTE data)
! 587: {
! 588: dos_outportb(Gf1RegisterSelect,reg);
! 589: dos_outportb(Gf1DataHigh,data);
! 590: }
! 591:
! 592: void SetGf116(BYTE reg,WORD data)
! 593: {
! 594: dos_outportb(Gf1RegisterSelect,reg);
! 595: dos_outportw(Gf1DataLow,data);
! 596: }
! 597:
! 598: BYTE GetGf18(BYTE reg)
! 599: {
! 600: dos_outportb(Gf1RegisterSelect,reg);
! 601: return(dos_inportb(Gf1DataHigh));
! 602: }
! 603:
! 604: WORD GetGf116(BYTE reg)
! 605: {
! 606: dos_outportb(Gf1RegisterSelect,reg);
! 607: return(dos_inportw(Gf1DataLow));
! 608: }
! 609:
! 610: void Gf1Delay(void)
! 611: {
! 612: int i;
! 613:
! 614: for (i=0;i<27;i++)
! 615: dos_inportb(Gf1TimerControl);
! 616: }
! 617:
! 618: DWORD ConvertTo16(DWORD Address)
! 619: {
! 620: return( ((Address>>1) & 0x0001FFFF) | (Address & 0x000C0000L) );
! 621: }
! 622:
! 623: void ClearGf1Ints(void)
! 624: {
! 625: int i;
! 626:
! 627: SetGf18(DMA_CONTROL,0x00);
! 628: SetGf18(ADLIB_CONTROL,0x00);
! 629: SetGf18(RECORD_CONTROL,0x00);
! 630:
! 631: GetGf18(DMA_CONTROL);
! 632: GetGf18(RECORD_CONTROL);
! 633: for (i=0;i<32;i++);
! 634: GetGf18(GET_IRQV);
! 635: }
! 636:
! 637:
1.1 root 638: //=============================================================================
639: // Get Interwave (UltraSound PnP) configuration if any
640: //=============================================================================
641: static qboolean GUS_GetIWData(void)
642: {
643: char *Interwave,s[INI_STRING_SIZE];
644: FILE *IwFile;
645: int CodecBase,CodecDma,i;
646:
647: Interwave=getenv("INTERWAVE");
648: if (Interwave==NULL)
649: return(false);
650:
651: // Open IW.INI
652: IwFile=ini_fopen(Interwave,"rt");
653: if (IwFile==NULL)
654: return(false);
655:
656: // Read codec base and codec DMA
657: ini_fgets(IwFile,"setup 0","CodecBase",s);
658: sscanf(s,"%X",&CodecBase);
659: ini_fgets(IwFile,"setup 0","DMA2",s);
660: sscanf(s,"%i",&CodecDma);
661:
662: ini_fclose(IwFile);
663:
664: // Make sure numbers OK
665: if (CodecBase==0 || CodecDma==0)
666: return(false);
667:
668: CodecRegisterSelect=CodecBase;
669: CodecData=CodecBase+1;
670: CodecStatus=CodecBase+2;
671: DmaChannel=CodecDma;
672:
673: // Make sure there is a CODEC at the CODEC base
674:
675: // Clear any pending IRQs
676: dos_inportb(CodecStatus);
677: dos_outportb(CodecStatus,0);
678:
679: // Wait for 'INIT' bit to clear
680: for (i=0;i<0xFFFF;i++)
681: if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0)
682: break;
683: if (i==0xFFFF)
684: return(false);
685:
686: // Get chip revision - can not be zero
687: dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
688: if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID)
689: return(false);
690: if ((dos_inportb(CodecData) & 0x0F) == 0)
691: return(false);
692:
1.1.1.2 ! root 693: HaveCodec=1;
! 694: Con_Printf("Sound Card is UltraSound PnP\n");
1.1 root 695: return(true);
696: }
697:
698: //=============================================================================
699: // Get UltraSound MAX configuration if any
700: //=============================================================================
701: static qboolean GUS_GetMAXData(void)
702: {
703: char *Ultrasnd,*Ultra16;
704: int i;
705: int GusBase,Dma1,Dma2,Irq1,Irq2;
706: int CodecBase,CodecDma,CodecIrq,CodecType;
707: BYTE MaxVal;
708:
709: Ultrasnd=getenv("ULTRASND");
710: Ultra16=getenv("ULTRA16");
711: if (Ultrasnd==NULL || Ultra16==NULL)
712: return(false);
713:
714: sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2);
715: sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType);
716:
717: if (CodecType==0 && CodecDma!=0)
718: DmaChannel=CodecDma & 0x07;
719: else
720: DmaChannel=Dma2 & 0x07;
721:
722: // Make sure there is a GUS at GUS base
723: dos_outportb(GusBase+0x08,0x55);
724: if (dos_inportb(GusBase+0x0A)!=0x55)
725: return(false);
726: dos_outportb(GusBase+0x08,0xAA);
727: if (dos_inportb(GusBase+0x0A)!=0xAA)
728: return(false);
729:
730: // Program CODEC control register
731: MaxVal=((CodecBase & 0xF0)>>4) | 0x40;
732: if (Dma1 > 3)
733: MaxVal|=0x10;
734: if (Dma2 > 3)
735: MaxVal|=0x20;
736: dos_outportb(GusBase+0x106,MaxVal);
737:
738: CodecRegisterSelect=CodecBase;
739: CodecData=CodecBase+1;
740: CodecStatus=CodecBase+2;
741:
742: // Make sure there is a CODEC at the CODEC base
743:
744: // Clear any pending IRQs
745: dos_inportb(CodecStatus);
746: dos_outportb(CodecStatus,0);
747:
748: // Wait for 'INIT' bit to clear
749: for (i=0;i<0xFFFF;i++)
750: if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0)
751: break;
752: if (i==0xFFFF)
753: return(false);
754:
755: // Get chip revision - can not be zero
756: dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
757: if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID)
758: return(false);
759: if ((dos_inportb(CodecData) & 0x0F) == 0)
760: return(false);
761:
1.1.1.2 ! root 762: HaveCodec=1;
! 763: Con_Printf("Sound Card is UltraSound MAX\n");
! 764: return(true);
! 765: }
! 766:
! 767: //=============================================================================
! 768: // Get regular UltraSound configuration if any
! 769: //=============================================================================
! 770: static qboolean GUS_GetGUSData(void)
! 771: {
! 772: char *Ultrasnd;
! 773: int GusBase,Dma1,Dma2,Irq1,Irq2,i;
! 774:
! 775: Ultrasnd=getenv("ULTRASND");
! 776: if (Ultrasnd==NULL)
! 777: return(false);
! 778:
! 779: sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2);
! 780:
! 781: DmaChannel=Dma1 & 0x07;
! 782:
! 783: // Make sure there is a GUS at GUS base
! 784: dos_outportb(GusBase+0x08,0x55);
! 785: if (dos_inportb(GusBase+0x0A)!=0x55)
! 786: return(false);
! 787: dos_outportb(GusBase+0x08,0xAA);
! 788: if (dos_inportb(GusBase+0x0A)!=0xAA)
! 789: return(false);
! 790:
! 791: Gf1TimerControl = GusBase+0x008;
! 792: Gf1PageRegister = GusBase+0x102;
! 793: Gf1RegisterSelect = GusBase+0x103;
! 794: Gf1DataLow = GusBase+0x104;
! 795: Gf1DataHigh = GusBase+0x105;
! 796:
! 797: // Reset the GUS
! 798: SetGf18(MASTER_RESET,0x00);
! 799: Gf1Delay();
! 800: Gf1Delay();
! 801: SetGf18(MASTER_RESET,0x01);
! 802: Gf1Delay();
! 803: Gf1Delay();
! 804:
! 805: // Set to max (32) voices
! 806: SetGf18(SET_VOICES,0xDF);
! 807:
! 808: // Clear any pending IRQ's
! 809: ClearGf1Ints();
! 810:
! 811: // Set all registers to known values
! 812: for (i=0;i<32;i++)
! 813: {
! 814: dos_outportb(Gf1PageRegister,i);
! 815: SetGf18(SET_CONTROL,0x03);
! 816: SetGf18(SET_VOLUME_CONTROL,0x03);
! 817: Gf1Delay();
! 818: SetGf18(SET_CONTROL,0x03);
! 819: SetGf18(SET_VOLUME_CONTROL,0x03);
! 820: SetGf116(SET_START_HIGH,0);
! 821: SetGf116(SET_START_LOW,0);
! 822: SetGf116(SET_END_HIGH,0);
! 823: SetGf116(SET_END_LOW,0);
! 824: SetGf116(SET_ACC_HIGH,0);
! 825: SetGf116(SET_ACC_LOW,0);
! 826: SetGf18(SET_VOLUME_RATE,63);
! 827: SetGf18(SET_VOLUME_START,5);
! 828: SetGf18(SET_VOLUME_END,251);
! 829: SetGf116(SET_VOLUME,5<<8);
! 830: }
! 831:
! 832: // Clear any pending IRQ's
! 833: ClearGf1Ints();
! 834:
! 835: // Enable DAC etc.
! 836: SetGf18(MASTER_RESET,0x07);
! 837:
! 838: // Enable line output so we can hear something
! 839: dos_outportb(GusBase,0x08);
! 840:
! 841: HaveCodec=0;
! 842: Con_Printf("Sound Card is UltraSound\n");
1.1 root 843: return(true);
844: }
845:
1.1.1.2 ! root 846:
1.1 root 847: //=============================================================================
848: // Programs the DMA controller to start DMAing in Auto-init mode
849: //=============================================================================
850: static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count)
851: {
852: int mode;
853: int RealAddr;
854:
855: RealAddr = ptr2real(dma_buffer);
856:
857: if (DmaChannel <= 3)
858: {
859: ModeReg = 0x0B;
860: DisableReg = 0x0A;
861: ClearReg = 0x0E;
862: }
863: else
864: {
865: ModeReg = 0xD6;
866: DisableReg = 0xD4;
867: ClearReg = 0xDC;
868: }
869: CountReg=CountRegs[DmaChannel];
870: AddrReg=AddrRegs[DmaChannel];
871:
872: dos_outportb(DisableReg, DmaChannel | 4); // disable channel
873:
874: // set mode- see "undocumented pc", p.876
875: mode = (1<<6) // single-cycle
876: +(0<<5) // address increment
877: +(1<<4) // auto-init dma
878: +(2<<2) // read
879: +(DmaChannel & 0x03); // channel #
880: dos_outportb(ModeReg, mode);
881:
882: // set page
883: dos_outportb(PageRegs[DmaChannel], RealAddr >> 16);
884:
885: if (DmaChannel <= 3)
886: { // address is in bytes
887: dos_outportb(0x0C, 0); // prepare to send 16-bit value
888: dos_outportb(AddrReg, RealAddr & 0xff);
889: dos_outportb(AddrReg, (RealAddr>>8) & 0xff);
890:
891: dos_outportb(0x0C, 0); // prepare to send 16-bit value
892: dos_outportb(CountReg, (count-1) & 0xff);
893: dos_outportb(CountReg, (count-1) >> 8);
894: }
895: else
896: { // address is in words
897: dos_outportb(0xD8, 0); // prepare to send 16-bit value
898: dos_outportb(AddrReg, (RealAddr>>1) & 0xff);
899: dos_outportb(AddrReg, (RealAddr>>9) & 0xff);
900:
901: dos_outportb(0xD8, 0); // prepare to send 16-bit value
902: dos_outportb(CountReg, ((count>>1)-1) & 0xff);
903: dos_outportb(CountReg, ((count>>1)-1) >> 8);
904: }
905:
906: dos_outportb(ClearReg, 0); // clear write mask
907: dos_outportb(DisableReg, DmaChannel & ~4);
908: }
909:
910: //=============================================================================
911: // Starts the CODEC playing
912: //=============================================================================
913: static void GUS_StartCODEC(int count,BYTE FSVal)
914: {
915: int i,j;
916:
917: // Clear any pending IRQs
918: dos_inportb(CodecStatus);
919: dos_outportb(CodecStatus,0);
920:
921: // Set mode to 2
922: dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
923: dos_outportb(CodecData,0xC0);
924:
925: // Stop any playback or capture which may be happening
926: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
927: dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC);
928:
929: // Set FS
930: dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40);
931: dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits
932:
933: // Wait a bit
934: for (i=0;i<10;i++)
935: dos_inportb(CodecData);
936:
937: // Routine 1 to counter CODEC bug - wait for init bit to clear and then a
938: // bit longer (i=min loop count, j=timeout
939: for (i=0,j=0;i<1000 && j<0x7FFFF;j++)
940: if ((dos_inportb(CodecRegisterSelect) & 0x80)==0)
941: i++;
942:
943: // Routine 2 to counter CODEC bug - this is from Forte's code. For me it
944: // does not seem to cure the problem, but is added security
945: // Waits till we can modify index register
946: for (j=0;j<0x7FFFF;j++)
947: {
948: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
949: if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40))
950: break;
951: }
952:
953: // Perform ACAL
954: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
955: dos_outportb(CodecData,0x08);
956:
957: // Clear MCE bit - this makes ACAL happen
958: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
959:
960: // Wait for ACAL to finish
961: for (j=0;j<0x7FFFF;j++)
962: {
963: if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0)
964: continue;
965: dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT);
966: if ((dos_inportb(CodecData) & 0x20) == 0)
967: break;
968: }
969:
970: // Clear ACAL bit
971: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
972: dos_outportb(CodecData,0x00);
973: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
974:
975: // Set some other junk
976: dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL);
977: dos_outportb(CodecData,0x00);
978: dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL);
979: dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control
980:
981: // Set count (it doesn't really matter what value we stuff in here
982: dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT);
983: dos_outportb(CodecData,count & 0xFF);
984: dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT);
985: dos_outportb(CodecData,count >> 8);
986:
987: // Start playback
988: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
989: dos_outportb(CodecData,0x01);
990: }
991:
992: //=============================================================================
1.1.1.2 ! root 993: // Starts the GF1 playing
1.1 root 994: //=============================================================================
1.1.1.2 ! root 995: static void GUS_StartGf1(int count,BYTE Voices)
1.1 root 996: {
1.1.1.2 ! root 997: DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR;
1.1 root 998:
1.1.1.2 ! root 999: // Set number of voices to give us the sampling rate we want
! 1000: SetGf18(SET_VOICES,0xC0 | (Voices-1));
1.1 root 1001:
1.1.1.2 ! root 1002: // Figure out addresses
! 1003: StartAddressL=ConvertTo16(0);
! 1004: EndAddressL=ConvertTo16(count-2-2);
! 1005: StartAddressR=ConvertTo16(2);
! 1006: EndAddressR=ConvertTo16(count-2);
! 1007:
! 1008: // Set left voice addresses
! 1009: dos_outportb(Gf1PageRegister,0);
! 1010: SetGf116(SET_START_LOW,StartAddressL<<9);
! 1011: SetGf116(SET_START_HIGH,StartAddressL>>7);
! 1012: SetGf116(SET_ACC_LOW,StartAddressL<<9);
! 1013: SetGf116(SET_ACC_HIGH,StartAddressL>>7);
! 1014: SetGf116(SET_END_LOW,EndAddressL<<9);
! 1015: SetGf116(SET_END_HIGH,EndAddressL>>7);
! 1016: // Set balance to full left
! 1017: SetGf18(SET_BALANCE,0);
! 1018: // Set volume to full
! 1019: SetGf116(SET_VOLUME,0xFFF0);
! 1020: // Set FC to 2 (so we play every second sample)
! 1021: SetGf116(SET_FREQUENCY,0x0800);
! 1022:
! 1023: // Set right voice addresses
! 1024: dos_outportb(Gf1PageRegister,1);
! 1025: SetGf116(SET_START_LOW,StartAddressR<<9);
! 1026: SetGf116(SET_START_HIGH,StartAddressR>>7);
! 1027: SetGf116(SET_ACC_LOW,StartAddressR<<9);
! 1028: SetGf116(SET_ACC_HIGH,StartAddressR>>7);
! 1029: SetGf116(SET_END_LOW,EndAddressR<<9);
! 1030: SetGf116(SET_END_HIGH,EndAddressR>>7);
! 1031: // Set balance to full right
! 1032: SetGf18(SET_BALANCE,15);
! 1033: // Set volume to full
! 1034: SetGf116(SET_VOLUME,0xFFF0);
! 1035: // Set FC to 2 (so we play every second sample)
! 1036: SetGf116(SET_FREQUENCY,0x0800);
! 1037:
! 1038: // Start voices
! 1039: dos_outportb(Gf1PageRegister,0);
! 1040: SetGf18(SET_CONTROL,0x0C);
! 1041: dos_outportb(Gf1PageRegister,1);
! 1042: SetGf18(SET_CONTROL,0x0C);
! 1043: Gf1Delay();
! 1044: dos_outportb(Gf1PageRegister,0);
! 1045: SetGf18(SET_CONTROL,0x0C);
! 1046: dos_outportb(Gf1PageRegister,1);
! 1047: SetGf18(SET_CONTROL,0x0C);
! 1048: }
1.1 root 1049:
1050:
1.1.1.2 ! root 1051: //=============================================================================
! 1052: // Figures out what kind of UltraSound we have, if any, and starts it playing
! 1053: //=============================================================================
! 1054: qboolean GUS_Init(void)
! 1055: {
! 1056: int rc;
! 1057: int RealAddr;
! 1058: BYTE FSVal,Voices;
! 1059: struct CodecRateStruct *CodecRate;
! 1060: struct Gf1RateStruct *Gf1Rate;
! 1061:
! 1062: // See what kind of UltraSound we have, if any
! 1063: if (GUS_GetIWData()==false)
! 1064: if (GUS_GetMAXData()==false)
! 1065: if (GUS_GetGUSData()==false)
! 1066: return(false);
! 1067:
! 1068: shm = &sn;
! 1069:
! 1070: if (HaveCodec)
! 1071: {
! 1072: // do 11khz sampling rate unless command line parameter wants different
! 1073: shm->speed = 11025;
! 1074: FSVal = 0x03;
! 1075: rc = COM_CheckParm("-sspeed");
! 1076: if (rc)
! 1077: {
! 1078: shm->speed = Q_atoi(com_argv[rc+1]);
! 1079:
! 1080: // Make sure rate not too high
! 1081: if (shm->speed>48000)
! 1082: shm->speed=48000;
! 1083:
! 1084: // Adjust speed to match one of the possible CODEC rates
! 1085: for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++)
! 1086: {
! 1087: if (shm->speed <= CodecRate->Rate)
! 1088: {
! 1089: shm->speed=CodecRate->Rate;
! 1090: FSVal=CodecRate->FSVal;
! 1091: break;
! 1092: }
! 1093: }
! 1094: }
! 1095:
! 1096:
! 1097: // Always do 16 bit stereo
! 1098: shm->channels = 2;
! 1099: shm->samplebits = 16;
! 1100:
! 1101: // allocate buffer twice the size we need so we can get aligned buffer
! 1102: dma_buffer = dos_getmemory(BUFFER_SIZE*2);
! 1103: if (dma_buffer==NULL)
! 1104: {
! 1105: Con_Printf("Couldn't allocate sound dma buffer");
! 1106: return false;
! 1107: }
! 1108:
! 1109: RealAddr = ptr2real(dma_buffer);
! 1110: RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1);
! 1111: dma_buffer = (short *) real2ptr(RealAddr);
! 1112:
! 1113: // Zero off DMA buffer
! 1114: memset(dma_buffer, 0, BUFFER_SIZE);
! 1115:
! 1116: shm->soundalive = true;
! 1117: shm->splitbuffer = false;
! 1118:
! 1119: shm->samplepos = 0;
! 1120: shm->submission_chunk = 1;
! 1121: shm->buffer = (unsigned char *) dma_buffer;
! 1122: shm->samples = BUFFER_SIZE/(shm->samplebits/8);
! 1123:
! 1124: GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
! 1125: GUS_StartCODEC(BUFFER_SIZE,FSVal);
! 1126: }
! 1127: else
! 1128: {
! 1129: // do 19khz sampling rate unless command line parameter wants different
! 1130: shm->speed = 19293;
! 1131: Voices=32;
! 1132: rc = COM_CheckParm("-sspeed");
! 1133: if (rc)
! 1134: {
! 1135: shm->speed = Q_atoi(com_argv[rc+1]);
! 1136:
! 1137: // Make sure rate not too high
! 1138: if (shm->speed>44100)
! 1139: shm->speed=44100;
! 1140:
! 1141: // Adjust speed to match one of the possible GF1 rates
! 1142: for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++)
! 1143: {
! 1144: if (shm->speed <= Gf1Rate->Rate)
! 1145: {
! 1146: shm->speed=Gf1Rate->Rate;
! 1147: Voices=Gf1Rate->Voices;
! 1148: break;
! 1149: }
! 1150: }
! 1151: }
! 1152:
! 1153: // Always do 16 bit stereo
! 1154: shm->channels = 2;
! 1155: shm->samplebits = 16;
! 1156:
! 1157: // allocate buffer twice the size we need so we can get aligned buffer
! 1158: dma_buffer = dos_getmemory(BUFFER_SIZE*2);
! 1159: if (dma_buffer==NULL)
! 1160: {
! 1161: Con_Printf("Couldn't allocate sound dma buffer");
! 1162: return false;
! 1163: }
! 1164:
! 1165: RealAddr = ptr2real(dma_buffer);
! 1166: RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1);
! 1167: dma_buffer = (short *) real2ptr(RealAddr);
! 1168:
! 1169: // Zero off DMA buffer
! 1170: memset(dma_buffer, 0, BUFFER_SIZE);
! 1171:
! 1172: shm->soundalive = true;
! 1173: shm->splitbuffer = false;
! 1174:
! 1175: shm->samplepos = 0;
! 1176: shm->submission_chunk = 1;
! 1177: shm->buffer = (unsigned char *) dma_buffer;
! 1178: shm->samples = BUFFER_SIZE/(shm->samplebits/8);
! 1179:
! 1180: GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
! 1181: SetGf116(SET_DMA_ADDRESS,0x0000);
! 1182: if (DmaChannel<=3)
! 1183: SetGf18(DMA_CONTROL,0x41);
! 1184: else
! 1185: SetGf18(DMA_CONTROL,0x45);
! 1186: GUS_StartGf1(BUFFER_SIZE,Voices);
! 1187: }
! 1188: return(true);
1.1 root 1189: }
1190:
1191: //=============================================================================
1192: // Returns the current playback position
1193: //=============================================================================
1194: int GUS_GetDMAPos(void)
1195: {
1196: int count;
1197:
1.1.1.2 ! root 1198: if (HaveCodec)
! 1199: {
! 1200: // clear 16-bit reg flip-flop
! 1201: // load the current dma count register
! 1202: if (DmaChannel < 4)
! 1203: {
! 1204: dos_outportb(0x0C, 0);
! 1205: count = dos_inportb(CountReg);
! 1206: count += dos_inportb(CountReg) << 8;
! 1207: if (shm->samplebits == 16)
! 1208: count /= 2;
! 1209: count = shm->samples - (count+1);
! 1210: }
! 1211: else
! 1212: {
! 1213: dos_outportb(0xD8, 0);
! 1214: count = dos_inportb(CountReg);
! 1215: count += dos_inportb(CountReg) << 8;
! 1216: if (shm->samplebits == 8)
! 1217: count *= 2;
! 1218: count = shm->samples - (count+1);
! 1219: }
! 1220:
! 1221: }
! 1222: else
! 1223: {
! 1224: // Read current position from GF1
! 1225: dos_outportb(Gf1PageRegister,0);
! 1226: count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF;
! 1227: // See which half of buffer we are in. Note that since this is 16 bit
! 1228: // data we are playing, position is in 16 bit samples
! 1229: if (GetGf18(DMA_CONTROL) & 0x40)
! 1230: {
! 1231: GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
! 1232: SetGf116(SET_DMA_ADDRESS,0x0000);
! 1233: if (DmaChannel<=3)
! 1234: SetGf18(DMA_CONTROL,0x41);
! 1235: else
! 1236: SetGf18(DMA_CONTROL,0x45);
! 1237: }
! 1238: }
1.1 root 1239:
1240: shm->samplepos = count & (shm->samples-1);
1241: return(shm->samplepos);
1242: }
1243:
1244: //=============================================================================
1245: // Stops the UltraSound playback
1246: //=============================================================================
1247: void GUS_Shutdown (void)
1248: {
1.1.1.2 ! root 1249: if (HaveCodec)
! 1250: {
! 1251: // Stop CODEC
! 1252: dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
! 1253: dos_outportb(CodecData,0x01);
! 1254: }
! 1255: else
! 1256: {
! 1257: // Stop Voices
! 1258: dos_outportb(Gf1PageRegister,0);
! 1259: SetGf18(SET_CONTROL,0x03);
! 1260: dos_outportb(Gf1PageRegister,1);
! 1261: SetGf18(SET_CONTROL,0x03);
! 1262: Gf1Delay();
! 1263: dos_outportb(Gf1PageRegister,0);
! 1264: SetGf18(SET_CONTROL,0x03);
! 1265: dos_outportb(Gf1PageRegister,1);
! 1266: SetGf18(SET_CONTROL,0x03);
! 1267:
! 1268: // Stop any DMA
! 1269: SetGf18(DMA_CONTROL,0x00);
! 1270: GetGf18(DMA_CONTROL);
! 1271: }
1.1 root 1272:
1.1.1.2 ! root 1273: dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel
1.1 root 1274: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.