mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-05 20:50:43 +00:00
1281 lines
33 KiB
C
1281 lines
33 KiB
C
|
|
/*
|
|
snd_gus.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"
|
|
|
|
//=============================================================================
|
|
// Author(s): Jayeson Lee-Steere
|
|
|
|
#define INI_STRING_SIZE 0x100
|
|
|
|
QFile *ini_fopen (const char *filename, const char *modes);
|
|
int ini_fclose (QFile *f);
|
|
void ini_fgets (QFile *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 QFile *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 (QFile *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 (strcasecmp (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 (strcasecmp (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, QFile *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
|
|
QFile *
|
|
ini_fopen (const char *filename, const char *modes)
|
|
{
|
|
return (Qopen (filename, modes));
|
|
}
|
|
|
|
// Closes a .INI file. Works like fclose
|
|
int
|
|
ini_fclose (QFile *f)
|
|
{
|
|
if (f == current_file)
|
|
reset_buffer (NULL);
|
|
return (Qclose (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 (QFile *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];
|
|
QFile *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
|
|
}
|