📄 snd_gus.c
字号:
//=============================================================================
// 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
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -