123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- /*
- 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.
- */
- #include "quakedef.h"
- #include "dosisms.h"
- int BLASTER_GetDMAPos(void);
- /*
- ===============================================================================
- GUS SUPPORT
- ===============================================================================
- */
- qboolean GUS_Init (void);
- int GUS_GetDMAPos (void);
- void GUS_Shutdown (void);
- /*
- ===============================================================================
- BLASTER SUPPORT
- ===============================================================================
- */
- short *dma_buffer=0;
- static int dma_size;
- static int dma;
- static int dsp_port;
- static int irq;
- static int low_dma;
- static int high_dma;
- static int mixer_port;
- static int mpu401_port;
- int dsp_version;
- int dsp_minor_version;
- int timeconstant=-1;
- void PrintBits (byte b)
- {
- int i;
- char str[9];
-
- for (i=0 ; i<8 ; i++)
- str[i] = '0' + ((b & (1<<(7-i))) > 0);
-
- str[8] = 0;
- Con_Printf ("%s (%i)", str, b);
- }
- void SB_Info_f(void)
- {
- Con_Printf ("BLASTER=%s\n", getenv("BLASTER"));
- Con_Printf("dsp version=%d.%d\n", dsp_version, dsp_minor_version);
- Con_Printf("dma=%d\n", dma);
- if (timeconstant != -1)
- Con_Printf("timeconstant=%d\n", timeconstant);
- Con_Printf("dma position:%i\n", BLASTER_GetDMAPos ());
- }
- // =======================================================================
- // Interprets BLASTER variable
- // =======================================================================
- int GetBLASTER(void)
- {
- char *BLASTER;
- char *param;
- BLASTER = getenv("BLASTER");
- if (!BLASTER)
- return 0;
- param = strchr(BLASTER, 'A');
- if (!param)
- param = strchr(BLASTER, 'a');
- if (!param)
- return 0;
- sscanf(param+1, "%x", &dsp_port);
- param = strchr(BLASTER, 'I');
- if (!param)
- param = strchr(BLASTER, 'i');
- if (!param)
- return 0;
- sscanf(param+1, "%d", &irq);
- param = strchr(BLASTER, 'D');
- if (!param)
- param = strchr(BLASTER, 'd');
- if (!param)
- return 0;
- sscanf(param+1, "%d", &low_dma);
- param = strchr(BLASTER, 'H');
- if (!param)
- param = strchr(BLASTER, 'h');
- if (param)
- sscanf(param+1, "%d", &high_dma);
- param = strchr(BLASTER, 'M');
- if (!param)
- param = strchr(BLASTER, 'm');
- if (param)
- sscanf(param+1, "%x", &mixer_port);
- else
- mixer_port = dsp_port;
- param = strchr(BLASTER, 'P');
- if (!param)
- param = strchr(BLASTER, 'p');
- if (param)
- sscanf(param+1, "%x", &mpu401_port);
- return 1;
- }
- // ==================================================================
- // Resets DSP. Returns 0 on success.
- // ==================================================================
- int ResetDSP(void)
- {
- volatile int i;
- dos_outportb(dsp_port + 6, 1);
- for (i=65536 ; i ; i--) ;
- dos_outportb(dsp_port + 6, 0);
- for (i=65536 ; i ; i--)
- {
- if (!(dos_inportb(dsp_port + 0xe) & 0x80)) continue;
- if (dos_inportb(dsp_port + 0xa) == 0xaa) break;
- }
- if (i) return 0;
- else return 1;
- }
- int ReadDSP(void)
- {
- while (!(dos_inportb(dsp_port+0xe)&0x80)) ;
- return dos_inportb(dsp_port+0xa);
- }
- void WriteDSP(int val)
- {
- while ((dos_inportb(dsp_port+0xc)&0x80)) ;
- dos_outportb(dsp_port+0xc, val);
- }
- int ReadMixer(int addr)
- {
- dos_outportb(mixer_port+4, addr);
- return dos_inportb(mixer_port+5);
- }
- void WriteMixer(int addr, int val)
- {
- dos_outportb(mixer_port+4, addr);
- dos_outportb(mixer_port+5, val);
- }
- int oldmixervalue;
- /*
- ================
- StartSB
- ================
- */
- void StartSB(void)
- {
- int i;
- // version 4.xx startup code
- if (dsp_version >= 4)
- {
- Con_Printf("Version 4 SB startup\n");
- WriteDSP(0xd1); // turn on speaker
- WriteDSP(0x41);
- WriteDSP(shm->speed>>8);
- WriteDSP(shm->speed&0xff);
- WriteDSP(0xb6); // 16-bit output
- WriteDSP(0x30); // stereo
- WriteDSP((shm->samples-1) & 0xff); // # of samples - 1
- WriteDSP((shm->samples-1) >> 8);
- }
- // version 3.xx startup code
- else if (dsp_version == 3)
- {
- Con_Printf("Version 3 SB startup\n");
- WriteDSP(0xd1); // turn on speaker
- oldmixervalue = ReadMixer (0xe);
- WriteMixer (0xe, oldmixervalue | 0x2);// turn on stereo
- WriteDSP(0x14); // send one byte
- WriteDSP(0x0);
- WriteDSP(0x0);
- for (i=0 ; i<0x10000 ; i++)
- dos_inportb(dsp_port+0xe); // ack the dsp
-
- timeconstant = 65536-(256000000/(shm->channels*shm->speed));
- WriteDSP(0x40);
- WriteDSP(timeconstant>>8);
- WriteMixer (0xe, ReadMixer(0xe) | 0x20);// turn off filter
- WriteDSP(0x48);
- WriteDSP((shm->samples-1) & 0xff); // # of samples - 1
- WriteDSP((shm->samples-1) >> 8);
- WriteDSP(0x90); // high speed 8 bit stereo
- }
- // normal speed mono
- else
- {
- Con_Printf("Version 2 SB startup\n");
- WriteDSP(0xd1); // turn on speaker
- timeconstant = 65536-(256000000/(shm->channels*shm->speed));
- WriteDSP(0x40);
- WriteDSP(timeconstant>>8);
- WriteDSP(0x48);
- WriteDSP((shm->samples-1) & 0xff); // # of samples - 1
- WriteDSP((shm->samples-1) >> 8);
- WriteDSP(0x1c); // normal speed 8 bit mono
- }
- }
- static int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
- static int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };
- static int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };
- static int mode_reg;
- static int flipflop_reg;
- static int disable_reg;
- static int clear_reg;
- /*
- ================
- StartDMA
- ================
- */
- void StartDMA(void)
- {
- int mode;
- int realaddr;
- realaddr = ptr2real(dma_buffer);
- // use a high dma channel if specified
- if (high_dma && dsp_version >= 4) // 8 bit snd can never use 16 bit dma
- dma = high_dma;
- else
- dma = low_dma;
- Con_Printf ("Using DMA channel %i\n", dma);
- if (dma > 3)
- {
- mode_reg = 0xd6;
- flipflop_reg = 0xd8;
- disable_reg = 0xd4;
- clear_reg = 0xdc;
- }
- else
- {
- mode_reg = 0xb;
- flipflop_reg = 0xc;
- disable_reg = 0xa;
- clear_reg = 0xe;
- }
- dos_outportb(disable_reg, dma|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
- +(dma&3); // channel #
- dos_outportb(mode_reg, mode);
-
- // set address
- // set page
- dos_outportb(page_reg[dma], realaddr >> 16);
- if (dma > 3)
- { // address is in words
- dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
- dos_outportb(addr_reg[dma], (realaddr>>1) & 0xff);
- dos_outportb(addr_reg[dma], (realaddr>>9) & 0xff);
- dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
- dos_outportb(count_reg[dma], ((dma_size>>1)-1) & 0xff);
- dos_outportb(count_reg[dma], ((dma_size>>1)-1) >> 8);
- }
- else
- { // address is in bytes
- dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
- dos_outportb(addr_reg[dma], realaddr & 0xff);
- dos_outportb(addr_reg[dma], (realaddr>>8) & 0xff);
- dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value
- dos_outportb(count_reg[dma], (dma_size-1) & 0xff);
- dos_outportb(count_reg[dma], (dma_size-1) >> 8);
- }
- dos_outportb(clear_reg, 0); // clear write mask
- dos_outportb(disable_reg, dma&~4);
- }
- /*
- ==================
- BLASTER_Init
- Returns false if nothing is found.
- ==================
- */
- qboolean BLASTER_Init(void)
- {
- int size;
- int realaddr;
- int rc;
- int p;
-
- shm = 0;
- rc = 0;
- //
- // must have a blaster variable set
- //
- if (!GetBLASTER())
- {
- Con_NotifyBox (
- "The BLASTER environment variable\n"
- "is not set, sound effects are\n"
- "disabled. See README.TXT for help.\n"
- );
- return 0;
- }
- if (ResetDSP())
- {
- Con_Printf("Could not reset SB");
- return 0;
- }
- //
- // get dsp version
- //
- WriteDSP(0xe1);
- dsp_version = ReadDSP();
- dsp_minor_version = ReadDSP();
- // we need at least v2 for auto-init dma
- if (dsp_version < 2)
- {
- Con_Printf ("Sound blaster must be at least v2.0\n");
- return 0;
- }
- // allow command line parm to set quality down
- p = COM_CheckParm ("-dsp");
- if (p && p < com_argc - 1)
- {
- p = Q_atoi (com_argv[p+1]);
- if (p < 2 || p > 4)
- Con_Printf ("-dsp parameter can only be 2, 3, or 4\n");
- else if (p > dsp_version)
- Con_Printf ("Can't -dsp %i on v%i hardware\n", p, dsp_version);
- else
- dsp_version = p;
- }
- // everyone does 11khz sampling rate unless told otherwise
- shm = &sn;
- shm->speed = 11025;
- rc = COM_CheckParm("-sspeed");
- if (rc)
- shm->speed = Q_atoi(com_argv[rc+1]);
- // version 4 cards (sb 16) do 16 bit stereo
- if (dsp_version >= 4)
- {
- shm->channels = 2;
- shm->samplebits = 16;
- }
- // version 3 cards (sb pro) do 8 bit stereo
- else if (dsp_version == 3)
- {
- shm->channels = 2;
- shm->samplebits = 8;
- }
- // v2 cards do 8 bit mono
- else
- {
- shm->channels = 1;
- shm->samplebits = 8;
- }
-
- Cmd_AddCommand("sbinfo", SB_Info_f);
- size = 4096;
- // allocate 8k and get a 4k-aligned buffer from it
- dma_buffer = dos_getmemory(size*2);
- if (!dma_buffer)
- {
- Con_Printf("Couldn't allocate sound dma buffer");
- return false;
- }
- realaddr = ptr2real(dma_buffer);
- realaddr = (realaddr + size) & ~(size-1);
- dma_buffer = (short *) real2ptr(realaddr);
- dma_size = size;
- memset(dma_buffer, 0, dma_size);
- shm->soundalive = true;
- shm->splitbuffer = false;
- shm->samples = size/(shm->samplebits/8);
- shm->samplepos = 0;
- shm->submission_chunk = 1;
- shm->buffer = (unsigned char *) dma_buffer;
- shm->samples = size/(shm->samplebits/8);
- StartDMA();
- StartSB();
- return true;
- }
- /*
- ==============
- BLASTER_GetDMAPos
- return the current sample position (in mono samples read)
- inside the recirculating dma buffer, so the mixing code will know
- how many sample are required to fill it up.
- ===============
- */
- int BLASTER_GetDMAPos(void)
- {
- int count;
- // this function is called often. acknowledge the transfer completions
- // all the time so that it loops
- if (dsp_version >= 4)
- dos_inportb(dsp_port+0xf); // 16 bit audio
- else
- dos_inportb(dsp_port+0xe); // 8 bit audio
- // clear 16-bit reg flip-flop
- // load the current dma count register
- if (dma < 4)
- {
- dos_outportb(0xc, 0);
- count = dos_inportb(dma*2+1);
- count += dos_inportb(dma*2+1) << 8;
- if (shm->samplebits == 16)
- count /= 2;
- count = shm->samples - (count+1);
- }
- else
- {
- dos_outportb(0xd8, 0);
- count = dos_inportb(0xc0+(dma-4)*4+2);
- count += dos_inportb(0xc0+(dma-4)*4+2) << 8;
- if (shm->samplebits == 8)
- count *= 2;
- count = shm->samples - (count+1);
- }
- // Con_Printf("DMA pos = 0x%x\n", count);
- shm->samplepos = count & (shm->samples-1);
- return shm->samplepos;
- }
- /*
- ==============
- BLASTER_Shutdown
- Reset the sound device for exiting
- ===============
- */
- void BLASTER_Shutdown(void)
- {
- if (dsp_version >= 4)
- {
- }
- else if (dsp_version == 3)
- {
- ResetDSP (); // stop high speed mode
- WriteMixer (0xe, oldmixervalue); // turn stereo off and filter on
- }
- else
- {
-
- }
-
- WriteDSP(0xd3); // turn off speaker
- ResetDSP ();
- dos_outportb(disable_reg, dma|4); // disable dma channel
- }
- /*
- ===============================================================================
- INTERFACE
- ===============================================================================
- */
- typedef enum
- {
- dma_none,
- dma_blaster,
- dma_gus
- } dmacard_t;
- dmacard_t dmacard;
- /*
- ==================
- SNDDM_Init
- Try to find a sound device to mix for.
- Returns false if nothing is found.
- Returns true and fills in the "shm" structure with information for the mixer.
- ==================
- */
- qboolean SNDDMA_Init(void)
- {
- if (GUS_Init ())
- {
- dmacard = dma_gus;
- return true;
- }
- if (BLASTER_Init ())
- {
- dmacard = dma_blaster;
- return true;
- }
-
- dmacard = dma_none;
-
- return false;
- }
- /*
- ==============
- SNDDMA_GetDMAPos
- return the current sample position (in mono samples, not stereo)
- inside the recirculating dma buffer, so the mixing code will know
- how many sample are required to fill it up.
- ===============
- */
- int SNDDMA_GetDMAPos(void)
- {
- switch (dmacard)
- {
- case dma_blaster:
- return BLASTER_GetDMAPos ();
- case dma_gus:
- return GUS_GetDMAPos ();
- case dma_none:
- break;
- }
-
- return 0;
- }
- /*
- ==============
- SNDDMA_Shutdown
- Reset the sound device for exiting
- ===============
- */
- void SNDDMA_Shutdown(void)
- {
- switch (dmacard)
- {
- case dma_blaster:
- BLASTER_Shutdown ();
- break;
- case dma_gus:
- GUS_Shutdown ();
- break;
- case dma_none:
- break;
- }
- dmacard = dma_none;
- return;
- }
- /*
- ==============
- SNDDMA_Submit
- Send sound to device if buffer isn't really the dma buffer
- ===============
- */
- void SNDDMA_Submit(void)
- {
- }
|