1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 |
- /*
- Copyright (C) 1996-1997 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- //=============================================================================
- // Routines for GUS support in QUAKE
- //
- // Author(s): Jayeson Lee-Steere
- //=============================================================================
- #include "quakedef.h"
- #include "dosisms.h"
- //=============================================================================
- // Author(s): Jayeson Lee-Steere
- #define INI_STRING_SIZE 0x100
- FILE *ini_fopen(const char *filename, const char *modes);
- int ini_fclose(FILE *f);
- void ini_fgets(FILE *f, const char *section, const char *field, char *s);
- // Routines for reading from .INI files
- // The read routines are fairly efficient.
- //
- // Author(s): Jayeson Lee-Steere
- #define MAX_SECTION_WIDTH 20
- #define MAX_FIELD_WIDTH 20
- #define NUM_SECTION_BUFFERS 10
- #define NUM_FIELD_BUFFERS 20
- struct section_buffer
- {
- long offset;
- char name[MAX_SECTION_WIDTH+1];
- };
- struct field_buffer
- {
- long offset;
- int section;
- char name[MAX_FIELD_WIDTH+1];
- };
- static FILE *current_file=NULL;
- static int current_section;
- static int current_section_buffer=0;
- static int current_field_buffer=0;
- static struct section_buffer section_buffers[NUM_SECTION_BUFFERS];
- static struct field_buffer field_buffers[NUM_FIELD_BUFFERS];
- //***************************************************************************
- // Internal routines
- //***************************************************************************
- static char toupper(char c)
- {
- if (c>='a' && c<='z')
- c-=('a'-'A');
- return(c);
- }
- static void reset_buffer(FILE *f)
- {
- int i;
- for (i=0;i<NUM_SECTION_BUFFERS;i++)
- section_buffers[i].name[0]=0;
- for (i=0;i<NUM_FIELD_BUFFERS;i++)
- field_buffers[i].name[0]=0;
- current_file=f;
- }
- // Sees if the current string is section "name" (i.e. ["name"]).
- // If "name"=="*", sees if the current string is any section
- // (i.e. [....]). Returns 1 if true else 0 if false.
- static int is_section(char *s,const char *name)
- {
- int wild=0;
- // See if wild search
- if (strcmp("*",name)==0)
- wild=1;
- // Skip leading spaces
- while (s[0]==' ')
- s++;
- // Look for leading "["
- if (s[0]!='[')
- return(0);
- s++;
- // Make sure name matches
- while (s[0]!=']' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0)
- {
- if (!wild)
- if (toupper(s[0])!=toupper(name[0]))
- return(0);
- s++;
- if (!wild)
- name++;
- }
- if (!wild)
- if (name[0]!=0)
- return(0);
- // Skip trailing spaces
- while (s[0]==' ')
- s++;
- // Make sure we have trailing "]"
- if (s[0]!=']')
- return(0);
- return(1);
- }
- // Sees if the current string is field "name" (i.e. "name"=...).
- // If "name"=="*", sees if the current string is any field
- // (i.e. ...=...). Returns 1 if true else 0 if false.
- static int is_field(char *s,const char *name)
- {
- int wild=0;
- // See if wild search
- if (strcmp("*",name)==0)
- wild=1;
- // Skip leading spaces
- while (s[0]==' ')
- s++;
- // Make sure name matches
- while (s[0]!='=' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0)
- {
- if (!wild)
- if (toupper(s[0])!=toupper(name[0]))
- return(0);
- s++;
- if (!wild)
- name++;
- }
- if (!wild)
- if (name[0]!=0)
- return(0);
- // Skip trailing spaces
- while (s[0]==' ')
- s++;
- // Make sure we have an "="
- if (s[0]!='=')
- return(0);
- return(1);
- }
- // Extracts the section name from a section heading
- // e.g. in="[hey man]" gives out="hey man"
- static void get_section_name(char *out, char *in)
- {
- int i=0;
- // Skip spaces before '['
- while (in[0]==' ')
- in++;
- // Make sure there is a '['
- if (in[0]!='[')
- {
- out[0]=0;
- return;
- }
- // Skip past '['
- in++;
- // Copy string if any to output string.
- while (in[0]!=']' && in[0]!=13 && in[0]!=10 && in[0]!=0)
- {
- if (i<MAX_SECTION_WIDTH)
- {
- out[i]=in[0];
- i++;
- }
- in++;
- }
- // Make sure string was terminated with ']'
- if (in[0]!=']')
- {
- out[0]=0;
- return;
- }
- // Remove trailing spaces
- while (i>0 && out[i-1]==' ')
- i--;
- // Null terminate the output string.
- out[i]=0;
- }
- // Extracts the field name from a field line
- // e.g. in="sooty=life be in it" gives out="sooty"
- static void get_field_name(char *out, char *in)
- {
- int i=0;
- // Skip leading spaces
- while (in[0]==' ')
- in++;
- // Copy name to output string
- while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0)
- {
- if (i<MAX_FIELD_WIDTH)
- {
- out[i]=in[0];
- i++;
- }
- in++;
- }
- // Make sure we stopped on "="
- if (in[0]!='=')
- {
- out[0]=0;
- return;
- }
- // Remove trailing spaces
- while (i>0 && out[i-1]==' ')
- i--;
- // Null terminate the output string.
- out[i]=0;
- }
- // Returns the field data from string s.
- // e.g. in="wally = golly man" gives out="golly man"
- static void get_field_string(char *out, char *in)
- {
- int i=0;
- // Find '=' if it exists
- while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0)
- in++;
- // If there is an '=', skip past it.
- if (in[0]=='=')
- in++;
- // Skip any spaces between the '=' and string.
- while (in[0]==' ' || in[0]=='[')
- in++;
- // Copy string, if there is one, to the output string.
- while (in[0]!=13 && in[0]!=10 && in[0]!=0 && i<(INI_STRING_SIZE-1))
- {
- out[i]=in[0];
- in++;
- i++;
- }
- // Null terminate the output string.
- out[i]=0;
- }
- // Adds a section to the buffer
- static int add_section(char *instring, long offset)
- {
- int i;
- char section[MAX_SECTION_WIDTH+1];
- // Extract section name
- get_section_name(section,instring);
- // See if section already exists.
- for (i=0;i<NUM_SECTION_BUFFERS;i++)
- if (stricmp(section,section_buffers[i].name)==0)
- return(i);
- // Increment current_section_buffer
- current_section_buffer++;
- if (current_section_buffer>NUM_SECTION_BUFFERS)
- current_section_buffer=0;
- // Delete any field buffers that correspond to this section
- for (i=0;i<NUM_FIELD_BUFFERS;i++)
- if (field_buffers[i].section==current_section_buffer)
- field_buffers[i].name[0]=0;
- // Set buffer information
- strcpy(section_buffers[current_section_buffer].name,section);
- section_buffers[current_section_buffer].offset=offset;
- return(current_section_buffer);
- }
- // Adds a field to the buffer
- static void add_field(char *instring, int section, long offset)
- {
- int i;
- char field[MAX_FIELD_WIDTH+1];
- // Extract field name
- get_field_name(field,instring);
- // See if field already exists
- for (i=0;i<NUM_FIELD_BUFFERS;i++)
- if (field_buffers[i].section==section)
- if (stricmp(field_buffers[i].name,field)==0)
- return;
- // Increment current_field_buffer
- current_field_buffer++;
- if (current_field_buffer>NUM_FIELD_BUFFERS)
- current_field_buffer=0;
- // Set buffer information
- strcpy(field_buffers[current_field_buffer].name,field);
- field_buffers[current_field_buffer].section=section;
- field_buffers[current_field_buffer].offset=offset;
- }
- // Identical to fgets except the string is trucated at the first ';',
- // carriage return or line feed.
- static char *stripped_fgets(char *s, int n, FILE *f)
- {
- int i=0;
- if (fgets(s,n,f)==NULL)
- return(NULL);
- while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0)
- i++;
- s[i]=0;
- return(s);
- }
- //***************************************************************************
- // Externally accessable routines
- //***************************************************************************
- // Opens an .INI file. Works like fopen
- FILE *ini_fopen(const char *filename, const char *modes)
- {
- return(fopen(filename,modes));
- }
- // Closes a .INI file. Works like fclose
- int ini_fclose(FILE *f)
- {
- if (f==current_file)
- reset_buffer(NULL);
- return(fclose(f));
- }
- // Puts "field" from "section" from .ini file "f" into "s".
- // If "section" does not exist or "field" does not exist in
- // section then s="";
- void ini_fgets(FILE *f, const char *section, const char *field, char *s)
- {
- int i;
- long start_pos,string_start_pos;
- char ts[INI_STRING_SIZE*2];
- if (f!=current_file)
- reset_buffer(f);
- // Default to "Not found"
- s[0]=0;
- // See if section is in buffer
- for (i=0;i<NUM_SECTION_BUFFERS;i++)
- if (strnicmp(section_buffers[i].name,section,MAX_SECTION_WIDTH)==0)
- break;
- // If section is in buffer, seek to it if necessary
- if (i<NUM_SECTION_BUFFERS)
- {
- if (i!=current_section)
- {
- current_section=i;
- fseek(f,section_buffers[i].offset,SEEK_SET);
- }
- }
- // else look through .ini file for it.
- else
- {
- // Make sure we are not at eof or this will cause trouble.
- if (feof(f))
- rewind(f);
- start_pos=ftell(f);
- while (1)
- {
- stripped_fgets(ts,INI_STRING_SIZE*2,f);
- // If it is a section, add it to the section buffer
- if (is_section(ts,"*"))
- current_section=add_section(ts,ftell(f));
- // If it is the section we are looking for, break.
- if (is_section(ts,section))
- break;
- // If we reach the end of the file, rewind to the start.
- if (feof(f))
- rewind(f);
- if (ftell(f)==start_pos)
- return;
- }
- }
- // See if field is in buffer
- for (i=0;i<NUM_FIELD_BUFFERS;i++)
- if (field_buffers[i].section==current_section)
- if (strnicmp(field_buffers[i].name,field,MAX_FIELD_WIDTH)==0)
- break;
- // If field is in buffer, seek to it and read it
- if (i<NUM_FIELD_BUFFERS)
- {
- fseek(f,field_buffers[i].offset,SEEK_SET);
- stripped_fgets(ts,INI_STRING_SIZE*2,f);
- get_field_string(s,ts);
- }
- else
- // else search through section for field.
- {
- // Make sure we do not start at eof or this will cause problems.
- if (feof(f))
- fseek(f,section_buffers[current_section].offset,SEEK_SET);
- start_pos=ftell(f);
- while (1)
- {
- string_start_pos=ftell(f);
- stripped_fgets(ts,INI_STRING_SIZE*2,f);
- // If it is a field, add it to the buffer
- if (is_field(ts,"*"))
- add_field(ts,current_section,string_start_pos);
- // If it is the field we are looking for, save it
- if (is_field(ts,field))
- {
- get_field_string(s,ts);
- break;
- }
- // If we reach the end of the section, start over
- if (feof(f) || is_section(ts,"*"))
- fseek(f,section_buffers[current_section].offset,SEEK_SET);
- if (ftell(f)==start_pos)
- return;
- }
- }
- }
- //=============================================================================
- #define BYTE unsigned char
- #define WORD unsigned short
- #define DWORD unsigned long
- #define BUFFER_SIZE 4096
- #define CODEC_ADC_INPUT_CONTROL_LEFT 0x00
- #define CODEC_ADC_INPUT_CONTROL_RIGHT 0x01
- #define CODEC_AUX1_INPUT_CONTROL_LEFT 0x02
- #define CODEC_AUX1_INPUT_CONTROL_RIGHT 0x03
- #define CODEC_AUX2_INPUT_CONTROL_LEFT 0x04
- #define CODEC_AUX2_INPUT_CONTROL_RIGHT 0x05
- #define CODEC_DAC_OUTPUT_CONTROL_LEFT 0x06
- #define CODEC_DAC_OUTPUT_CONTROL_RIGHT 0x07
- #define CODEC_FS_FORMAT 0x08
- #define CODEC_INTERFACE_CONFIG 0x09
- #define CODEC_PIN_CONTROL 0x0A
- #define CODEC_ERROR_STATUS_AND_INIT 0x0B
- #define CODEC_MODE_AND_ID 0x0C
- #define CODEC_LOOPBACK_CONTROL 0x0D
- #define CODEC_PLAYBACK_UPPER_BASE_COUNT 0x0E
- #define CODEC_PLAYBACK_LOWER_BASE_COUNT 0x0F
- #define SET_CONTROL 0x00
- #define SET_FREQUENCY 0x01
- #define SET_START_HIGH 0x02
- #define SET_START_LOW 0x03
- #define SET_END_HIGH 0x04
- #define SET_END_LOW 0x05
- #define SET_VOLUME_RATE 0x06
- #define SET_VOLUME_START 0x07
- #define SET_VOLUME_END 0x08
- #define SET_CURR_VOLUME 0x09
- #define SET_VOLUME 0x09
- #define SET_ACC_HIGH 0x0A
- #define SET_ACC_LOW 0x0B
- #define SET_BALANCE 0x0C
- #define SET_VOLUME_CONTROL 0x0D
- #define SET_VOICES 0x0E
- #define DMA_CONTROL 0x41
- #define SET_DMA_ADDRESS 0x42
- #define SET_DRAM_LOW 0x43
- #define SET_DRAM_HIGH 0x44
- #define ADLIB_CONTROL 0x45
- #define ADLIB_TIMER1 0x46
- #define ADLIB_TIMER2 0x47
- #define SET_RECORD_RATE 0x48
- #define RECORD_CONTROL 0x49
- #define SET_JOYSTICK 0x4B
- #define MASTER_RESET 0x4C
- #define GET_CONTROL 0x80
- #define GET_FREQUENCY 0x81
- #define GET_START_HIGH 0x82
- #define GET_START_LOW 0x83
- #define GET_END_HIGH 0x84
- #define GET_END_LOW 0x85
- #define GET_VOLUME_RATE 0x86
- #define GET_VOLUME_START 0x87
- #define GET_VOLUME_END 0x88
- #define GET_VOLUME 0x89
- #define GET_ACC_HIGH 0x8A
- #define GET_ACC_LOW 0x8B
- #define GET_BALANCE 0x8C
- #define GET_VOLUME_CONTROL 0x8D
- #define GET_VOICES 0x8E
- #define GET_IRQV 0x8F
- struct CodecRateStruct
- {
- WORD Rate;
- BYTE FSVal;
- };
- struct Gf1RateStruct
- {
- WORD Rate;
- BYTE Voices;
- };
- //=============================================================================
- // Reference variables in SND_DOS.C
- //=============================================================================
- extern short *dma_buffer;
- //=============================================================================
- // GUS-only variables
- //=============================================================================
- static BYTE HaveCodec=0;
- static WORD CodecRegisterSelect;
- static WORD CodecData;
- static WORD CodecStatus;
- static WORD Gf1TimerControl;
- static WORD Gf1PageRegister;
- static WORD Gf1RegisterSelect;
- static WORD Gf1DataLow;
- static WORD Gf1DataHigh;
- static BYTE DmaChannel;
- static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
- static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
- static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
- static WORD AddrReg;
- static WORD CountReg;
- static WORD ModeReg;
- static WORD DisableReg;
- static WORD ClearReg;
- static struct CodecRateStruct CodecRates[]=
- {
- { 5512,0x01},
- { 6620,0x0F},
- { 8000,0x00},
- { 9600,0x0E},
- {11025,0x03},
- {16000,0x02},
- {18900,0x05},
- {22050,0x07},
- {27420,0x04},
- {32000,0x06},
- {33075,0x0D},
- {37800,0x09},
- {44100,0x0B},
- {48000,0x0C},
- { 0,0x00} // End marker
- };
- static struct Gf1RateStruct Gf1Rates[]=
- {
- {19293,32},
- {19916,31},
- {20580,30},
- {21289,29},
- {22050,28},
- {22866,27},
- {23746,26},
- {24696,25},
- {25725,24},
- {26843,23},
- {28063,22},
- {29400,21},
- {30870,20},
- {32494,19},
- {34300,18},
- {36317,17},
- {38587,16},
- {41160,15},
- {44100,14},
- {0,0}
- };
- //=============================================================================
- // Basic GF1 functions
- //=============================================================================
- void SetGf18(BYTE reg,BYTE data)
- {
- dos_outportb(Gf1RegisterSelect,reg);
- dos_outportb(Gf1DataHigh,data);
- }
- void SetGf116(BYTE reg,WORD data)
- {
- dos_outportb(Gf1RegisterSelect,reg);
- dos_outportw(Gf1DataLow,data);
- }
- BYTE GetGf18(BYTE reg)
- {
- dos_outportb(Gf1RegisterSelect,reg);
- return(dos_inportb(Gf1DataHigh));
- }
- WORD GetGf116(BYTE reg)
- {
- dos_outportb(Gf1RegisterSelect,reg);
- return(dos_inportw(Gf1DataLow));
- }
- void Gf1Delay(void)
- {
- int i;
- for (i=0;i<27;i++)
- dos_inportb(Gf1TimerControl);
- }
- DWORD ConvertTo16(DWORD Address)
- {
- return( ((Address>>1) & 0x0001FFFF) | (Address & 0x000C0000L) );
- }
- void ClearGf1Ints(void)
- {
- int i;
- SetGf18(DMA_CONTROL,0x00);
- SetGf18(ADLIB_CONTROL,0x00);
- SetGf18(RECORD_CONTROL,0x00);
-
- GetGf18(DMA_CONTROL);
- GetGf18(RECORD_CONTROL);
- for (i=0;i<32;i++);
- GetGf18(GET_IRQV);
- }
- //=============================================================================
- // Get Interwave (UltraSound PnP) configuration if any
- //=============================================================================
- static qboolean GUS_GetIWData(void)
- {
- char *Interwave,s[INI_STRING_SIZE];
- FILE *IwFile;
- int CodecBase,CodecDma,i;
- Interwave=getenv("INTERWAVE");
- if (Interwave==NULL)
- return(false);
- // Open IW.INI
- IwFile=ini_fopen(Interwave,"rt");
- if (IwFile==NULL)
- return(false);
- // Read codec base and codec DMA
- ini_fgets(IwFile,"setup 0","CodecBase",s);
- sscanf(s,"%X",&CodecBase);
- ini_fgets(IwFile,"setup 0","DMA2",s);
- sscanf(s,"%i",&CodecDma);
- ini_fclose(IwFile);
- // Make sure numbers OK
- if (CodecBase==0 || CodecDma==0)
- return(false);
- CodecRegisterSelect=CodecBase;
- CodecData=CodecBase+1;
- CodecStatus=CodecBase+2;
- DmaChannel=CodecDma;
- // Make sure there is a CODEC at the CODEC base
- // Clear any pending IRQs
- dos_inportb(CodecStatus);
- dos_outportb(CodecStatus,0);
- // Wait for 'INIT' bit to clear
- for (i=0;i<0xFFFF;i++)
- if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0)
- break;
- if (i==0xFFFF)
- return(false);
- // Get chip revision - can not be zero
- dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
- if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID)
- return(false);
- if ((dos_inportb(CodecData) & 0x0F) == 0)
- return(false);
- HaveCodec=1;
- Con_Printf("Sound Card is UltraSound PnP\n");
- return(true);
- }
- //=============================================================================
- // Get UltraSound MAX configuration if any
- //=============================================================================
- static qboolean GUS_GetMAXData(void)
- {
- char *Ultrasnd,*Ultra16;
- int i;
- int GusBase,Dma1,Dma2,Irq1,Irq2;
- int CodecBase,CodecDma,CodecIrq,CodecType;
- BYTE MaxVal;
- Ultrasnd=getenv("ULTRASND");
- Ultra16=getenv("ULTRA16");
- if (Ultrasnd==NULL || Ultra16==NULL)
- return(false);
- sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2);
- sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType);
- if (CodecType==0 && CodecDma!=0)
- DmaChannel=CodecDma & 0x07;
- else
- DmaChannel=Dma2 & 0x07;
- // Make sure there is a GUS at GUS base
- dos_outportb(GusBase+0x08,0x55);
- if (dos_inportb(GusBase+0x0A)!=0x55)
- return(false);
- dos_outportb(GusBase+0x08,0xAA);
- if (dos_inportb(GusBase+0x0A)!=0xAA)
- return(false);
- // Program CODEC control register
- MaxVal=((CodecBase & 0xF0)>>4) | 0x40;
- if (Dma1 > 3)
- MaxVal|=0x10;
- if (Dma2 > 3)
- MaxVal|=0x20;
- dos_outportb(GusBase+0x106,MaxVal);
- CodecRegisterSelect=CodecBase;
- CodecData=CodecBase+1;
- CodecStatus=CodecBase+2;
- // Make sure there is a CODEC at the CODEC base
- // Clear any pending IRQs
- dos_inportb(CodecStatus);
- dos_outportb(CodecStatus,0);
- // Wait for 'INIT' bit to clear
- for (i=0;i<0xFFFF;i++)
- if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0)
- break;
- if (i==0xFFFF)
- return(false);
- // Get chip revision - can not be zero
- dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
- if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID)
- return(false);
- if ((dos_inportb(CodecData) & 0x0F) == 0)
- return(false);
- HaveCodec=1;
- Con_Printf("Sound Card is UltraSound MAX\n");
- return(true);
- }
- //=============================================================================
- // Get regular UltraSound configuration if any
- //=============================================================================
- static qboolean GUS_GetGUSData(void)
- {
- char *Ultrasnd;
- int GusBase,Dma1,Dma2,Irq1,Irq2,i;
- Ultrasnd=getenv("ULTRASND");
- if (Ultrasnd==NULL)
- return(false);
- sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2);
- DmaChannel=Dma1 & 0x07;
- // Make sure there is a GUS at GUS base
- dos_outportb(GusBase+0x08,0x55);
- if (dos_inportb(GusBase+0x0A)!=0x55)
- return(false);
- dos_outportb(GusBase+0x08,0xAA);
- if (dos_inportb(GusBase+0x0A)!=0xAA)
- return(false);
- Gf1TimerControl = GusBase+0x008;
- Gf1PageRegister = GusBase+0x102;
- Gf1RegisterSelect = GusBase+0x103;
- Gf1DataLow = GusBase+0x104;
- Gf1DataHigh = GusBase+0x105;
- // Reset the GUS
- SetGf18(MASTER_RESET,0x00);
- Gf1Delay();
- Gf1Delay();
- SetGf18(MASTER_RESET,0x01);
- Gf1Delay();
- Gf1Delay();
- // Set to max (32) voices
- SetGf18(SET_VOICES,0xDF);
- // Clear any pending IRQ's
- ClearGf1Ints();
- // Set all registers to known values
- for (i=0;i<32;i++)
- {
- dos_outportb(Gf1PageRegister,i);
- SetGf18(SET_CONTROL,0x03);
- SetGf18(SET_VOLUME_CONTROL,0x03);
- Gf1Delay();
- SetGf18(SET_CONTROL,0x03);
- SetGf18(SET_VOLUME_CONTROL,0x03);
- SetGf116(SET_START_HIGH,0);
- SetGf116(SET_START_LOW,0);
- SetGf116(SET_END_HIGH,0);
- SetGf116(SET_END_LOW,0);
- SetGf116(SET_ACC_HIGH,0);
- SetGf116(SET_ACC_LOW,0);
- SetGf18(SET_VOLUME_RATE,63);
- SetGf18(SET_VOLUME_START,5);
- SetGf18(SET_VOLUME_END,251);
- SetGf116(SET_VOLUME,5<<8);
- }
- // Clear any pending IRQ's
- ClearGf1Ints();
- // Enable DAC etc.
- SetGf18(MASTER_RESET,0x07);
- // Enable line output so we can hear something
- dos_outportb(GusBase,0x08);
- HaveCodec=0;
- Con_Printf("Sound Card is UltraSound\n");
- return(true);
- }
- //=============================================================================
- // Programs the DMA controller to start DMAing in Auto-init mode
- //=============================================================================
- static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count)
- {
- int mode;
- int RealAddr;
- RealAddr = ptr2real(dma_buffer);
- if (DmaChannel <= 3)
- {
- ModeReg = 0x0B;
- DisableReg = 0x0A;
- ClearReg = 0x0E;
- }
- else
- {
- ModeReg = 0xD6;
- DisableReg = 0xD4;
- ClearReg = 0xDC;
- }
- CountReg=CountRegs[DmaChannel];
- AddrReg=AddrRegs[DmaChannel];
- dos_outportb(DisableReg, DmaChannel | 4); // disable channel
- // set mode- see "undocumented pc", p.876
- mode = (1<<6) // single-cycle
- +(0<<5) // address increment
- +(1<<4) // auto-init dma
- +(2<<2) // read
- +(DmaChannel & 0x03); // channel #
- dos_outportb(ModeReg, mode);
- // set page
- dos_outportb(PageRegs[DmaChannel], RealAddr >> 16);
- if (DmaChannel <= 3)
- { // address is in bytes
- dos_outportb(0x0C, 0); // prepare to send 16-bit value
- dos_outportb(AddrReg, RealAddr & 0xff);
- dos_outportb(AddrReg, (RealAddr>>8) & 0xff);
- dos_outportb(0x0C, 0); // prepare to send 16-bit value
- dos_outportb(CountReg, (count-1) & 0xff);
- dos_outportb(CountReg, (count-1) >> 8);
- }
- else
- { // address is in words
- dos_outportb(0xD8, 0); // prepare to send 16-bit value
- dos_outportb(AddrReg, (RealAddr>>1) & 0xff);
- dos_outportb(AddrReg, (RealAddr>>9) & 0xff);
- dos_outportb(0xD8, 0); // prepare to send 16-bit value
- dos_outportb(CountReg, ((count>>1)-1) & 0xff);
- dos_outportb(CountReg, ((count>>1)-1) >> 8);
- }
- dos_outportb(ClearReg, 0); // clear write mask
- dos_outportb(DisableReg, DmaChannel & ~4);
- }
- //=============================================================================
- // Starts the CODEC playing
- //=============================================================================
- static void GUS_StartCODEC(int count,BYTE FSVal)
- {
- int i,j;
- // Clear any pending IRQs
- dos_inportb(CodecStatus);
- dos_outportb(CodecStatus,0);
- // Set mode to 2
- dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID);
- dos_outportb(CodecData,0xC0);
- // Stop any playback or capture which may be happening
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
- dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC);
- // Set FS
- dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40);
- dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits
- // Wait a bit
- for (i=0;i<10;i++)
- dos_inportb(CodecData);
- // Routine 1 to counter CODEC bug - wait for init bit to clear and then a
- // bit longer (i=min loop count, j=timeout
- for (i=0,j=0;i<1000 && j<0x7FFFF;j++)
- if ((dos_inportb(CodecRegisterSelect) & 0x80)==0)
- i++;
- // Routine 2 to counter CODEC bug - this is from Forte's code. For me it
- // does not seem to cure the problem, but is added security
- // Waits till we can modify index register
- for (j=0;j<0x7FFFF;j++)
- {
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
- if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40))
- break;
- }
- // Perform ACAL
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
- dos_outportb(CodecData,0x08);
- // Clear MCE bit - this makes ACAL happen
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
- // Wait for ACAL to finish
- for (j=0;j<0x7FFFF;j++)
- {
- if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0)
- continue;
- dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT);
- if ((dos_inportb(CodecData) & 0x20) == 0)
- break;
- }
- // Clear ACAL bit
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40);
- dos_outportb(CodecData,0x00);
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
- // Set some other junk
- dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL);
- dos_outportb(CodecData,0x00);
- dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL);
- dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control
- // Set count (it doesn't really matter what value we stuff in here
- dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT);
- dos_outportb(CodecData,count & 0xFF);
- dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT);
- dos_outportb(CodecData,count >> 8);
- // Start playback
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
- dos_outportb(CodecData,0x01);
- }
- //=============================================================================
- // Starts the GF1 playing
- //=============================================================================
- static void GUS_StartGf1(int count,BYTE Voices)
- {
- DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR;
- // Set number of voices to give us the sampling rate we want
- SetGf18(SET_VOICES,0xC0 | (Voices-1));
- // Figure out addresses
- StartAddressL=ConvertTo16(0);
- EndAddressL=ConvertTo16(count-2-2);
- StartAddressR=ConvertTo16(2);
- EndAddressR=ConvertTo16(count-2);
- // Set left voice addresses
- dos_outportb(Gf1PageRegister,0);
- SetGf116(SET_START_LOW,StartAddressL<<9);
- SetGf116(SET_START_HIGH,StartAddressL>>7);
- SetGf116(SET_ACC_LOW,StartAddressL<<9);
- SetGf116(SET_ACC_HIGH,StartAddressL>>7);
- SetGf116(SET_END_LOW,EndAddressL<<9);
- SetGf116(SET_END_HIGH,EndAddressL>>7);
- // Set balance to full left
- SetGf18(SET_BALANCE,0);
- // Set volume to full
- SetGf116(SET_VOLUME,0xFFF0);
- // Set FC to 2 (so we play every second sample)
- SetGf116(SET_FREQUENCY,0x0800);
- // Set right voice addresses
- dos_outportb(Gf1PageRegister,1);
- SetGf116(SET_START_LOW,StartAddressR<<9);
- SetGf116(SET_START_HIGH,StartAddressR>>7);
- SetGf116(SET_ACC_LOW,StartAddressR<<9);
- SetGf116(SET_ACC_HIGH,StartAddressR>>7);
- SetGf116(SET_END_LOW,EndAddressR<<9);
- SetGf116(SET_END_HIGH,EndAddressR>>7);
- // Set balance to full right
- SetGf18(SET_BALANCE,15);
- // Set volume to full
- SetGf116(SET_VOLUME,0xFFF0);
- // Set FC to 2 (so we play every second sample)
- SetGf116(SET_FREQUENCY,0x0800);
- // Start voices
- dos_outportb(Gf1PageRegister,0);
- SetGf18(SET_CONTROL,0x0C);
- dos_outportb(Gf1PageRegister,1);
- SetGf18(SET_CONTROL,0x0C);
- Gf1Delay();
- dos_outportb(Gf1PageRegister,0);
- SetGf18(SET_CONTROL,0x0C);
- dos_outportb(Gf1PageRegister,1);
- SetGf18(SET_CONTROL,0x0C);
- }
- //=============================================================================
- // Figures out what kind of UltraSound we have, if any, and starts it playing
- //=============================================================================
- qboolean GUS_Init(void)
- {
- int rc;
- int RealAddr;
- BYTE FSVal,Voices;
- struct CodecRateStruct *CodecRate;
- struct Gf1RateStruct *Gf1Rate;
- // See what kind of UltraSound we have, if any
- if (GUS_GetIWData()==false)
- if (GUS_GetMAXData()==false)
- if (GUS_GetGUSData()==false)
- return(false);
- shm = &sn;
- if (HaveCodec)
- {
- // do 11khz sampling rate unless command line parameter wants different
- shm->speed = 11025;
- FSVal = 0x03;
- rc = COM_CheckParm("-sspeed");
- if (rc)
- {
- shm->speed = Q_atoi(com_argv[rc+1]);
-
- // Make sure rate not too high
- if (shm->speed>48000)
- shm->speed=48000;
-
- // Adjust speed to match one of the possible CODEC rates
- for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++)
- {
- if (shm->speed <= CodecRate->Rate)
- {
- shm->speed=CodecRate->Rate;
- FSVal=CodecRate->FSVal;
- break;
- }
- }
- }
-
- // Always do 16 bit stereo
- shm->channels = 2;
- shm->samplebits = 16;
-
- // allocate buffer twice the size we need so we can get aligned buffer
- dma_buffer = dos_getmemory(BUFFER_SIZE*2);
- if (dma_buffer==NULL)
- {
- Con_Printf("Couldn't allocate sound dma buffer");
- return false;
- }
- RealAddr = ptr2real(dma_buffer);
- RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1);
- dma_buffer = (short *) real2ptr(RealAddr);
- // Zero off DMA buffer
- memset(dma_buffer, 0, BUFFER_SIZE);
- shm->soundalive = true;
- shm->splitbuffer = false;
- shm->samplepos = 0;
- shm->submission_chunk = 1;
- shm->buffer = (unsigned char *) dma_buffer;
- shm->samples = BUFFER_SIZE/(shm->samplebits/8);
- GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
- GUS_StartCODEC(BUFFER_SIZE,FSVal);
- }
- else
- {
- // do 19khz sampling rate unless command line parameter wants different
- shm->speed = 19293;
- Voices=32;
- rc = COM_CheckParm("-sspeed");
- if (rc)
- {
- shm->speed = Q_atoi(com_argv[rc+1]);
- // Make sure rate not too high
- if (shm->speed>44100)
- shm->speed=44100;
- // Adjust speed to match one of the possible GF1 rates
- for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++)
- {
- if (shm->speed <= Gf1Rate->Rate)
- {
- shm->speed=Gf1Rate->Rate;
- Voices=Gf1Rate->Voices;
- break;
- }
- }
- }
- // Always do 16 bit stereo
- shm->channels = 2;
- shm->samplebits = 16;
- // allocate buffer twice the size we need so we can get aligned buffer
- dma_buffer = dos_getmemory(BUFFER_SIZE*2);
- if (dma_buffer==NULL)
- {
- Con_Printf("Couldn't allocate sound dma buffer");
- return false;
- }
- RealAddr = ptr2real(dma_buffer);
- RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1);
- dma_buffer = (short *) real2ptr(RealAddr);
- // Zero off DMA buffer
- memset(dma_buffer, 0, BUFFER_SIZE);
- shm->soundalive = true;
- shm->splitbuffer = false;
- shm->samplepos = 0;
- shm->submission_chunk = 1;
- shm->buffer = (unsigned char *) dma_buffer;
- shm->samples = BUFFER_SIZE/(shm->samplebits/8);
- GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
- SetGf116(SET_DMA_ADDRESS,0x0000);
- if (DmaChannel<=3)
- SetGf18(DMA_CONTROL,0x41);
- else
- SetGf18(DMA_CONTROL,0x45);
- GUS_StartGf1(BUFFER_SIZE,Voices);
- }
- return(true);
- }
- //=============================================================================
- // Returns the current playback position
- //=============================================================================
- int GUS_GetDMAPos(void)
- {
- int count;
- if (HaveCodec)
- {
- // clear 16-bit reg flip-flop
- // load the current dma count register
- if (DmaChannel < 4)
- {
- dos_outportb(0x0C, 0);
- count = dos_inportb(CountReg);
- count += dos_inportb(CountReg) << 8;
- if (shm->samplebits == 16)
- count /= 2;
- count = shm->samples - (count+1);
- }
- else
- {
- dos_outportb(0xD8, 0);
- count = dos_inportb(CountReg);
- count += dos_inportb(CountReg) << 8;
- if (shm->samplebits == 8)
- count *= 2;
- count = shm->samples - (count+1);
- }
- }
- else
- {
- // Read current position from GF1
- dos_outportb(Gf1PageRegister,0);
- count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF;
- // See which half of buffer we are in. Note that since this is 16 bit
- // data we are playing, position is in 16 bit samples
- if (GetGf18(DMA_CONTROL) & 0x40)
- {
- GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE);
- SetGf116(SET_DMA_ADDRESS,0x0000);
- if (DmaChannel<=3)
- SetGf18(DMA_CONTROL,0x41);
- else
- SetGf18(DMA_CONTROL,0x45);
- }
- }
- shm->samplepos = count & (shm->samples-1);
- return(shm->samplepos);
- }
- //=============================================================================
- // Stops the UltraSound playback
- //=============================================================================
- void GUS_Shutdown (void)
- {
- if (HaveCodec)
- {
- // Stop CODEC
- dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG);
- dos_outportb(CodecData,0x01);
- }
- else
- {
- // Stop Voices
- dos_outportb(Gf1PageRegister,0);
- SetGf18(SET_CONTROL,0x03);
- dos_outportb(Gf1PageRegister,1);
- SetGf18(SET_CONTROL,0x03);
- Gf1Delay();
- dos_outportb(Gf1PageRegister,0);
- SetGf18(SET_CONTROL,0x03);
- dos_outportb(Gf1PageRegister,1);
- SetGf18(SET_CONTROL,0x03);
- // Stop any DMA
- SetGf18(DMA_CONTROL,0x00);
- GetGf18(DMA_CONTROL);
- }
- dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel
- }
|