mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-27 06:34:11 +00:00
1fce1ea12e
this is I mistakenly did so while making some other changes (which I made sure were NOT in the checkin:)
648 lines
13 KiB
C
648 lines
13 KiB
C
|
|
/*
|
|
snd_dos.c
|
|
|
|
@description@
|
|
|
|
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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
$Id$
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#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, "No Description");
|
|
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)
|
|
{
|
|
}
|