Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5131 clevermous 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
// 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
 
34
FILE *ini_fopen(const char *filename, const char *modes);
35
int ini_fclose(FILE *f);
36
void 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
 
49
struct section_buffer
50
{
51
   long offset;
52
   char name[MAX_SECTION_WIDTH+1];
53
};
54
 
55
struct field_buffer
56
{
57
   long offset;
58
   int  section;
59
   char name[MAX_FIELD_WIDTH+1];
60
};
61
 
62
static FILE *current_file=NULL;
63
static int   current_section;
64
 
65
static int current_section_buffer=0;
66
static int current_field_buffer=0;
67
 
68
static struct section_buffer section_buffers[NUM_SECTION_BUFFERS];
69
static struct field_buffer field_buffers[NUM_FIELD_BUFFERS];
70
//***************************************************************************
71
// Internal routines
72
//***************************************************************************
73
static char toupper(char c)
74
{
75
   if (c>='a' && c<='z')
76
      c-=('a'-'A');
77
   return(c);
78
}
79
 
80
static void reset_buffer(FILE *f)
81
{
82
   int i;
83
 
84
   for (i=0;i
85
      section_buffers[i].name[0]=0;
86
   for (i=0;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.
95
static 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.
135
static 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"
172
static 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
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"
212
static 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
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"
244
static 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
269
static 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
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
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
295
static 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
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.
319
static 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
337
FILE *ini_fopen(const char *filename, const char *modes)
338
{
339
   return(fopen(filename,modes));
340
}
341
 
342
// Closes a .INI file. Works like fclose
343
int 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="";
353
void 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
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
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
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
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
 
516
struct CodecRateStruct
517
{
518
   WORD Rate;
519
   BYTE FSVal;
520
};
521
 
522
struct Gf1RateStruct
523
{
524
   WORD Rate;
525
   BYTE Voices;
526
};
527
 
528
//=============================================================================
529
// Reference variables in SND_DOS.C
530
//=============================================================================
531
extern short *dma_buffer;
532
 
533
//=============================================================================
534
// GUS-only variables
535
//=============================================================================
536
static BYTE HaveCodec=0;
537
 
538
static WORD CodecRegisterSelect;
539
static WORD CodecData;
540
static WORD CodecStatus;
541
static WORD Gf1TimerControl;
542
static WORD Gf1PageRegister;
543
static WORD Gf1RegisterSelect;
544
static WORD Gf1DataLow;
545
static WORD Gf1DataHigh;
546
 
547
static BYTE DmaChannel;
548
 
549
static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
550
static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
551
static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
552
 
553
static WORD AddrReg;
554
static WORD CountReg;
555
static WORD ModeReg;
556
static WORD DisableReg;
557
static WORD ClearReg;
558
 
559
static 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
 
578
static 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
//=============================================================================
605
void SetGf18(BYTE reg,BYTE data)
606
{
607
   dos_outportb(Gf1RegisterSelect,reg);
608
   dos_outportb(Gf1DataHigh,data);
609
}
610
 
611
void SetGf116(BYTE reg,WORD data)
612
{
613
   dos_outportb(Gf1RegisterSelect,reg);
614
   dos_outportw(Gf1DataLow,data);
615
}
616
 
617
BYTE GetGf18(BYTE reg)
618
{
619
   dos_outportb(Gf1RegisterSelect,reg);
620
   return(dos_inportb(Gf1DataHigh));
621
}
622
 
623
WORD GetGf116(BYTE reg)
624
{
625
   dos_outportb(Gf1RegisterSelect,reg);
626
   return(dos_inportw(Gf1DataLow));
627
}
628
 
629
void Gf1Delay(void)
630
{
631
   int i;
632
 
633
   for (i=0;i<27;i++)
634
      dos_inportb(Gf1TimerControl);
635
}
636
 
637
DWORD ConvertTo16(DWORD Address)
638
{
639
   return( ((Address>>1) & 0x0001FFFF) | (Address & 0x000C0000L) );
640
}
641
 
642
void 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
//=============================================================================
660
static 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
//=============================================================================
720
static 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
//=============================================================================
789
static 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
//=============================================================================
869
static 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
//=============================================================================
932
static 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
//=============================================================================
1014
static 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
//=============================================================================
1073
qboolean 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
//=============================================================================
1213
int 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
//=============================================================================
1266
void 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
}