home *** CD-ROM | disk | FTP | other *** search
- /* Soundblaster Digital Audio driver for Borland C++. *
- * *
- * Much of the program code is derived from various modules *
- * of the VangeliSTracker program, specifically SOUNDBLA.PAS *
- * and HARDWARE.PAS. Many thanks for making these wonderful *
- * sources available to the public. They basically allowed *
- * me to complete this project. *
- * *
- * Other portions of the code are derived from Michael *
- * Fulbright and Steve Haehnichen's SBMSDOS v1.0 code. *
- * *
- * 31May94 zaph : Slight changes to compile under MSC *
- * *
- * Copyright (C) 1993 by Daniel Sachs *
- * Parts Copyright (C) 1993 by VangeliSTeam *
- * Parts Copyright (C) 1992 by Steve Haehnichen */
-
- #define SB_DRIVE
- // #define DEBUG_PROC
- // #define DEBUG_DMASTAT
-
- #include "sb_drive.h"
- #include "dma.h"
- #include <dos.h>
- #include <stdlib.h>
- #ifdef _MSC_VER
- #include <memory.h>
- #include "patch.h"
- #define setvect(i,p) _dos_setvect(i,p)
- #define getvect(i) _dos_getvect(i)
- #define disable() _disable()
- #define enable() _enable()
- #else
- #include <mem.h>
- #endif
- #include <assert.h>
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <conio.h>
-
- #define AUTO_PREWRITE // Uncomment to load entire buffer before
- // starting playback. By default playback
- // starts immediately.
-
- int GUS = 0; // Welp, the GUS needs some special treatment. :)
-
- //#define WARN_OS2 // Uncomment to warn about 16-bit DMA incompatibilty
-
- //#define DEBUG // Uncomment for full debugging outputs.
-
- //#define DEBUG_DMASTAT // Uncomment for DMA buffer status information
- //#define DEBUG_PROC // Uncomment for debug information from most functions.
- //#define DEBUG_WRITE // Uncomment for debug information from dsp_write
- //#define DEBUG_FREE // Uncomment for debug information from dsp_bufs_free
-
- #ifdef DEBUG
- #define DEBUG_DMASTAT
- #define DEBUG_PROC
- #define DEBUG_WRITE
- #define DEBUG_FREE
- #endif
-
- struct dsp_device_caps SB_caps[] =
- {
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // No SB
- { 4000, 22222, 0, 0, 0, 11111, 0, 0, 0, 0 }, // SB 1.x
- { 4000, 45454, 0, 0, 0, 15151, 0, 0, 0, 0 }, // SB 2.x
- { 4000, 45454, 22727, 0, 0, 45454, 22727, 0, 0, 0 }, // SBPro
- { 4000, 45454, 45454, 45454, 45454, 45454, 45454, 45454, 45454, 1 } // SB16
- };
-
- int SBport = 0x220;
- int SBirq = 5;
- int SBdma = 1;
- int SBdma16 = 5;
-
- int SBintnum;
- int SBtype;
-
- int SBTimeOut = 5000; /* time to wait for DSP response */
-
- int SBspeaker; // Speaker enabled?
-
- unsigned char SBProMixRegs[] = { 0x22, 0x04, 0x26, 0x2E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00};
- unsigned char SB16MixRegs[] = { 0x30, 0x32, 0x34, 0x38, 0x36, 0x41, 0x44, 0x46, 0x3F, 0x3A, 0x3B};
-
- char SbOK;
-
- int bufs_open;
-
- char *DMABufferBase;
- char *DMABuffers[16];
-
- unsigned DMABufferSize;
-
- unsigned BufLen;
- unsigned BufCount;
-
- unsigned BufTotal;
-
- static char volatile BufQueueHead;
- static char volatile BufQueueTail;
-
- static int volatile DMABufsFull;
- static int volatile DMAStopped = 1;
-
- unsigned dsp_speed;
- char dsp_bits;
- char dsp_stereo;
- char dsp_hispeed;
- char dsp_signed;
-
- int volatile dsp_overrun;
-
- #ifdef _MSC_VER
- void (_interrupt _far *old_irq_handler)();
- #else
- void interrupt (*old_irq_handler)(void);
- #endif
- void interrupt write_irq_handler(void);
- void interrupt null_irq_handler(void);
-
- static void (*callback_function)(void);
-
- char BitMasks[] = {0xFE, /* 1111 1110 */
- 0xFD, /* 1111 1101 */
- 0xFB, /* 1111 1011 */
- 0xF7, /* 1111 0111 */
- 0xEF, /* 1110 1111 */
- 0xDF, /* 1101 1111 */
- 0xBF, /* 1011 1111 */
- 0x7F};/* 0111 1111 */
-
- void SbWriteLoop(unsigned t)
- {
- _asm {
- MOV BX,t
- MOV DX,[SBport]
- ADD DX,DSPWriteOffset
- }
-
- lp: _asm {
- DEC BX
- JZ end
- IN AL,DX
- ADD AL,AL
- JC lp
- }
- end: _asm {
- OR BL,BH
- MOV [SbOK],BL
- }
- }
-
- void SbWriteByteTimeout(unsigned char command, unsigned timeout)
- {
- SbWriteLoop(timeout);
-
- inp(DSPReadPort);
-
- SbWriteLoop(timeout);
-
- outp(DSPWritePort,command);
- }
-
- void SbWriteByte(unsigned char command)
- {
- SbWriteLoop(SBTimeOut);
-
- inp(DSPReadPort);
-
- SbWriteLoop(SBTimeOut);
-
- outp(DSPWritePort,command);
- }
-
-
- void SbReadLoop(unsigned t)
- {
- _asm MOV BX,t
- _asm MOV DX,[SBport]
- _asm ADD DX,[DSPRStatOffset]
-
- lp: _asm DEC BX
- _asm JZ fin
- _asm IN AL,DX
- _asm ADD AL,AL
- _asm JNC lp
-
- fin: _asm OR BL,BH
- _asm MOV [SbOK],BL
- }
-
- unsigned char SbReadByte(unsigned timeout)
- {
- SbReadLoop(timeout);
- return inp(DSPReadPort);
- }
-
- void SbWriteMixerReg(unsigned char reg, unsigned char value)
- {
- outp(MixAddrPort,reg);
- outp(MixDataPort,value);
- }
-
- char SbReadMixerReg(unsigned char reg)
- {
- outp(MixAddrPort,reg);
- return inp(MixDataPort);
- }
-
-
- void EnableIRQ(char irq)
- {
- disable();
-
- if( irq < 8 )
- outp(0x21,inp(0x21) & BitMasks[irq]);
- else
- {
- outp(0x21,inp(0x21) & BitMasks[1]);
- outp(0xA1,inp(0xA1) & BitMasks[irq-8]);
- }
-
- enable();
- }
-
-
- void DisableIRQ(char irq)
- {
- disable();
-
- if( irq < 8 )
- outp(0x21,inp(0x21) | (BitMasks[irq])^(0xFF));
- else
- outp(0xA1,inp(0xA1) | (BitMasks[irq-8])^(0xFF));
-
- enable();
- }
-
-
- int dsp_reset(void)
- {
- int stat;
- int ct;
-
- SBspeaker = 0;
-
- outp(DSPResetPort,1);
- delay(50);
- outp(DSPResetPort,0);
-
- for( ct=0; ct<100; ct++ ) // Reset DSP delay loop
- {
- inp(DSPRStatPort);
- stat = inp(DSPReadPort);
-
- if( stat == 0xAA )
- break;
- }
-
- if( stat != 0xAA )
- return 0;
-
- SBtype = (dsp_version() / 100); // Return card type
-
- #ifdef DEBUG_PROC
- printf("dsp_reset: type %i\n",SBtype);
- #endif
-
- return SBtype;
-
- }
-
- int dsp_version(void)
- {
- static int minor=0;
- static int major=0;
-
- int i;
-
- if( major == 0 )
- {
- SbWriteByte(DSPGetVersion); /* Command to get DSP version */
-
- for( i=0; i<10; i++ ) // Wait for card to respond
- {
- major=SbReadByte(0xFFFF);
-
- if( (major != 0xAA) /* && (SbOK) */ )
- break;
- }
-
- if( i==10 )
- return 0;
-
- minor = SbReadByte(SBTimeOut);
- }
-
- #ifdef DEBUG_PROC
- printf("dsp_version: %i.%02i\n",major,minor);
- #endif
-
- return ((major*100) + minor); // Return numeric version number
- }
-
-
- void dsp_speaker(int status)
- {
- #ifdef DEBUG_PROC
- printf("dsp_speaker: %s\n",status ? "on" : "off" );
- #endif
-
- if( SBtype >= 4 )
- return;
-
- status = !!status;
-
- if( status != SBspeaker )
- if( status )
- SbWriteByte(DSPSpeakerOn);
- else
- SbWriteByte(DSPSpeakerOff);
-
- SBspeaker = status;
-
- delay(50);
- }
-
- int dsp_open(int port, int dma, int irq, int dma16, unsigned bufsize, unsigned numbufs)
- {
- char *work;
-
- long wlong;
-
- int page;
- unsigned offset;
- unsigned segment;
-
- unsigned tot_bufsize;
-
- int i;
-
- work = getenv("ULTRASND"); // Check to see if we have a GUS
-
- if( work != NULL )
- {
- GUS = 1;
- #ifdef DEBUG_PROC
- printf("dsp_open: Gravis Ultrasound detected.\n");
- #endif
- }
-
- else
- GUS = 0;
- #ifdef DEBUG_PROC
- printf("dsp_open: port %x dma %i irq %i dma16 %i bufsize %i numbufs %i\n",port,dma,irq,dma16,bufsize,numbufs);
- #endif
-
- SBport = port; // Set the configuration flags
- SBdma = dma;
- SBirq = irq;
- SBdma16 = dma16;
-
- if(SBirq == 2)
- SBirq = 9;
-
- SBintnum = SBirq + 8 + 96*(SBirq>7);
-
- if( (DMABufferBase != NULL) && ((numbufs != BufCount) || (bufsize != BufLen)) )
- return 0; // Sorry, once you set up buffers you're stuck with 'em.
- // All you can do is change the PORT/IRQ/DMA settings.
-
- if( (numbufs < 2) || (numbufs > 16) || (bufsize < 128) || (bufsize > 31774) )
- return 0; // Too many or too long buffers?
-
- if( bufsize % 4 ) // Buffer size has to be devisible by 4
- bufsize = 4*((bufsize+4)/4);
-
- if( ((long)bufsize * numbufs) > 64000 ) // and can't be longer than 64K
- return 0;
-
- tot_bufsize = bufsize * numbufs; // How much we need to allocate
-
- if( !dsp_reset() ) // Reset the DSP. Is it there?
- return 0;
-
- #ifdef WARN_OS2
- if( (SBtype == 4) && (dma16 >= 4) && (_osmajor >= 20) )
- {
- printf("Warning: 16-bit DMA does not work properly with OS/2.\n\n"
- "Press <Esc> to abort to DOS and select 8-bit DMA.\n"
- "Press <Enter> to ignore this warning and continue.\n");
-
- i = 0;
-
- while( (i != 27) && (i != 13) )
- i = getch();
-
- if( i == 27 )
- exit(100);
- }
- #endif
-
- atexit(dsp_close); // Finish up when program exits
-
- BufLen = bufsize; // Set up buffer configuration
- BufCount = numbufs;
-
- BufTotal = tot_bufsize;
-
- if( DMABufferBase == NULL ) // Make sure we haven't
- { // done this already :)
- work = calloc(1,tot_bufsize+1);
-
- if( work == NULL )
- return 0;
-
- // Allocate page-safe buffer:
-
- offset = ((FP_SEG(work)&0x0FFF)<<4) + FP_OFF(work);
-
- if( ((long)offset+tot_bufsize) > 65535L ) // Current buffer page-safe?
- {
- realloc(work,65536L-offset); // No: reallocate it
-
- DMABufferBase = calloc(1,tot_bufsize+1);// This should be :)
- free(work);
-
- offset = ((FP_SEG(DMABufferBase)&0x0FFF)<<4) + FP_OFF(DMABufferBase);
-
- if( ((long)offset+tot_bufsize) > 65535L )
- {
- printf("Unable to allocate page-safe buffer\n");
- exit(100);
- }
- }
- else
- DMABufferBase = work; // Current buffer OK
-
- if( DMABufferBase == NULL )
- return 0;
-
- DMABufferSize = tot_bufsize; // Set up buffer pointers
- for( i = 0; i < numbufs; i++ )
- DMABuffers[i] = DMABufferBase + i*bufsize;
- }
- #ifdef _MSC_VER
- old_irq_handler = _dos_getvect(SBintnum); // Save old interrupt
- _dos_setvect(SBintnum,null_irq_handler); // ...and stick ours in
- #else
- old_irq_handler = getvect(SBintnum); // Save old interrupt
- setvect(SBintnum,null_irq_handler); // ...and stick ours in
- #endif
- callback_function = NULL;
-
- disable();
-
- BufQueueHead = 0; // We're not playing anything
- BufQueueTail = 0;
- DMABufsFull = 0;
- DMAStopped = 1;
-
- enable();
-
- EnableIRQ(SBirq);
-
- dsp_speaker(1);
-
- return SBtype;
- }
-
- void dsp_close(void)
- {
- #ifdef DEBUG_PROC
- printf("dsp_close\n");
- #endif
-
- dsp_pause_dma();
- dma_reset(SBdma);
- dma_reset(SBdma16);
-
- dsp_reset();
-
- if( SBtype <= 3 )
- SbWriteByte(DSPSBProADCMono);
-
- DisableIRQ(SBirq);
- #ifdef _MSC_VER
- _dos_setvect(SBintnum,old_irq_handler); // Put things back to normal
- #else
- setvect(SBintnum,old_irq_handler); // Put things back to normal
- #endif
- }
-
- void interrupt sbpro_bug_handler(void) // To deal with the SBPro's channel swap bug.
- {
- enable();
-
- inp(DSPIrqAck8Port); // Acknowledge interrupt
-
- dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
-
- SbWriteByte(DSPSetHSDMASize); // Reset size (more than 1 byte :)
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
-
- SbWriteByte(DSPStartHSDMA); // Restart DMA with correct channels
-
- setvect(SBintnum,write_irq_handler);
-
- if( SBirq > 8 )
- outp(0xA0,0x20);
-
- outp(0x20,0x20); // Done with interrupt
- }
-
-
- void interrupt null_irq_handler(void)
- {
- if( dsp_bits == 16 )
- inp(DSPIrqAck16Port); // Acknowledge the SB's interupt
- else
- inp(DSPIrqAck8Port);
-
- if( SBirq > 8 )
- outp(0xA0,0x20);
-
- outp(0x20,0x20); // Done with interrupt
- }
-
- void interrupt read_irq_handler(void)
- {
- enable();
-
- if( dsp_bits == 16 )
- inp(DSPIrqAck16Port); // Acknowledge the SB's interupt
- else
- inp(DSPIrqAck8Port);
-
- if( DMABufsFull == BufCount ) // Check for overrun
- {
- dsp_overrun++; // We've got an overrun. Oops.
-
- BufQueueHead = BufQueueTail = (BufQueueTail+1) % BufCount;
-
- if( dsp_overrun > 16 )
- { // We've got a LOT of overruns. Cancel
- dsp_pause_dma(); // DMA. Someone forgot about it.
- dma_reset(dsp_bits == 16 ? SBdma16 : SBdma);
-
- DMAStopped=1;
-
- BufQueueHead=0;
- BufQueueTail=0;
- DMABufsFull =0;
-
- setvect(SBintnum,null_irq_handler);
-
- SbWriteByte(DSPSBProADCMono);
- }
- }
-
- #ifdef DEBUG_DMASTAT
- {
- poke(0xB800,154,11824+BufQueueHead);
- poke(0xB800,156,11824+BufQueueTail);
- poke(0xB800,158,11824+DMABufsFull);
- }
- #endif
-
- BufQueueTail = (BufQueueTail+1) % BufCount;
- DMABufsFull++;
-
- if( SBtype <= 3 )
- {
- if( dsp_hispeed )
- SbWriteByte(DSPStartHSADCDMA);
- else
- {
- SbWriteByte(DSPStartADCDMA);
- SbWriteByte((BufLen-1)%256);
- SbWriteByte((BufLen-1)/256);
- }
- }
-
- if( callback_function != NULL )
- callback_function();
-
- if( SBirq > 8 )
- outp(0xA0,0x20);
- outp(0x20,0x20);
- }
-
- void interrupt write_irq_handler(void)
- {
- enable();
-
- if( DMABufsFull <= 1 ) // Are we done playing everything?
- {
- if( dsp_bits == 16 ) // Yup, ack the soundcard,
- inp(DSPIrqAck16Port);
- else
- inp(DSPIrqAck8Port);
-
- SbWriteByte(DSPPauseDMA); // Kill playback
-
- setvect(SBintnum,null_irq_handler); // Flip to other interrupt handler
- dma_reset(dsp_bits == 16 ? SBdma16 : SBdma); // Kill DMA
-
- DMAStopped = 1; // And flag it.
- DMABufsFull = 0;
-
- BufQueueHead = 0;
- BufQueueTail = 0;
-
- if( SBirq > 8 )
- outp(0xA0,0x20);
-
- outp(0x20,0x20); // We're done.
-
- #ifdef DEBUG_DMASTAT
- {
- poke(0xB800,154,20016+BufQueueHead);
- poke(0xB800,156,20016+BufQueueTail);
- poke(0xB800,158,20016+DMABufsFull);
- }
-
- #endif
-
- return;
- }
-
- if( dsp_bits == 16 ) // We're not done. 16 bits?
- {
- inp(DSPIrqAck16Port); // Yup. Acknowledge it to continue automatically.
-
- BufQueueHead = (BufQueueHead+1) % BufCount; // Mark that buffer as played
- DMABufsFull--;
- }
- else
- {
- inp(DSPIrqAck8Port); // No, 8 bits. So continue...
-
- BufQueueHead = (BufQueueHead+1) % BufCount; // Mark that buffer as played
- DMABufsFull--;
-
- if( GUS )
- dma_setup(SBdma,DMABuffers[BufQueueHead],BufLen-1,1);
-
- if( SBtype < 4 ) // Soundblaster 16 continues upon ack.
- if( !dsp_hispeed ) // Low-speed DMA mode
- {
- SbWriteByte(DSPStartDMA);
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
- }
- else // High-speed DMA mode
- SbWriteByte(DSPStartHSDMA);
- }
-
- if( callback_function != NULL ) // Tell the caller that we've
- callback_function(); // gotten an interrupt.
-
- #ifdef DEBUG_DMASTAT
- {
- poke(0xB800,154,11824+BufQueueHead);
- poke(0xB800,156,11824+BufQueueTail);
- poke(0xB800,158,11824+DMABufsFull);
- }
- #endif
-
- if( SBirq > 8 )
- outp(0xA0,0x20);
-
- outp(0x20,0x20); // Complete the interrupt.
- }
-
- void dsp_callback(void (*handler)(void))
- {
- #ifdef DEBUG_PROC
- printf("dsp_callback: %p\n",handler);
- #endif
-
- callback_function = handler;
- }
-
- int dsp_buffers_free(void)
- {
- int x;
-
- disable();
- x = DMABufsFull;
- enable();
-
- #ifdef DEBUG_FREE
- printf("dsp_buffers_free: %i\n",BufCount-x);
- #endif
-
- return BufCount - x;
- }
-
- int dsp_active(void)
- {
- return !DMAStopped;
- }
-
- struct dsp_device_caps *dsp_get_device_caps(void)
- {
- return &SB_caps[SBtype];
- }
-
- unsigned dsp_set_record(unsigned speed, int stereo, int bits, int sign)
- {
- dsp_speaker(0);
-
- if( SBtype >= 3 )
- return dsp_set_sample(speed,stereo,bits,sign);
-
- if( SBtype == 2 )
- if( speed <= 15151 )
- return dsp_set_sample(speed,stereo,bits,sign);
- else
- {
- #ifdef DEBUG_PROC
- printf("dsp_set_sample: speed %u %s%i %s\n",speed,stereo ? "stereo " : "", bits, sign ? "signed" : "unsigned");
- printf("dsp_set_record: failure\n");
- #endif
- return 0;
- }
-
- if( SBtype == 1 )
- if( speed <= 11111 )
- return dsp_set_sample(speed,stereo,bits,sign);
- else
- {
- #ifdef DEBUG_PROC
- printf("dsp_set_sample: speed %u %s%i %s\n",speed,stereo ? "stereo " : "", bits, sign ? "signed" : "unsigned");
- printf("dsp_set_record: failure\n");
- #endif
- return 0;
- }
-
- return 0;
- }
-
- unsigned dsp_set_sample(unsigned speed, int stereo, int bits, int sign)
- {
- int x;
- long test;
-
- stereo = !!stereo;
- sign = !!sign;
-
- #ifdef DEBUG_PROC
- printf("dsp_set_sample: speed %u %s%i %s\n",speed,stereo ? "stereo " : "", bits, sign ? "signed" : "unsigned");
- #endif
-
- dsp_pause_dma();
-
- if( !DMAStopped ) // Was a playback going?
- if( dsp_bits == 16 ) // Yes... 8 or 16 bit?
- dma_reset(SBdma16); // Reset 16 bit DMA
- else
- dma_reset(SBdma); // Reset 8 bit DMA
-
- disable();
- BufQueueHead = BufQueueTail = 0; // Reset DMA variables
- enable();
-
- dsp_speed = 0;
-
- if( sign && (SBtype < 4) ) // Check for card's capabilities.
- goto fail;
-
- dsp_signed = sign;
-
- if( speed < 4000 )
- goto fail;
-
- if( stereo != 0 )
- stereo = 1;
-
- if( stereo && (SBtype < 3) )
- goto fail;
-
- if( (SBtype < 4) && (bits != 8) )
- goto fail;
-
- if( (bits != 8) && (bits != 16 ) )
- goto fail;
-
- if( (speed > 22222) & (SBtype == 1) )
- goto fail;
-
- if( stereo && (SBtype == 3) )
- {
- test = speed * 2L;
-
- speed *= 2; // SBPro requires 2x speed for stereo.
- }
- else
- test = speed;
-
- if( test > 45454 )
- goto fail;
-
- dsp_set_speed(&speed); // Set speed.
-
- if( SBtype == 3 )
- {
- x = SbReadMixerReg(0x0E); // Set stereo control bit in mixer
- SbWriteMixerReg(0x0E,x & 0xFD);
-
- SbWriteMixerReg(0x0E,(x & 0xFD) + 2*stereo);
- }
-
- if( stereo && (SBtype == 3) )
- speed /= 2; // Real speed.
-
- dsp_speed = speed; // Set variables for IRQ routines.
- dsp_bits = bits;
- dsp_stereo = stereo;
-
- disable();
- DMAStopped = 1; // Reset DMA variables.
- DMABufsFull = 0;
- enable();
-
- fail:
- #ifdef DEBUG_PROC
- printf("dsp_set_playback: %s\n",speed > 0 ? "success" : "failure");
- #endif
-
- return dsp_speed; // Return true speed on success.
- }
-
- void dsp_set_speed(unsigned *speed)
- {
- static char time_const;
-
- unsigned speed0,speed1;
-
- if( *speed != 0 )
- {
- time_const = 0;
-
- if( SBtype >= 4 )
- {
- if( (*speed >= 44000) && (*speed < 44700) )
- {
- *speed = 44100;
- time_const = 1;
- }
-
- if( (*speed >= 22000) && (*speed < 22120) )
- {
- *speed = 22050;
- time_const = 2;
- }
-
- if( (*speed >= 11000) && (*speed < 11080) )
- {
- *speed = 11025;
- time_const = 3;
- }
- }
-
- if( !time_const )
- {
- if( SBtype >= 2 ) // Can we use high-speed DMA?
- {
- time_const = (char)((65536L-(256000000L / (long)*speed)) >> 8);
-
- speed0 = (256000000L/(65536L-((long)(time_const )<< 8)));
- speed1 = (256000000L/(65536L-((long)(time_const+1)<< 8)));
-
- if( (*speed - speed0) > (speed1 - *speed) )
- {
- time_const++;
- *speed = speed1;
- }
- else
- *speed = speed0;
-
- dsp_speed = *speed;
- dsp_hispeed = 1;
- }
- else
- {
- time_const = (char)(256-(1000000 / *speed));
-
- speed0 = 1000000 / (256-(time_const ));
- speed1 = 1000000 / (256-(time_const+1));
-
- if( (*speed - speed0) > (speed1 - *speed) )
- {
- time_const++;
- *speed = speed1;
- }
- else
- *speed = speed0;
-
- dsp_speed = *speed;
- dsp_hispeed = 0;
- }
- }
- }
-
- switch(time_const)
- {
-
- case 1:
- SbWriteByte(DSPSB16SetSpeed);
- SbWriteByte(172); // 44100 / 256
- SbWriteByte(68); // 44100 % 256
- break;
-
- case 2:
- SbWriteByte(DSPSB16SetSpeed);
- SbWriteByte(86); // 22050 / 256
- SbWriteByte(34); // 22050 % 256
- break;
-
- case 3:
- SbWriteByte(DSPSB16SetSpeed);
- SbWriteByte(43); // 11025 / 256
- SbWriteByte(17); // 11025 % 256
- break;
-
- default:
- SbWriteByte(DSPSetTimeConstant);
- SbWriteByteTimeout(time_const,SBTimeOut*5);
- break;
- }
-
- #ifdef DEBUG_PROC
- if( *speed )
- printf("dsp_set_speed: true speed %u time constant %i\n",*speed,(int)time_const);
- else
- printf("dsp_set_speed: resetting time constant %i\n",(int)time_const);
- #endif
- }
-
- void dsp_pause_dma(void)
- {
- SbWriteByte(DSPPauseDMA); // Issue Pause DMA command.
-
- #ifdef DEBUG_PROC
- printf("dsp_pause_dma\n");
- #endif
- }
-
- int dsp_continue_dma(void)
- {
- int x;
-
- disable();
- x = DMABufsFull;
- enable();
-
- if( x==0 )
- return 0; // We're not playing. Forget it.
-
- if( dsp_bits == 16 )
- SbWriteByte(DSPContinueDMA); // Kluge for 16-bits. [Shrug]
-
- SbWriteByte(DSPContinueDMA); // Issue Continue DMA command.
-
- #ifdef DEBUG_PROC
- printf("dsp_continue_dma\n");
- #endif
-
- return 1;
- }
-
- void *dsp_open_buf(void)
- {
-
- if( DMABufsFull == BufCount ) // Is the buffer full?
- {
- return NULL;
- }
- else
- {
- #ifdef DEBUG_WRITE
- printf("dsp_open_buf: success %p\n",DMABuffers[BufQueueTail]);
- #endif
- bufs_open = 1;
-
- return DMABuffers[BufQueueTail];
- }
- }
-
- void dsp_close_buf(void)
- {
-
- int start;
- int tail;
-
- unsigned speed_dummy = 0;
-
- if( !bufs_open )
- if( DMABufsFull && DMAStopped )
- {
- setvect(SBintnum,write_irq_handler); // Set up interrupts.
- DMAStopped = 0;
- goto start_playback;
- }
- else
- return;
-
- bufs_open = 0;
-
- disable();
-
- #ifdef DEBUG_WRITE
- printf("dsp_close_buf\n");
- #endif
-
- enable(); // No.
-
- disable();
- start = DMAStopped; // Find out if DMA is running currently
- enable();
-
- #ifdef AUTO_PREWRITE
- if( (start) && (BufQueueTail < (BufCount-1)) ) // If prewrite is
- {
-
- disable();
- BufQueueHead = 0; // Tell dsp_write we've done it.
- BufQueueTail++;
- DMABufsFull++;
- enable();
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,20016+BufQueueHead);
- poke(0xB800,156,20016+BufQueueTail);
- poke(0xB800,158,20016+DMABufsFull);
- #endif
- return;
- }
- #endif
-
- if( !start )
- {
- disable();
- BufQueueTail++; // Move tail to account for it
-
- if( BufQueueTail >= BufCount ) // Wrap tail
- BufQueueTail = 0;
-
- DMABufsFull++; // Mark another buffer as full.
- enable();
- }
- else
- {
- if( SBtype < 4 )
- dsp_speaker(1);
-
- disable(); // Set up DMA variables.
- BufQueueHead = 0;
- BufQueueTail++;
- DMABufsFull++;
- DMAStopped = 0;
-
- if( BufQueueTail >= BufCount ) // Wrap tail
- BufQueueTail = 0;
-
- enable();
-
- start_playback:
- if( SBtype >= 4 )
- {
- setvect(SBintnum,write_irq_handler); // Set up interrupts.
-
- dsp_reset(); // Reset DSP
- dsp_set_speed(&speed_dummy); // Set DMA speed.
-
- if( dsp_bits == 8 )
- {
- dma_reset(SBdma); // Setup 8-bit SB16 DMA
-
- SbWriteByte(DSPSB16Start8DMA);
- SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
-
- dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
- }
- else
- {
- dma_reset(SBdma16); // Setup 16-bit DMA
-
- SbWriteByte(DSPSB16Start16DMA);
- SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
- SbWriteByte(((BufLen-2)/2) % 256);
- SbWriteByte(((BufLen-2)/2) / 256);
-
- dma_setup(SBdma16,DMABufferBase,BufTotal-1,1);
- }
- }
- else
- {
- dma_reset(SBdma);
-
- if( dsp_stereo )
- {
- setvect(SBintnum,sbpro_bug_handler); // (grin)
-
- dma_setup(SBdma,DMABufferBase+1,0,1);
-
- SbWriteByte(DSPStartHSDMA); // To avoid bug in SBPro playback,
- SbWriteByte(0); // we have to play back a one-byte sample.
- SbWriteByte(0); // How stupid.
- }
- else
- {
- setvect(SBintnum,write_irq_handler); // Set up interrupts.
-
- if( GUS )
- dma_setup(SBdma,DMABuffers[0],BufLen-1,1);
- else
- dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
-
- if( dsp_hispeed )
- {
- SbWriteByte(DSPSetHSDMASize); // High speed mode
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
-
- SbWriteByte(DSPStartHSDMA);
- }
- else
- {
- SbWriteByte(DSPStartDMA); // Low speed mode
-
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
- }
- }
- }
- }
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,11824+BufQueueHead);
- poke(0xB800,156,11824+BufQueueTail);
- poke(0xB800,158,11823+DMABufsFull);
- #endif
-
- return;
- }
- int dsp_prewrite(void *buffer)
- {
- int start;
- int tail;
-
- #ifdef DEBUG_WRITE
- printf("dsp_prewrite: %p\n",buffer);
- #endif
-
- if( BufQueueTail == (BufCount-1) ) // Is there room?
- return 0; // No.
-
- disable();
- start = DMAStopped; // Are we running already?
- enable();
-
- if( !start )
- {
- return 0; // Yes.
- }
- else
- {
- memcpy(DMABuffers[BufQueueTail],buffer,BufLen); // Copy prewrite into buffer
-
- disable();
- BufQueueHead = 0; // Tell dsp_write we've done it.
- BufQueueTail++;
- DMABufsFull++;
- enable();
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,20016+BufQueueHead);
- poke(0xB800,156,20016+BufQueueTail);
- poke(0xB800,158,20016+DMABufsFull);
- #endif
- }
-
- return 1; // Success.
- }
-
- int dsp_write(void *buffer)
- {
- int start;
- int tail;
-
- unsigned speed_dummy = 0;
-
- if( buffer == NULL )
- if( DMABufsFull && DMAStopped )
- {
- setvect(SBintnum,write_irq_handler); // Set up interrupts.
- DMAStopped = 0;
- goto start_playback;
- }
- else
- return 0;
-
- disable();
- if( DMABufsFull == BufCount ) // Is the buffer full?
- {
- enable(); // Yes, can't write.
- return 0;
- }
-
- #ifdef DEBUG_WRITE
- printf("dsp_write: %p\n",buffer);
- #endif
-
- enable(); // No.
-
- disable();
- start = DMAStopped; // Find out if DMA is running currently
- enable();
-
- #ifdef AUTO_PREWRITE
- if( (start) && (BufQueueTail < (BufCount-1)) ) // If prewrite is
- {
- dsp_prewrite(buffer); // requested, do it.
- return 1;
- }
- #endif
-
- if( !start )
- {
- memcpy(DMABuffers[BufQueueTail],buffer,BufLen); // Copy data into DMA buffer
-
- disable();
- BufQueueTail++; // Move tail to account for it
-
- if( BufQueueTail >= BufCount ) // Wrap tail
- BufQueueTail = 0;
-
- DMABufsFull++; // Mark another buffer as full.
- enable();
- }
- else
- {
- if( SBtype < 4 )
- dsp_speaker(1);
-
- memcpy(DMABuffers[BufQueueTail],buffer,BufLen); // Copy data into DMA buffer
-
- disable(); // Set up DMA variables.
- BufQueueHead = 0;
- BufQueueTail++;
- DMABufsFull++;
- DMAStopped = 0;
-
- if( BufQueueTail >= BufCount ) // Wrap tail
- BufQueueTail = 0;
-
- enable();
-
- start_playback: // Go here to start DMA, no questions
- if( SBtype >= 4 )
- {
- setvect(SBintnum,write_irq_handler); // Set up interrupts.
-
- dsp_reset(); // Reset DSP
- dsp_set_speed(&speed_dummy); // Set DMA speed.
-
- if( dsp_bits == 8 )
- {
- dma_reset(SBdma); // Setup 8-bit SB16 DMA
-
- SbWriteByte(DSPSB16Start8DMA);
- SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
-
- dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
- }
- else
- {
- dma_reset(SBdma16); // Setup 16-bit DMA
-
- SbWriteByte(DSPSB16Start16DMA);
- SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
- SbWriteByte(((BufLen-2)/2) % 256);
- SbWriteByte(((BufLen-2)/2) / 256);
-
- dma_setup(SBdma16,DMABufferBase,BufTotal-1,1);
- }
- }
- else
- {
- dma_reset(SBdma);
-
- if( dsp_stereo )
- {
- setvect(SBintnum,sbpro_bug_handler); // (grin)
-
- dma_setup(SBdma,DMABufferBase+1,0,1);
-
- SbWriteByte(DSPSetHSDMASize); // To avoid bug in SBPro playback,
- SbWriteByte(0); // we have to play back a one-byte sample.
- SbWriteByte(0); // How stupid.
-
- SbWriteByte(DSPStartHSDMA);
- }
- else
- {
- setvect(SBintnum,write_irq_handler); // Set up interrupts.
-
- if( GUS )
- dma_setup(SBdma,DMABuffers[0],BufLen-1,1);
- else
- dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
-
- if( dsp_hispeed )
- {
- SbWriteByte(DSPSetHSDMASize); // High speed mode
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
-
- SbWriteByte(DSPStartHSDMA);
- }
- else
- {
- SbWriteByte(DSPStartDMA); // Low speed mode
-
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
- }
- }
- }
- }
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,11824+BufQueueHead);
- poke(0xB800,156,11824+BufQueueTail);
- poke(0xB800,158,11823+DMABufsFull);
- #endif
-
- return 1;
- }
-
- int dsp_read(void *ptr)
- {
- unsigned speed_dummy = 0;
-
- #ifdef DEBUG_WRITE
- printf("dsp_read: %p\n",ptr);
- #endif
-
- if( ptr == NULL )
- {
- setvect(SBintnum,null_irq_handler);
-
- dma_reset(dsp_bits == 16 ? SBdma16 : SBdma);
-
- if( SBtype == 3 )
- SbWriteByte(DSPSBProADCMono);
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,7*256+32);
- poke(0xB800,156,7*256+32);
- poke(0xB800,158,7*256+32);
- #endif
-
- return 4;
- }
-
- if( DMAStopped )
- {
- dsp_speaker(0);
-
- if( SBtype == 3 )
- if( dsp_stereo )
- {
- SbWriteByte(DSPSBProADCStereo);
- SbWriteMixerReg(0x0E,(SbReadMixerReg(0x0E)&0xFD));
-
- SbWriteMixerReg(0x0C,(SbReadMixerReg(0x0C)|(0x20)));
- }
- else
- {
- SbWriteByte(DSPSBProADCMono);
- SbWriteMixerReg(0x0E,SbReadMixerReg(0x0E)&0xFD);
-
- if( dsp_speed <= 8000 )
- SbWriteMixerReg(0x0C,SbReadMixerReg(0x0C)&(0xDF));
- else
- SbWriteMixerReg(0x0C,SbReadMixerReg(0x0C)|(0x20));
- }
-
- BufQueueHead = 0;
- BufQueueTail = 0;
- DMABufsFull = 0;
- DMAStopped = 0;
-
- dsp_overrun = 0;
-
- setvect(SBintnum,read_irq_handler);
-
- if( SBtype >= 4 )
- {
- dsp_reset();
- dsp_set_speed(&speed_dummy);
-
- if( dsp_bits == 8 )
- {
- SbWriteByte(DSPSB16Start8ADCDMA);
- SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
- }
- else
- {
- SbWriteByte(DSPSB16Start16ADCDMA);
- SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
- SbWriteByte(((BufLen-2)/2) % 256);
- SbWriteByte(((BufLen-2)/2) / 256);
- }
- dma_setup(dsp_bits == 16 ? SBdma16 : SBdma,DMABufferBase,BufTotal-1,0);
- }
- else
- {
- if( dsp_hispeed )
- {
- SbWriteByte(DSPSetHSDMASize); // High speed mode
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
-
- SbWriteByte(DSPStartHSADCDMA);
- }
- else
- {
- SbWriteByte(DSPStartADCDMA); // Low speed mode
- SbWriteByte((BufLen-1) % 256);
- SbWriteByte((BufLen-1) / 256);
- }
-
- dma_setup(SBdma,DMABufferBase,BufTotal-1,0);
- }
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,11824+BufQueueHead);
- poke(0xB800,156,11824+BufQueueTail);
- poke(0xB800,158,11824+DMABufsFull);
- #endif
-
- return 3;
- }
- else
- if( BufQueueHead == BufQueueTail )
- return 2;
- else
- {
- memcpy(ptr,DMABuffers[BufQueueHead],BufLen);
-
- disable();
- BufQueueHead = (BufQueueHead+1)%BufCount;
- enable();
-
- DMABufsFull--;
-
- #ifdef DEBUG_DMASTAT
- poke(0xB800,154,11824+BufQueueHead);
- poke(0xB800,156,11824+BufQueueTail);
- poke(0xB800,158,11824+DMABufsFull);
- #endif
-
- if( dsp_overrun > 0 )
- {
- dsp_overrun = 0;
- return 1;
- }
- else
- return 0;
- }
- }
-
-
- int sb_get_params(int *port, int *dma, int *irq, int *dma16)
- {
- char *t, *t1, *blaster;
-
- /* Set arguments to reasonable values (Soundblaster defaults) */
- *port = 0x220;
- *irq = 5;
- *dma = 1;
- *dma16 = 5;
-
- /* Attempt to read environment variable */
- t = getenv("BLASTER");
-
- /* Is the environment variable set? */
- if(t == NULL)
- return 0;
-
- /* Duplicate the string so that we don't trash our environment */
- blaster = strdup(t);
-
- /* Now parse the BLASTER variable */
- t = strtok(blaster," \t");
-
- while(t)
- {
- switch(toupper(t[0]))
- {
- case 'A': /* I/O address */
- *port = (int)strtol(t+1,&t1,16);
- break;
-
- case 'I': /* Hardware IRQ */
- *irq = atoi(t+1);
- break;
-
- case 'D': /* DMA channel */
- *dma = atoi(t+1);
- break;
-
- case 'H':
- *dma16 = atoi(t+1);
- break;
- }
- t = strtok(NULL," \t");
- }
-
- #ifdef DEBUG_PROC
- printf("sb_get_params: A%3X I%i D%i H%i\n",*port,*irq,*dma,*dma16);
- #endif
-
- free(blaster);
- return 1;
- }
-
- int mix_reset(void)
- {
- SbWriteMixerReg(0,0);
-
- return !!(SbReadMixerReg(4));
- }
-
- unsigned char mix_read(int device, int channel)
- {
- int r1;
- int ll=0,lr=0;
-
- if( device > 8 )
- channel = SBtype >= 4 ? MIXleft : MIXright;
-
- if( SBtype <= 3 && SBProMixRegs[device] )
- {
- r1 = SbReadMixerReg(SBProMixRegs[device]); // Read register
-
- ll = (r1 & 0xF0); // Left channel: High nibble
- lr = (r1 & 0x0F) << 4; // Right channel: Low nibble
- }
-
- if( SBtype >= 4 && SB16MixRegs[device] )
- {
- ll = SbReadMixerReg(SB16MixRegs[device]); // Given byte: Left
- lr = SbReadMixerReg(SB16MixRegs[device]+1); // Byte+1: Right
- }
-
- if( (SBtype <= 3) && (device == MIXmicrophone) )
- lr <<= 1;
-
- #ifdef DEBUG_PROC
- printf("mix_read: %i %i (%i %i)\n",device,channel,ll,lr);
- #endif
-
- switch( channel )
- {
- case MIXleft: return ll;
- case MIXright: return lr;
- case MIXboth: return (ll+lr)/2;
- }
-
- return 0;
- }
-
- void mix_write(int device, int channel, unsigned char level)
- {
- int r1,r2;
-
- #ifdef DEBUG_PROC
- printf("mix_write: device %i channel %i level %i\n",device,channel,(int)level);
- #endif
-
- if( device > 8 )
- channel = SBtype >= 4 ? MIXleft : MIXright;
-
- if( (SBtype <= 3) && (device == MIXmicrophone) )
- level >>= 1;
-
- if( SBtype <= 3 && SBProMixRegs[device] ) // Soundblaster Pro shares each register between two channels
- {
- r1 = SbReadMixerReg(SBProMixRegs[device]);
-
- switch( channel ) // We have to set the correct part of the byte.
- {
- case MIXright: r2 = (r1 & 0xF0) + (level>>4); break;
- case MIXleft: r2 = (r1 & 0x0F) + (level & 0xF0); break;
- case MIXboth: r2 = (level & 0xF0) + (level>>4); break;
- }
-
- SbWriteMixerReg(SBProMixRegs[device],r2);
- }
-
- if( SBtype >= 4 && SB16MixRegs[device] ) // Soundblaster 16 uses register per channel
- switch( channel ) // Makes things a bit easier, no?
- {
- case MIXleft: SbWriteMixerReg(SB16MixRegs[device] ,level); break;
- case MIXright: SbWriteMixerReg(SB16MixRegs[device]+1,level); break;
- case MIXboth: SbWriteMixerReg(SB16MixRegs[device] ,level);
- SbWriteMixerReg(SB16MixRegs[device]+1,level); break;
- }
- }
-
-
- void mix_set_sb16_output(int value)
- {
- #ifdef DEBUG_PROC
- printf("mix_set_sb16_output: %i\n",value);
- #endif
-
- SbWriteMixerReg(0x3C,value); // Send output value raw :)
- }
-
- void mix_set_sb16_input(int channel, int value)
- {
- #ifdef DEBUG_PROC
- printf("mix_set_sb16_value: channel %i value %i\n",channel,value);
- #endif
-
- switch( channel )
- {
- case MIXboth: SbWriteMixerReg(0x3E,value);
- case MIXleft: SbWriteMixerReg(0x3D,value); break;
- case MIXright: SbWriteMixerReg(0x3E,value); break;
- }
- }
-
- void mix_set_input(int value)
- {
- int i;
-
- #ifdef DEBUG_PROC
- printf("mix_set_input: %x",value);
- #endif
-
- SbReadMixerReg(0x0C); // Read input control byte
-
- /* bit: 7 6 5 4 3 2 1 0 F=frequency (0=low, 1=high)
- x x T x F S S x SS=source (00=MIC, 01=CD, 11=LINE)
- T=input filter switch (ANFI) */
-
- // i &= 0xF9;
- i |= 0x20;
-
- switch( value )
- {
- case 0x18: i |= 6; break;
- case 0x06: i |= 2; break;
- case 0x01: break;
- }
-
-
- SbWriteMixerReg(0x0C,i); // Write input control byte
- }
-