X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=blobdiff_plain;f=libs%2Foldmik%2Fsrc%2Fdrv_ss.c;fp=libs%2Foldmik%2Fsrc%2Fdrv_ss.c;h=4d0ff2674bb2640e8f6c4c3934c4050e90bddd4f;hp=0000000000000000000000000000000000000000;hb=77db1ca18d5446dcda9e524261399b63c2cd1813;hpb=a714b8c4811627d874934b0a0387b8cb27fc5921 diff --git a/libs/oldmik/src/drv_ss.c b/libs/oldmik/src/drv_ss.c new file mode 100644 index 0000000..4d0ff26 --- /dev/null +++ b/libs/oldmik/src/drv_ss.c @@ -0,0 +1,714 @@ +/* + +Name: +DRV_SS.C + +Description: +Mikmod driver for output on Ensoniq Soundscape / Soundscape ELITE + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include +#include +#include +#include + +#include "mikmod.h" +#include "mdma.h" +#include "mirq.h" + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SS stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +/* Ensoniq gate-array chip defines ... */ + +#define ODIE 0 /* ODIE gate array */ +#define OPUS 1 /* OPUS gate array */ +#define MMIC 2 /* MiMIC gate array */ + +/* relevant direct register defines - offsets from base address */ +#define GA_HOSTCTL_OFF 2 /* host port ctrl/stat reg */ +#define GA_ADDR_OFF 4 /* indirect address reg */ +#define GA_DATA_OFF 5 /* indirect data reg */ +#define GA_CODEC_OFF 8 /* for some boards CoDec is fixed from base */ + +/* relevant indirect register defines */ +#define GA_DMAB_REG 3 /* DMA chan B assign reg */ +#define GA_INTCFG_REG 4 /* interrupt configuration reg */ +#define GA_DMACFG_REG 5 /* DMA configuration reg */ +#define GA_CDCFG_REG 6 /* CD-ROM/CoDec config reg */ +#define GA_HMCTL_REG 9 /* host master control reg */ + + +/* AD-1848 or compatible CoDec defines ... */ +/* relevant direct register defines - offsets from base */ +#define CD_ADDR_OFF 0 /* indirect address reg */ +#define CD_DATA_OFF 1 /* indirect data reg */ +#define CD_STATUS_OFF 2 /* status register */ + +/* relevant indirect register defines */ +#define CD_ADCL_REG 0 /* left DAC input control reg */ +#define CD_ADCR_REG 1 /* right DAC input control reg */ +#define CD_CDAUXL_REG 2 /* left DAC output control reg */ +#define CD_CDAUXR_REG 3 /* right DAC output control reg */ +#define CD_DACL_REG 6 /* left DAC output control reg */ +#define CD_DACR_REG 7 /* right DAC output control reg */ +#define CD_FORMAT_REG 8 /* clock and data format reg */ +#define CD_CONFIG_REG 9 /* interface config register */ +#define CD_PINCTL_REG 10 /* external pin control reg */ +#define CD_UCOUNT_REG 14 /* upper count reg */ +#define CD_LCOUNT_REG 15 /* lower count reg */ +#define CD_XFORMAT_REG 28 /* extended format reg - 1845 record */ +#define CD_XUCOUNT_REG 30 /* extended upper count reg - 1845 record */ +#define CD_XLCOUNT_REG 31 /* extended lower count reg - 1845 record */ + +#define CD_MODE_CHANGE 0x40 /* mode change mask for addr reg */ + +/**************************************************************************** +hardware config info ... +****************************************************************************/ + +static UWORD BasePort; /* Gate Array/MPU-401 base port */ +static UWORD MidiIrq; /* the MPU-401 IRQ */ +static UWORD WavePort; /* the AD-1848 base port */ +static UWORD WaveIrq; /* the PCM IRQ */ +static UWORD DmaChan; /* the PCM DMA channel */ + +/**************************************************************************** +all kinds of stuff ... +****************************************************************************/ + +static UWORD Windx; /* Wave IRQ index - for reg writes */ +static UWORD Mindx; /* MIDI IRQ index - for reg writes */ + +static UBYTE IcType; /* the Ensoniq chip type */ +static UBYTE CdCfgSav; /* gate array register save area */ +static UBYTE DmaCfgSav; /* gate array register save area */ +static UBYTE IntCfgSav; /* gate array register save area */ + +static UWORD const SsIrqs[4] = { 9, 5, 7, 10 }; /* Soundscape IRQs */ +static UWORD const RsIrqs[4] = { 9, 7, 5, 15 }; /* an older IRQ set */ +static UWORD const *Irqs; /* pointer to one of the IRQ sets */ + +static UBYTE DacSavL; /* DAC left volume save */ +static UBYTE DacSavR; /* DAC right volume save */ +static UBYTE CdxSavL; /* CD/Aux left volume save */ +static UBYTE CdxSavR; /* CD/Aux right volume save */ +static UBYTE AdcSavL; /* ADC left volume save */ +static UBYTE AdcSavR; /* ADC right volume save */ + +static DMAMEM *SS_DMAMEM; +static char *SS_DMABUF; + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> The actual SS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + + + +UBYTE GaRead(UWORD rnum) +/* + This function is used to read the indirect addressed registers in the + Ensoniq Soundscape gate array. + + INPUTS: + rnum - the numner of the indirect register to be read + + RETURNS: + the contents of the indirect register are returned +*/ +{ + outportb(BasePort + GA_ADDR_OFF, rnum); + return inportb(BasePort + GA_DATA_OFF); +} + + + +void GaWrite(UWORD rnum,UBYTE value) +/* + This function is used to write the indirect addressed registers in the + Ensoniq Soundscape gate array. + + INPUTS: + rnum - the numner of the indirect register to be read + value - the byte value to be written to the indirect register + + RETURNS: + Nothing +*/ +{ + outportb(BasePort + GA_ADDR_OFF, rnum); + outportb(BasePort + GA_DATA_OFF, value); +} + + +UBYTE CdRead(UWORD rnum) +/* + This function is used to read the indirect addressed registers in the + AD-1848 or compatible CoDec. It will preserve the special function bits + in the upper-nibble of the indirect address register. + + INPUTS: + rnum - the numner of the indirect register to be read + + RETURNS: + the contents of the indirect register are returned + +*/ +{ + outportb(WavePort + CD_ADDR_OFF, + (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum); + return inportb(WavePort+CD_DATA_OFF); +} + + + +void CdWrite(UWORD rnum,UBYTE value) +/* + This function is used to write the indirect addressed registers in the + Ad-1848 or compatible CoDec. It will preserve the special function bits + in the upper-nibble of the indirect address register. + + INPUTS: + rnum - the numner of the indirect register to be read + value - the byte value to be written to the indirect register + + RETURNS: + Nothing +*/ +{ + outportb(WavePort + CD_ADDR_OFF, + (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum); + outportb(WavePort + CD_DATA_OFF, value); +} + + + + +void SetDacVol(UBYTE lvol,UBYTE rvol) +/* + This function sets the left and right DAC output level in the CoDec. + + INPUTS: + lvol - left volume, 0-127 + rvol - right volume, 0-127 + + RETURNS: + Nothing + +*/ +{ + CdWrite(CD_DACL_REG, ~(lvol >> 1) & 0x3f); + CdWrite(CD_DACR_REG, ~(rvol >> 1) & 0x3f); +} + + + +void SetCdRomVol(UBYTE lvol,UBYTE rvol) +/* + This function sets the left and right CD-ROM output level in the CoDec. + + INPUTS: + lvol - left volume, 0-127 + rvol - right volume, 0-127 + + RETURNS: + Nothing +*/ +{ + CdWrite(CD_CDAUXL_REG, ~(lvol >> 2) & 0x1f); + CdWrite(CD_CDAUXR_REG, ~(rvol >> 2) & 0x1f); +} + + +void SetAdcVol(UBYTE lvol,UBYTE rvol) +/* + This function sets the left and right ADC input level in the CoDec. + + INPUTS: + lvol - left volume, 0-127 + rvol - right volume, 0-127 + + RETURNS: + Nothing + +*/ +{ + CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0xf0) | (lvol & 0x7f) >> 3); + CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0xf0) | (rvol & 0x7f) >> 3); +} + + +void StopCoDec(void) +{ + UWORD i; + + CdWrite(CD_CONFIG_REG,CdRead(CD_CONFIG_REG)&0xfc); + + /* Let the CoDec receive its last DACK(s). The DMAC must not be */ + /* masked while the CoDec has DRQs pending. */ +/* for(i=0; i<256; ++i ) + if(!(inportb(DmacRegP->status) & (0x10 << DmaChan))) break; +*/ +} + + + +BOOL GetConfigEntry(char *entry, char *dest, FILE *fp) +/* + This function parses a file (SNDSCAPE.INI) for a left-hand string and, + if found, writes its associated right-hand value to a destination buffer. + This function is case-insensitive. + + INPUTS: + fp - a file pointer to the open SNDSCAPE.INI config file + dst - the destination buffer pointer + lhp - a pointer to the right-hand string + + RETURNS: + 1 - if successful + 0 - if the right-hand string is not found or has no equate +*/ +{ + char static str[83]; + char static tokstr[33]; + char *p; + + /* make a local copy of the entry, upper-case it */ + strcpy(tokstr, entry); + strupr(tokstr); + + /* rewind the file and try to find it ... */ + rewind(fp); + + for( ;; ) { + /* get the next string from the file */ + + fgets(str, 83, fp); + if(feof(fp)) return 0; + + /* properly terminate the string */ + for( p = str; *p != '\0'; ++p ) { + if( *p == ' ' || *p == '\t' || *p == 0x0a || *p == 0x0d ) { + *p = '\0'; + break; + } + } + + /* see if it's an 'equate' string; if so, zero the '=' */ + if( !(p = strchr(str, '=')) ) continue; + *p = '\0'; + + /* upper-case the current string and test it */ + strupr(str); + if( strcmp(str, tokstr) ) + continue; + + /* it's our string - copy the right-hand value to buffer */ + for( p = str + strlen(str) + 1; (*dest++ = *p++) != '\0'; ); + break; + } + return 1; +} + + +static BOOL SS_IsThere(void) +{ + static char str[78]; + char *envptr; + FILE *fp; + UBYTE tmp; + + if((envptr=getenv("SNDSCAPE"))==NULL) return 0; + + strcpy(str, envptr); + if( str[strlen(str) - 1] == '\\' ) + str[strlen(str) - 1] = '\0'; + + strcat(str, "\\SNDSCAPE.INI"); + + if(!(fp=fopen(str, "r"))) return 0; + + /* read all of the necessary config info ... */ + if(!GetConfigEntry("Product",str,fp)){ + fclose(fp); + return 0; + } + + /* if an old product name is read, set the IRQs accordingly */ + strupr(str); + if(strstr(str,"SOUNDFX") || strstr(str,"MEDIA_FX")) + Irqs = RsIrqs; + else + Irqs = SsIrqs; + + if(!GetConfigEntry("Port", str, fp)){ + fclose(fp); + return 0; + } + + BasePort=strtol(str,NULL,16); + + if(!GetConfigEntry("WavePort",str,fp)){ + fclose(fp); + return 0; + } + + WavePort=strtol(str,NULL,16); + + if(!GetConfigEntry("IRQ",str,fp)){ + fclose(fp); + return 0; + } + + MidiIrq=strtol(str,NULL,10); + if(MidiIrq==2) MidiIrq = 9; + + if(!GetConfigEntry("SBIRQ",str,fp)){ + fclose(fp); + return 0; + } + + WaveIrq=strtol(str,NULL,10); + if(WaveIrq==2) WaveIrq=9; + + if(!GetConfigEntry("DMA",str,fp)){ + fclose(fp); + return 0; + } + + DmaChan=strtol(str,NULL,10); + + fclose(fp); + + /* see if Soundscape is there by reading HW ... */ + if((inportb(BasePort+GA_HOSTCTL_OFF)&0x78) != 0x00) return 0; + if((inportb(BasePort+GA_ADDR_OFF)&0xf0)==0xf0) return 0; + + outportb(BasePort+GA_ADDR_OFF,0xf5); + + tmp=inportb(BasePort+GA_ADDR_OFF); + + if((tmp & 0xf0)==0xf0) return 0; + if((tmp & 0x0f)!=0x05) return 0; + + /* formulate the chip ID */ + if( (tmp & 0x80) != 0x00 ) + IcType = MMIC; + else if((tmp & 0x70) != 0x00) + IcType = OPUS; + else + IcType = ODIE; + + /* now do a quick check to make sure the CoDec is there too */ + if((inportb(WavePort)&0x80)!=0x00) return 0; + return 1; +} + + +#ifdef NEVER + +static void interrupt newhandler(void) +{ + if(WaveIrq==7){ + outportb(0x20,0x0b); + inportb(0x21); + if(!(inportb(0x20)&0x80)) return; + } + else if(WaveIrq==15){ + outportb(0xa0,0x0b); + inportb(0xa1); + if(!(inportb(0xa0)&0x80)) return; + } + + interruptcount++; + + /* if the CoDec is interrupting clear the AD-1848 interrupt */ + if(inportb(WavePort+CD_STATUS_OFF)&0x01) + outportb(WavePort+CD_STATUS_OFF,0x00); + + MIrq_EOI(WaveIrq); +} + + +static PVI oldhandler; + +#endif + + +static UWORD codecfreqs[14]={ + 5512, 6615, 8000, 9600,11025,16000,18900, + 22050,27428,32000,33075,37800,44100,48000 +}; + + +static UWORD codecformats[14]={ + 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, 0x05, + 0x07, 0x04, 0x06, 0x0d, 0x09, 0x0b, 0x0c +}; + + +static UBYTE codecformat; + + +static BOOL SS_Init(void) +{ + int t; + + if(!SS_IsThere()){ + myerr="No such hardware detected, check your 'SNDSCAPE' env. variable"; + return 0; + } + + printf("Ensoniq Soundscape at port 0x%x, irq %d, dma %d\n",WavePort,WaveIrq,DmaChan); + + /* find closest codec frequency */ + + for(t=0;t<14;t++){ + if(t==13 || md_mixfreq<=codecfreqs[t]){ + md_mixfreq=codecfreqs[t]; + break; + } + } + + codecformat=codecformats[t]; + if(md_mode & DMODE_STEREO) codecformat|=0x10; + if(md_mode & DMODE_16BITS) codecformat|=0x40; + + if(!VC_Init()) return 0; + + SS_DMAMEM=MDma_AllocMem(md_dmabufsize); + + if(SS_DMAMEM==NULL){ + VC_Exit(); + myerr="Couldn't allocate page-contiguous dma-buffer"; + return 0; + } + + SS_DMABUF=(char *)MDma_GetPtr(SS_DMAMEM); + + /* In case the CoDec is running, stop it */ + StopCoDec(); + + /* Clear possible CoDec and SoundBlaster emulation interrupts */ + outportb(WavePort+CD_STATUS_OFF,0x00); + inportb(0x22e); + + /* If necessary, save some regs, do some resource re-routing */ + if( IcType != MMIC) { + + /* derive the MIDI and Wave IRQ indices (0-3) for reg writes */ + for( Mindx = 0; Mindx < 4; ++Mindx ) + if( MidiIrq == *(Irqs + Mindx) ) + break; + for( Windx = 0; Windx < 4; ++Windx ) + if( WaveIrq == *(Irqs + Windx) ) + break; + + /* setup the CoDec DMA polarity */ + GaWrite(GA_DMACFG_REG, 0x50); + + /* give the CoDec control of the DMA and Wave IRQ resources */ + CdCfgSav = GaRead(GA_CDCFG_REG); + GaWrite(GA_CDCFG_REG, 0x89 | (DmaChan << 4) | (Windx << 1)); + + /* pull the Sound Blaster emulation off of those resources */ + DmaCfgSav = GaRead(GA_DMAB_REG); + GaWrite(GA_DMAB_REG, 0x20); + IntCfgSav = GaRead(GA_INTCFG_REG); + GaWrite(GA_INTCFG_REG, 0xf0 | (Mindx << 2) | Mindx); + } + + /* Save all volumes that we might use, init some levels */ + DacSavL = CdRead(CD_DACL_REG); + DacSavR = CdRead(CD_DACR_REG); + CdxSavL = CdRead(CD_CDAUXL_REG); + CdxSavR = CdRead(CD_CDAUXL_REG); + AdcSavL = CdRead(CD_ADCL_REG); + AdcSavR = CdRead(CD_ADCR_REG); + + SetDacVol(127, 127); + SetAdcVol(96, 96); + + /* Select the mic/line input to the record mux; */ + /* if not ODIE, set the mic gain bit too */ + CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0x3f) | + (IcType == ODIE ? 0x80 : 0xa0)); + CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0x3f) | + (IcType == ODIE ? 0x80 : 0xa0)); + + /* Put the CoDec into mode change state */ + outportb(WavePort + CD_ADDR_OFF, 0x40); + + /* Setup CoDec mode - single DMA chan, AutoCal on */ + CdWrite(CD_CONFIG_REG, 0x0c); + +#ifdef NEVER + /* enable the CoDec interrupt pin */ + CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02); + oldhandler=MIrq_SetHandler(WaveIrq,newhandler); + MIrq_OnOff(WaveIrq,1); +#else + /* disable the interrupt for mikmod */ + CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd); +#endif + return 1; +} + + + +static void SS_Exit(void) +{ + /* in case the CoDec is running, stop it */ + StopCoDec(); + + /* mask the PC DMA Controller */ +/* outportb(DmacRegP->mask, 0x04 | DmaChan); */ + +#ifdef NEVER + /* disable the CoDec interrupt pin */ + CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd); + MIrq_OnOff(WaveIrq,0); + MIrq_SetHandler(WaveIrq,oldhandler); +#endif + + /* restore all volumes ... */ + CdWrite(CD_DACL_REG, DacSavL); + CdWrite(CD_DACR_REG, DacSavR); + CdWrite(CD_CDAUXL_REG, CdxSavL); + CdWrite(CD_CDAUXL_REG, CdxSavR); + CdWrite(CD_ADCL_REG, AdcSavL); + CdWrite(CD_ADCR_REG, AdcSavR); + + /* if necessary, restore gate array resource registers */ + if(IcType!=MMIC){ + GaWrite(GA_INTCFG_REG, IntCfgSav); + GaWrite(GA_DMAB_REG, DmaCfgSav); + GaWrite(GA_CDCFG_REG, CdCfgSav); + } + + MDma_FreeMem(SS_DMAMEM); + VC_Exit(); +} + + +static UWORD last=0; +static UWORD curr=0; + + +static void SS_Update(void) +{ + UWORD todo,index; + + curr=(md_dmabufsize-MDma_Todo(DmaChan))&0xfffc; + + if(curr>=md_dmabufsize) return; + if(curr==last) return; + + if(curr>last){ + todo=curr-last; index=last; + last+=VC_WriteBytes(&SS_DMABUF[index],todo); + MDma_Commit(SS_DMAMEM,index,todo); + if(last>=md_dmabufsize) last=0; + } + else{ + todo=md_dmabufsize-last; + VC_WriteBytes(&SS_DMABUF[last],todo); + MDma_Commit(SS_DMAMEM,last,todo); + last=VC_WriteBytes(SS_DMABUF,curr); + MDma_Commit(SS_DMAMEM,0,curr); + } +} + + +static void SS_PlayStart(void) +{ + int direction=0; + long i; + UWORD tmp; + + VC_PlayStart(); + + /* make sure the the CoDec is in mode change state */ + outportb(WavePort + CD_ADDR_OFF, 0x40); + + /* and write the format register */ + CdWrite(CD_FORMAT_REG, codecformat); + + /* if not using ODIE and recording, setup extended format register */ + if( IcType != ODIE && direction ) + CdWrite(CD_XFORMAT_REG, codecformat & 0x70); + + /* delay for internal re-synch */ + for( i = 0; i < 200000UL; ++i ) + inportb(BasePort + GA_ADDR_OFF); + + /* clear the dma buffer */ + + VC_SilenceBytes(SS_DMABUF,md_dmabufsize); + MDma_Commit(SS_DMAMEM,0,md_dmabufsize); + + /* Write the CoDec interrupt count - sample frames per half-buffer. */ + /* If not using ODIE and recording, use extended count regs */ + tmp = md_dmabufsize; + if(md_mode&DMODE_STEREO) tmp>>=1; + if(md_mode&DMODE_16BITS) tmp>>=1; + tmp--; + + if( IcType != ODIE && direction ) { + CdWrite(CD_XLCOUNT_REG, tmp); + CdWrite(CD_XUCOUNT_REG, tmp >> 8); + } + else { + CdWrite(CD_LCOUNT_REG, tmp); + CdWrite(CD_UCOUNT_REG, tmp >> 8); + } + + if(!MDma_Start(DmaChan,SS_DMAMEM,md_dmabufsize,INDEF_WRITE)){ + return; + } + + /* disable mode change state and start the CoDec */ + outportb(WavePort + CD_ADDR_OFF, 0x00); + CdWrite(CD_CONFIG_REG, direction ? 0x02 : 0x01); +} + + +static void SS_PlayStop(void) +{ + StopCoDec(); + VC_PlayStop(); +} + + +DRIVER drv_ss={ + NULL, + "Ensoniq Soundscape", + "MikMod Ensoniq Soundscape Driver v0.0 - Thanks to CyberCerus", + SS_IsThere, + VC_SampleLoad, + VC_SampleUnload, + SS_Init, + SS_Exit, + SS_PlayStart, + SS_PlayStop, + SS_Update, + VC_VoiceSetVolume, + VC_VoiceSetFrequency, + VC_VoiceSetPanning, + VC_VoicePlay +};