0,0 → 1,653 |
/* |
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) |
{ |
} |
|