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