2000 /* SBIO.C */ /* Soundblaster 16 basic audio I/O functions */ /* Copyright 1995 by Ethan Brodsky. All rights reserved */ /* Modified extensively by Philip VanBaren to suit my purposes */ /* Ethan Brodsky's original SB16 sampling code may be found at: */ /* ftp://oak.oakland.edu/simtel/msdos/sound/sb16snd.zip */ /* Interface variables that can be changed in the background */ volatile int sb16dmarunning; volatile char curblock; #include #include #include #include #include #include "sbio.h" #include "freq.h" /* Needed for DOUT() definition only */ #define lo(value) (unsigned char)((value) & 0x00FF) #define hi(value) (unsigned char)((value) >> 8) int mixerport; int mixdataport; int resetport; int readport; int writeport; int pollport; int poll16port; int pic_rotateport; int pic_maskport; int dma_maskport; int dma_clrptrport; int dma_modeport; int dma_baseaddrport; int dma_countport; int dma_pageport; char irq_startmask; char irq_stopmask; char irq_intvector; char int_controller; char dma_startmask; char dma_stopmask; char dma_mode; /* This function is defined in sc_sb16.c */ extern void interrupt sb16_callback(void); void interrupt (*oldintvector)() = NULL; int handlerinstalled; void far *dmabuffer = NULL; /* Twice the size of the output buffer */ int far *dmaptr = NULL; /* Pointer to the used portion */ unsigned long buf_addr; /* 16-bit addressing */ unsigned char buf_page; unsigned int buf_ofs; mode iomode; /* Flags input or output mode */ /* Low level sound card I/O */ void write_dsp(unsigned char value) { while (inp(writeport) & 0x80); /* Wait for bit 7 to be cleared */ outp(writeport, value); } unsigned char read_dsp(void) { long timeout=1000000L; /* Wait for bit 7 to be set, or for a timeout */ while((!(inp(pollport) & 0x80)) && (--timeout)); return inp(readport); } int reset_dsp(void) { int i; sb16dmarunning=0; outp(resetport, 1); outp(resetport, 0); i = 100; while ((read_dsp() != 0xAA) && i--); return i; } /* Convert a far pointer to a linear address, for DMA addressing */ #define getlinearaddr(p) ((unsigned long)FP_SEG(p)*16 + (unsigned long)FP_OFF(p)) /* Initialization and shutdown */ void installhandler(void) { /* Install the interrupt handler */ disable(); /* Disable interrupts */ outp(pic_maskport, (inp(pic_maskport)|irq_stopmask)); /* Mask IRQ */ oldintvector = getvect(irq_intvector); /* Save old vector */ setvect(irq_intvector, sb16_callback); /* Install new handler */ outp(pic_maskport, (inp(pic_maskport)&irq_startmask)); /* Unmask IRQ */ enable(); /* Reenable interupts */ handlerinstalled = 1; } void uninstallhandler(void) { sb16dmarunning=0; disable(); /* Disable interrupts */ outp(pic_maskport, (inp(pic_maskport)|irq_stopmask)); /* Mask IRQ */ setvect(irq_intvector, oldintvector); /* Restore old vector */ enable(); /* Enable interrupts */ handlerinstalled = 0; } /* This function is run at program exit to ensure cleanup */ void sb_exitproc(void) { sb16dmarunning=0; outp(0x20, 0x20); outp(0xA0, 0x20); /* Acknowledge any hanging ints */ write_dsp(0xD5); /* Stop digitized sound xfer */ outp(dma_maskport, dma_stopmask); /* Mask DMA channel */ if (handlerinstalled) uninstallhandler(); /* Uninstall int handler */ reset_dsp(); /* Reset SB DSP */ } /* * Initialize the Soundblaster-16 card with the settings: * baseio: base IO address for the card * irq: IRQ channel used by the card * dma16: 16-bit DMA channel used by the card * io: sampling mode, either "input" or "output" * length: maximum block size to be used, in samples * (this is used for allocating the DMA buffer) */ int init_sb(int baseio, char irq, char dma16, mode io, unsigned int length) { int val,onboard; /* Sound card IO ports */ mixerport = baseio + 0x004; mixdataport = baseio + 0x005; resetport = baseio + 0x006; readport = baseio + 0x00A; writeport = baseio + 0x00C; pollport = baseio + 0x00E; poll16port = baseio + 0x00F; /* Reset DSP */ sb16dmarunning=0; if (!reset_dsp()) return 0; /* Verify that we have a SB-16 at this address */ write_dsp(0xE1); val=read_dsp(); /* Get the major version number */ read_dsp(); /* Grab the minor version number also */ if(val<4) return 0; /* Check the current IRQ and DMA settings, and compare with * the values passed to this routine */ outportb(mixerport,0x80); /* IRQ settings */ val=inportb(mixdataport); onboard=-1; if(val&0x01) { onboard=2; DOUT("SB16: IRQ2 enabled"); } if(val&0x02) { onboard=5; DOUT("SB16: IRQ5 enabled"); } if(val&0x04) { onboard=7; DOUT("SB16: IRQ7 enabled"); } if(val&0x08) { onboard=10; DOUT("SB16: IRQ10 enabled"); } if(onboard==-1) { puts("Error: Soundblaster has no IRQ enabled, aborting ..."); return(0); } switch(irq) { case 2: if(!(val&0x01)) { irq=-1; } break; case 5: if(!(val&0x02)) { irq=-1; } break; case 7: if(!(val&0x04)) { irq=-1; } break; case 10: if(!(val&0x08)) { irq=-1; } break; default: irq=-1; } if(irq==-1) { printf("Warning: Soundblaster has IRQ%d selected, using that value instead\n",onboard); irq=onboard; } outportb(mixerport,0x81); /* DMA settings */ val=inportb(mixdataport); onboard=-1; if(val&0x01) { /*onboard=0;*/ DOUT("SB16: DMA0 enabled"); } if(val&0x02) { /*onboard=1;*/ DOUT("SB16: DMA1 enabled"); } if(val&0x08) { /*onboard=3;*/ DOUT("SB16: DMA3 enabled"); } if(val&0x20) { onboard=5; DOUT("SB16: DMA5 enabled"); } if(val&0x40) { onboard=6; DOUT("SB16: DMA6 enabled"); } if(val&0x80) { onboard=7; DOUT("SB16: DMA7 enabled"); } if(onboard==-1) { puts("Error: Soundblaster has no 16-bit DMA enabled, aborting ..."); return(0); } switch(dma16) { // case 0: if(!(val&0x01)) { dma16=-1; } break; // case 1: if(!(val&0x02)) { dma16=-1; } break; // case 3: if(!(val&0x08)) { dma16=-1; } break; case 5: if(!(val&0x20)) { dma16=-1; } break; case 6: if(!(val&0x40)) { dma16=-1; } break; case 7: if(!(val&0x80)) { dma16=-1; } break; default: dma16=-1; } if(dma16==-1) { printf("Warning: Soundblaster has DMA%d selected, using that value instead\n",onboard); dma16=onboard; } #ifdef DEBUG_OUTPUT /* Check if the IRQs are enabled */ outportb(mixerport,0x82); /* IRQ status */ val=inportb(mixdataport); if(val&0x01) { DOUT("SB16: 8-bit IRQ active"); } if(val&0x02) { DOUT("SB16: 16-bit IRQ active"); } if(val&0x04) { DOUT("SB16: MPU-401 IRQ active"); } #endif /* These two lines are strictly a kludge for freq.exe */ outportb(mixerport,0x3d); /* Input control */ outportb(mixdataport,0x7f); /* Use all channels for input */ /* Compute interrupt ports and parameters */ if (irq < 8) { int_controller = 1; pic_rotateport = 0x20; pic_maskport = 0x21; irq_intvector = 0x08 + irq; } else { int_controller = 2; pic_rotateport = 0xA0; pic_maskport = 0x21; irq_intvector = 0x70 + irq-8; } irq_stopmask = 1 << (irq % 8); irq_startmask = ~irq_stopmask; /* Compute DMA ports and parameters */ dma_maskport = 0xD4; dma_clrptrport = 0xD8; dma_modeport = 0xD6; dma_baseaddrport = 0xC0 + 4*(dma16-4); dma_countport = 0xC2 + 4*(dma16-4); switch(dma16) { case 5: dma_pageport = 0x8B; break; case 6: dma_p 14a1 ageport = 0x89; break; case 7: dma_pageport = 0x8A; break; } dma_stopmask = dma16-4 + 0x04; /* 000001xx */ dma_startmask = dma16-4 + 0x00; /* 000000xx */ /* Allocate a buffer for DMA transfer */ /* (need a block of memory that does not cross a page boundary) */ if ((dmabuffer = farmalloc(8*length)) == NULL) { puts("Unable to allocate DMA buffer."); exit(1); } dmaptr = (int far *)dmabuffer; if(((getlinearaddr(dmabuffer) >> 1) % 65536L) + length*2 > 65536L) dmaptr += 2*length; /* Pick second half to avoid crossing boundary */ /* Compute DMA parameters */ buf_addr = getlinearaddr(dmaptr); buf_page = (unsigned int)(buf_addr >> 16); buf_ofs = (unsigned int)((buf_addr >> 1) % 65536L); /* Other initialization */ iomode = io; switch (iomode) { case input: dma_mode = dma16-4 + 0x54; break; /* 010101xx */ case output: dma_mode = dma16-4 + 0x58; break; /* 010110xx */ } installhandler(); /* Install interrupt handler */ atexit(sb_exitproc); /* Install exit procedure */ return 1; } /* * Shut down the sampling process, clean up the interrupts, * and free up the memory allocation */ void shutdown_sb(void) { sb16dmarunning=0; reset_dsp(); if(handlerinstalled) uninstallhandler(); farfree(dmabuffer); } /* * Start continuous I/O at a rate of {rate} Hz, with a call to the * callback_sb16 procedure after every block of length {length} has * been finished. The sampling input or output is done on alternating * blocks pointed to by (dmaptr+curblock*length). * i.e. When curblock is 0, the current block is (dmaptr). * When curblock is 1, the current block is (dmaptr+length) * The variable curblock is maintained in the callback_sb16 routine. */ void startio(unsigned int rate, unsigned long length) { sb16dmarunning = 1; curblock = 0; /* Program DMA controller */ outp(dma_maskport, dma_stopmask); outp(dma_clrptrport, 0x00); outp(dma_modeport, dma_mode); outp(dma_baseaddrport, lo(buf_ofs)); /* Low byte of offset */ outp(dma_baseaddrport, hi(buf_ofs)); /* High word of offset */ outp(dma_countport, lo(length*2-1)); /* Low byte of count */ outp(dma_countport, hi(length*2-1)); /* High byte of count */ outp(dma_pageport, buf_page); outp(dma_maskport, dma_startmask); /* Program sound card */ switch (iomode) { case input: write_dsp(0x42); break; /* Set input sampling rate */ case output: write_dsp(0x41); break; /* Set output sampling rate */ } write_dsp(hi(rate)); /* High byte of sampling rate */ write_dsp(lo(rate)); /* Low byte of sampling rate */ switch (iomode) { case output: write_dsp(0xB6); break; /* 16-bit D->A, A/I, FIFO */ case input: write_dsp(0xBE); break; /* 16-bit A->D, A/I, FIFO */ } write_dsp(0x10); /* DMA Mode: 16-bit signed mono */ write_dsp(lo(length-1)); /* Low byte of block length */ write_dsp(hi(length-1)); /* High byte of block length */ } /* * Stops the current sampling process. */ void stopio(void) { outp(0x20, 0x20); outp(0xA0, 0x20); /* Acknowledge any hanging ints */ write_dsp(0xD9); /* Stop digitized sound xfer */ outp(dma_maskport, dma_stopmask); /* Mask DMA channel */ sb16dmarunning=0; reset_dsp(); /* Reset SB DSP */ } /* Mixer setting and reading functions */ void set_cd_level(unsigned int level) { level=level*256/100; if(level>255) level=255; outportb(mixerport,0x36); /* CD Audio left */ outportb(mixdataport,level); outportb(mixerport,0x37); /* CD Audio right */ outportb(mixdataport,level); } void set_mic_level(unsigned int level) { level=level*256/100; if(level>255) level=255; outportb(mixerport,0x3A); /* Microphone */ outportb(mixdataport,level); } void set_line_level(unsigned int level) { level=level*256/100; if(level>255) level=255; outportb(mixerport,0x38); /* Line In left */ outportb(mixdataport,level); outportb(mixerport,0x39); /* Line In right */ outportb(mixdataport,level); } unsigned int get_cd_level(void) { unsigned int level; outportb(mixerport,0x36); /* CD Audio left */ level=inportb(mixdataport); outportb(mixerport,0x37); /* CD Audio right */ level+=inportb(mixdataport); level=level*50/256; if(level>100) level=100; return(level); } unsigned int get_mic_level(void) { unsigned int level; outportb(mixerport,0x3A); /* Microphone */ level=inportb(mixdataport); level=level*100/256; if(level>100) level=100; return(level); } unsigned int get_line_level(void) { unsigned int level; outportb(mixerport,0x38); /* Line In left */ level=inportb(mixdataport); outportb(mixerport,0x39); /* Line In right */ level+=inportb(mixdataport); level=level*50/256; if(level>100) level=100; return(level); } . 0