318 lines
7.3 KiB
C++
318 lines
7.3 KiB
C++
/* Hovertank 3-D Source Code
|
|
* Copyright (C) 1993-2014 Flat Rock Software
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/*
|
|
** jm_sb.c
|
|
** Jason's Music routines for games
|
|
** SoundBlaster Stuff
|
|
** v1.0d4
|
|
*/
|
|
|
|
#include <dos.h>
|
|
|
|
#include "jm_sb.h"
|
|
|
|
#pragma warn -pia // Because I use if (x = y) alot...
|
|
|
|
// Macros for SoundBlaster stuff
|
|
#define sbOut(n,b) outportb((n) + sbLocation,b)
|
|
#define sbIn(n) inportb((n) + sbLocation)
|
|
#define sbWriteDelay() while (sbIn(sbWriteStat) & 0x80);
|
|
#define sbReadDelay() while (sbIn(sbDataAvail) & 0x80);
|
|
|
|
//
|
|
// Stuff I need
|
|
//
|
|
static boolean sbSamplePlaying = false;
|
|
static byte sbOldIntMask = -1;
|
|
static byte huge *sbNextSegPtr;
|
|
static int sbLocation = -1,sbInterrupt = 7,sbIntVec = 0xf,
|
|
sbIntVectors[] = {-1,-1,0xa,0xb,-1,0xd,-1,0xf};
|
|
static longword sbNextSegLen;
|
|
static SampledSound huge *sbSamples;
|
|
static void interrupt (*sbOldIntHand)(void);
|
|
|
|
//
|
|
// jmDelay(usec) - Delays for usec micro-seconds
|
|
//
|
|
static void
|
|
jmDelay(int usec)
|
|
{
|
|
// DEBUG - use real timing routines
|
|
int i;
|
|
|
|
while (usec--)
|
|
for (i = 0;i < 1000;i++)
|
|
;
|
|
}
|
|
|
|
//
|
|
// SoundBlaster code
|
|
//
|
|
|
|
//
|
|
// Plays a chunk of sampled sound
|
|
//
|
|
static longword
|
|
jmPlaySeg(byte huge *data,longword length)
|
|
{
|
|
unsigned datapage;
|
|
longword dataofs,uselen;
|
|
|
|
uselen = length;
|
|
datapage = FP_SEG(data) >> 12;
|
|
dataofs = ( (FP_SEG(data)&0xfff)<<4 ) + FP_OFF(data);
|
|
if (dataofs>=0x10000)
|
|
{
|
|
datapage++;
|
|
dataofs-=0x10000;
|
|
}
|
|
|
|
if (dataofs + uselen > 0x10000)
|
|
uselen = 0x10000 - dataofs;
|
|
|
|
uselen--; // DEBUG
|
|
|
|
// Program the DMA controller
|
|
outportb(0x0a,5); // Mask off channel 1 DMA
|
|
outportb(0x0c,0); // Clear byte ptr F/F to lower byte
|
|
outportb(0x0b,0x49); // Set transfer mode for D/A conv
|
|
outportb(0x02,(byte)dataofs); // Give LSB of address
|
|
outportb(0x02,(byte)(dataofs >> 8)); // Give MSB of address
|
|
outportb(0x83,(byte)datapage); // Give page of address
|
|
outportb(0x03,(byte)uselen); // Give LSB of length
|
|
outportb(0x03,(byte)(uselen >> 8)); // Give MSB of length
|
|
outportb(0x0a,1); // Turn on channel 1 DMA
|
|
|
|
// Start playing the thing
|
|
sbWriteDelay();
|
|
sbOut(sbWriteCmd,0x14);
|
|
sbWriteDelay();
|
|
sbOut(sbWriteData,(byte)uselen);
|
|
sbWriteDelay();
|
|
sbOut(sbWriteData,(byte)(uselen >> 8));
|
|
|
|
return(++uselen);
|
|
}
|
|
|
|
//
|
|
// Services the SoundBlaster DMA interrupt
|
|
//
|
|
void interrupt
|
|
jmSBService(void)
|
|
{
|
|
longword used;
|
|
|
|
sbIn(sbDataAvail);
|
|
outportb(0x20,0x20);
|
|
|
|
if (sbNextSegPtr)
|
|
{
|
|
used = jmPlaySeg(sbNextSegPtr,sbNextSegLen);
|
|
if (sbNextSegLen <= used)
|
|
sbNextSegPtr = nil;
|
|
else
|
|
{
|
|
sbNextSegPtr += used;
|
|
sbNextSegLen -= used;
|
|
}
|
|
}
|
|
else
|
|
jmStopSample();
|
|
}
|
|
|
|
//
|
|
// Plays a sampled sound. Returns immediately and uses DMA to play the sound
|
|
//
|
|
void
|
|
jmPlaySample(sound)
|
|
int sound;
|
|
{
|
|
byte huge *data,
|
|
timevalue;
|
|
longword used;
|
|
SampledSound sample;
|
|
|
|
jmStopSample();
|
|
|
|
sample = sbSamples[--sound];
|
|
|
|
data = ((byte huge *)sbSamples) + sample.offset;
|
|
timevalue = 256 - (1000000 / sample.hertz);
|
|
|
|
// printf("sample #%d ",sound);
|
|
// printf("%ld bytes, %ldHz, tc=%d\n",sample.length,sample.hertz,timevalue);
|
|
|
|
// printf("setting time constant\n");
|
|
// Set the SoundBlaster DAC time constant
|
|
sbWriteDelay();
|
|
sbOut(sbWriteCmd,0x40);
|
|
sbWriteDelay();
|
|
sbOut(sbWriteData,timevalue);
|
|
|
|
used = jmPlaySeg(data,sample.length);
|
|
if (sample.length <= used)
|
|
sbNextSegPtr = nil;
|
|
else
|
|
{
|
|
sbNextSegPtr = data + used;
|
|
sbNextSegLen = sample.length - used;
|
|
}
|
|
|
|
// printf("enabling SB irq #%d\n",sbInterrupt);
|
|
// Save old interrupt status and unmask ours
|
|
sbOldIntMask = inportb(0x21);
|
|
outportb(0x21,sbOldIntMask & ~(1 << sbInterrupt));
|
|
|
|
// printf("enabling DSP DMA request\n");
|
|
sbWriteDelay();
|
|
sbOut(sbWriteCmd,0xd4); // Make sure DSP DMA is enabled
|
|
|
|
// printf("sound should be playing\n");
|
|
sbSamplePlaying = true;
|
|
}
|
|
|
|
//
|
|
// Stops any active sampled sound and causes DMA to cease
|
|
//
|
|
void
|
|
jmStopSample(void)
|
|
{
|
|
extern unsigned SndPriority;
|
|
byte is;
|
|
|
|
if (sbSamplePlaying)
|
|
{
|
|
// printf("turning off DSP DMA\n");
|
|
sbWriteDelay();
|
|
sbOut(sbWriteCmd,0xd0); // Turn off DSP DMA
|
|
|
|
// printf("restoring interrupt\n");
|
|
is = inportb(0x21); // Restore interrupt mask bit
|
|
if (sbOldIntMask & (1 << sbInterrupt))
|
|
is |= (1 << sbInterrupt);
|
|
else
|
|
is &= ~(1 << sbInterrupt);
|
|
outportb(0x21,is);
|
|
|
|
|
|
sbSamplePlaying = false;
|
|
SndPriority = 0; // JC: hack for playsound
|
|
}
|
|
}
|
|
//
|
|
// Checks to see if a SoundBlaster resides at a particular I/O location
|
|
//
|
|
static boolean
|
|
jmCheckSB(int port)
|
|
{
|
|
int i;
|
|
|
|
sbLocation = port << 4; // Initialize stuff for later use
|
|
|
|
sbOut(sbReset,true); // Reset the SoundBlaster DSP
|
|
jmDelay(4); // Wait 4usec
|
|
sbOut(sbReset,false); // Turn off sb DSP reset
|
|
jmDelay(100); // Wait 100usec
|
|
for (i = 0;i < 100;i++)
|
|
{
|
|
if (sbIn(sbDataAvail) & 0x80) // If data is available...
|
|
{
|
|
if (sbIn(sbReadData) == 0xaa) // If it matches correct value
|
|
return(true);
|
|
else
|
|
{
|
|
sbLocation = -1; // Otherwise not a SoundBlaster
|
|
return(false);
|
|
}
|
|
}
|
|
}
|
|
sbLocation = -1; // Retry count exceeded - fail
|
|
return(false);
|
|
}
|
|
|
|
//
|
|
// Checks to see if a SoundBlaster is in the system. If the port passed is
|
|
// -1, then it scans through all possible I/O locations. If the port
|
|
// passed is 0, then it uses the default (2). If the port is >0, then
|
|
// it just passes it directly to jmCheckSB()
|
|
//
|
|
boolean
|
|
jmDetectSoundBlaster(int port)
|
|
{
|
|
int i;
|
|
|
|
if (port == 0) // If user specifies default address, use 2
|
|
port = 2;
|
|
if (port == -1)
|
|
{
|
|
for (i = 1;i <= 6;i++) // Scan through possible SB locations
|
|
{
|
|
if (jmCheckSB(i)) // If found at this address,
|
|
return(true); // return success
|
|
}
|
|
return(false); // All addresses failed, return failure
|
|
}
|
|
else
|
|
return(jmCheckSB(port)); // User specified address or default
|
|
}
|
|
|
|
//
|
|
// Sets the interrupt number that my code expects the SoundBlaster to use
|
|
//
|
|
void
|
|
jmSetSBInterrupt(int num)
|
|
{
|
|
sbInterrupt = num;
|
|
sbIntVec = sbIntVectors[sbInterrupt];
|
|
}
|
|
|
|
void
|
|
jmStartSB(void)
|
|
{
|
|
// printf("setting interrupt #0x%02x handler\n",sbIntVec);
|
|
sbOldIntHand = getvect(sbIntVec); // Get old interrupt handler
|
|
setvect(sbIntVec,jmSBService); // Set mine
|
|
|
|
// printf("setting DSP modes\n");
|
|
sbWriteDelay();
|
|
sbOut(sbWriteCmd,0xd1); // Turn on DSP speaker
|
|
}
|
|
|
|
void
|
|
jmShutSB(void)
|
|
{
|
|
jmStopSample();
|
|
|
|
// printf("restoring interrupt vector\n");
|
|
setvect(sbIntVec,sbOldIntHand); // Set vector back
|
|
}
|
|
|
|
boolean
|
|
jmSamplePlaying(void)
|
|
{
|
|
return(sbSamplePlaying);
|
|
}
|
|
|
|
void
|
|
jmSetSamplePtr(s)
|
|
SampledSound huge *s;
|
|
{
|
|
sbSamples = s;
|
|
}
|