Home | History | Annotate | Download | only in WinQuake
      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<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.
     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<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"
    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<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"
    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<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
    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<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.
    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<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 
    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 }
   1294