From: Me Newsgroups: comp.os.msdos.djgpp Subject: Porting 16bit software for 32bit DJGPP Date: Wed, 29 Jan 1997 17:04:17 -0800 Organization: Maine Medical Center Lines: 902 Message-ID: <32EFF391.CB6@ime.net> NNTP-Posting-Host: mitchg-home.mmc.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------7E1E274D67D" To: djgpp AT delorie DOT com DJ-Gateway: from newsgroup comp.os.msdos.djgpp This is a multi-part message in MIME format. --------------7E1E274D67D Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Can someone please help me with porting this 16 bit program to compoile and run with djgpp v2? Thanks in advance. --------------7E1E274D67D Content-Type: text/plain; charset=us-ascii; name="SNDSCAPE.C" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="SNDSCAPE.C" /**************************************************************************** ENSONIQ Soundscape Digital Audio Driver DOS Real-Mode Version Created 09/08/95, ENSONIQ Corp. ****************************************************************************/ #include #include #include #include #include #include "sndscape.h" /**************************************************************************** EXTERNAL GLOBAL VARS - hardware config info ... ****************************************************************************/ WORD BasePort; /* Gate Array/MPU-401 base port */ short MidiIrq; /* the MPU-401 IRQ */ WORD WavePort; /* the CODEC base port */ short WaveIrq; /* the PCM IRQ */ short DmaChan; /* the PCM DMA channel */ /**************************************************************************** INTERNAL GLOBAL VARS - all kinds of stuff ... ****************************************************************************/ /* This var MUST be pre-initialized !!! */ BYTE Flags = 0x00; /* driver state flags */ #define FLAG_DETECTED 0x01 #define FLAG_OPENED 0x02 #define FLAG_CODECON 0x04 short Windx; /* Wave IRQ index - for reg writes */ short Mindx; /* MIDI IRQ index - for reg writes */ char * volatile NextBuffer; /* dynamic DMA A/B buffer pointer */ char * DmaBuffP; /* pointer to aligned dma buff */ DWORD DmaPaddr; /* physical DMA address */ char DmaBuffer[BUFFERSIZE*2]; /* the dma buffer space */ #ifdef USE_CODEC_IRQ void (interrupt * VecSav)(); /* int vector save area */ WORD PicBase; /* the PIC base port */ short IrqMask; /* IRQ mask value */ short IrqMaskSav; /* save area for IRQ mask */ short Vector; /* the Wave interrupt vector num */ #endif short CdCfgSav; /* gate array register save area */ short DmaCfgSav; /* gate array register save area */ short IntCfgSav; /* gate array register save area */ short DacSavL; /* DAC left volume save */ short DacSavR; /* DAC right volume save */ short CdxSavL; /* CD/Aux left volume save */ short CdxSavR; /* CD/Aux right volume save */ short const SsIrqs[4] = { 9, 5, 7, 10 }; /* Soundscape IRQs */ short const RsIrqs[4] = { 9, 7, 5, 15 }; /* an older IRQ set */ short const * Irqs; /* pointer to one of the IRQ sets */ DMAC_REGS const DmacRegs[4] = { /* the DMAC regs for chans 0-3 ... */ { 0x00, 0x01, 0x08, 0x0a, 0x0b, 0x0c, 0x87 }, { 0x02, 0x03, 0x08, 0x0a, 0x0b, 0x0c, 0x83 }, { 0x04, 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x81 }, { 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x82 } }; DMAC_REGS const * DmacRegP; /* a pointer to a DMAC reg struct */ /**************************************************************************** EXTERNAL DRIVER FUNCTIONS ****************************************************************************/ /* short DetectSoundscape(void); This function is used to detect the presence of a Soundscape card in a system. It will read the hardware config info from the SNDSCAPE.INI file, the path to which is indicated by the SNDSCAPE environment variable. This config info will be stored in global variable space for the other driver functions to reference. Once the config settings have been determined, a hardware test will be performed to see if the Soundscape card is actually present. If this function is not explicitly called by the application, it it will be called by the OpenSoundscape function. INPUTS: None RETURNS: 0 - if Soundscape detection was successful -1 - if The SNDSCAPE environment variable was not found The SNDSCAPE.INI file cannot be opened The SNDSCAPE.INI file is missing information or is corrupted The Soundscape hardware is not detected */ short DetectSoundscape(void) { char static str[78]; short tmp, status; char *ep; FILE *fp; /* default status to failure */ status = -1; /* initialize some local vars ... */ fp = 0; /* get the environment var and build the filename; then open it */ if( (ep = getenv("SNDSCAPE")) == NULL ) goto exit; strcpy(str, ep); if( str[strlen(str) - 1] == '\\' ) str[strlen(str) - 1] = '\0'; strcat(str, "\\SNDSCAPE.INI"); if( (fp = fopen(str, "r")) == NULL ) goto exit; /* read all of the necessary config info ... */ if( GetConfigEntry(str, "Product", fp) ) goto exit; /* if an old product name is read, set the IRQs accordingly */ strupr(str); if( !strstr(str, "SOUNDFX") && !strstr(str, "MEDIA_FX") ) Irqs = SsIrqs; else Irqs = RsIrqs; if( GetConfigEntry(str, "Port", fp) ) goto exit; BasePort = (WORD) strtol(str, NULL, 16); if( GetConfigEntry(str, "WavePort", fp) ) goto exit; WavePort = (WORD) strtol(str, NULL, 16); if( GetConfigEntry(str, "IRQ", fp) ) goto exit; MidiIrq = (short) strtol(str, NULL, 10); if( MidiIrq == 2) MidiIrq = 9; if( GetConfigEntry(str, "SBIRQ", fp) ) goto exit; WaveIrq = (short) strtol(str, NULL, 10); if( WaveIrq == 2 ) WaveIrq = 9; if( GetConfigEntry(str, "DMA", fp) ) goto exit; DmaChan = (short) strtol(str, NULL, 10); /* see if Soundscape is there by reading HW ... */ if( (inp(BasePort + GA_HOSTCTL_OFF) & 0x78) != 0x00 ) goto exit; if( (inp(BasePort + GA_ADDR_OFF) & 0xf0) == 0xf0 ) goto exit; outp(BasePort + GA_ADDR_OFF, 0xf5); tmp = inp(BasePort + GA_ADDR_OFF); if( (tmp & 0xf0) == 0xf0) goto exit; if( (tmp & 0x0f) != 0x05) goto exit; /* now do a quick check to make sure the CoDec is there too */ if( (inp(WavePort) & 0x80) != 0x00 ) goto exit; /* flag success !!! */ status = 0; Flags |= FLAG_DETECTED; exit: if( fp != NULL ) fclose(fp); return status; } /* short OpenSoundscape(void); This function opens the Soundscape driver. It will setup the Soundscape hardware for Native PCM mode (the default mode is Sound Blaster emulation). It will first call the DetectSoundscape function it it determines that it has not already been called. INPUTS: None. RETURNS: 0 - if successful -1 - if the DetectSoundscape function needed to be called and returned unnsuccessful */ short OpenSoundscape(void) { /* already opened? if so, success */ if( Flags & FLAG_OPENED ) return 0; /* see if we need to detect first */ if( !(Flags & FLAG_DETECTED) ) if( DetectSoundscape() != 0 ) return -1; /* Clear possible CODEC and SoundBlaster emulation interrupts */ outp(WavePort + CD_STATUS_OFF, 0x00); inp(0x22e); #ifdef USE_CODEC_IRQ /* install the interrupt vector */ Vector = (WaveIrq < 8 ? 0x08 : 0x70) + (WaveIrq & 0x07); VecSav = _dos_getvect(Vector); _dos_setvect(Vector, WaveIsr); /* enable the CODEC interrupt pin */ CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02); /* Setup PIC/IRQ vars, unmask our IRQ */ PicBase = WaveIrq < 8 ? 0x20 : 0xa0; IrqMask = 1 << (WaveIrq & 0x07); IrqMaskSav = inp(PicBase + 1) & IrqMask; outp(PicBase + 1, inp(PicBase + 1) & ~IrqMask); #else /* disable the CODEC interrupt pin */ CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd); #endif /* Align the DMA buffer within the allocated space */ DmaBuffP = DmaBuffer; DmaPaddr = (DWORD) (char *) DmaBuffer; DmaPaddr = ((DmaPaddr >> 12) & 0x000ffff0) + (WORD) DmaPaddr; if( DmaPaddr >> 16 != (DmaPaddr + BUFFERSIZE-1) >> 16 ) { DmaPaddr += BUFFERSIZE; DmaBuffP += BUFFERSIZE; } /* Set DMA controller register set pointer based on channel */ DmacRegP = &DmacRegs[DmaChan]; /* Save some regs, do some resource re-routing */ /* 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); /* Init the DAC volume to ... full */ SetDacVol(127, 127); /* Put the CoDec into mode change state */ outp(WavePort + CD_ADDR_OFF, 0x40); /* Setup CoDec mode - single DMA chan, AutoCal on */ CdWrite(CD_CONFIG_REG, 0x0c); /* exit mode change state */ outp(WavePort + CD_ADDR_OFF, 0x00); /* flag open status and return */ Flags |= FLAG_OPENED; return 0; } /* short StartCoDec(WORD srate, BOOL stereo, BOOL size16bit); This function will start the CoDec auto-restart DMA process. INPUTS: srate - the audio sample rate in Hertz stereo - FALSE for mono data, TRUE for L/R interleaved stereo data size16bit - FALSE for 8-bit unsigned data, TRUE for 16-bit signed data RETURNS: 0 - if the CoDec was started successfully -1 - if the driver has not been opened or if the sample rate is unacceptable */ short StartCoDec(WORD srate, BOOL stereo, BOOL size16bit) { DWORD i; WORD tmp; /* if the driver is not open, fail */ if( !(Flags & FLAG_OPENED) ) return -1; /* make sure the CODEC is not already running */ if( Flags & FLAG_CODECON ) StopCoDec(); /* set format .... */ /* init the format register value */ tmp = 0; /* find the sample rate */ switch(srate) { case 8000U: tmp = 0x00; break; case 11025U: tmp = 0x03; break; case 16000U: tmp = 0x02; break; case 22050U: tmp = 0x07; break; case 32000U: tmp = 0x06; break; case 44100U: tmp = 0x0b; break; case 48000U: tmp = 0x0c; break; default: return -1; } /* set other format bits ... */ if( stereo == TRUE ) tmp |= 0x10; if( size16bit == TRUE ) tmp |= 0x40; /* put the the CoDec in mode change state */ outp(WavePort + CD_ADDR_OFF, 0x40); /* and write the format register */ CdWrite(CD_FORMAT_REG, tmp); /* delay for internal re-synch */ for( i = 0; i < 200000UL; ++i ) inp(BasePort + GA_ADDR_OFF); /* exit mode change state */ outp(WavePort + CD_ADDR_OFF, 0x00); /* pre-fill the audio buffer with silence */ tmp = size16bit == TRUE ? 0x00 : 0x80; for( i = 0; i < BUFFERSIZE; ++i ) *(DmaBuffP + i) = (char) tmp; /* init the "next-buffer" ptr */ NextBuffer = NULL; #ifdef USE_CODEC_IRQ /* Write CODEC interrupt count - sample frames per half-buffer (n-1). */ tmp = BUFFERSIZE/2; if( stereo == TRUE ) tmp >>= 1; if( size16bit == TRUE ) tmp >>= 1; CdWrite(CD_LCOUNT_REG, (tmp - 1)); CdWrite(CD_UCOUNT_REG, ((tmp - 1) >> 8)); #endif /* Setup the PC DMA Controller */ outp(DmacRegP->mask, 0x04 | DmaChan); outp(DmacRegP->mode, DMAC_AUTO_OUT | DmaChan); outp(DmacRegP->page, (int) (DmaPaddr >> 16)); /* Disable around 2-byte programming sequences */ _disable(); outp(DmacRegP->clrff, 0x00); outp(DmacRegP->addr, (int) DmaPaddr); outp(DmacRegP->addr, (int) DmaPaddr >> 8); outp(DmacRegP->count, BUFFERSIZE - 1); outp(DmacRegP->count, (BUFFERSIZE - 1) >> 8); _enable(); /* clear status, unmask the PC DMA Controller */ inp(DmacRegP->status); outp(DmacRegP->mask, DmaChan); /* disable mode change state and start the CoDec */ outp(WavePort + CD_ADDR_OFF, 0x00); CdWrite(CD_CONFIG_REG, 0x01); /* flag CODEC-on status and return */ Flags |= FLAG_CODECON; return 0; } /* char *GetNextBuffer(void); This function is used by the application, normally in a polling fashion, to get a pointer to the next buffer half to be filled/stored. INPUTS: None RETURNS: 0 - if the CoDec is not ready for audio data / has no audio data non-0 - a pointer to a BUFFERSIZE/2 byte buffer will be returned if the CoDec is ready for data / just filled a buffer */ char *GetNextBuffer(void) { BYTE *p; #ifdef USE_CODEC_IRQ if( (p = NextBuffer) != NULL ) NextBuffer = NULL; return p; #else if( GetDmaCount() < BUFFERSIZE/2 ) p = DmaBuffer; else p = DmaBuffer + BUFFERSIZE/2; if( p == (BYTE)NextBuffer ) return NULL; NextBuffer = p; return p; #endif } /* void PauseCoDec(void); This function will pause the CoDec auto-restart DMA process. Don't use this to stop the CoDec - use the StopCoDec function to do that; it will cleanup DRQs. INPUTS: None RETURNS: Nothing */ void PauseCoDec() { CdWrite(CD_CONFIG_REG, CdRead(CD_CONFIG_REG) & 0xfc); return; } /* void ResumeCoDec(void); This function will pause the CoDec auto-restart DMA process. INPUTS: None RETURNS: Nothing */ void ResumeCoDec(void) { CdWrite(CD_CONFIG_REG, 0x01); return; } /* void StopCoDec(void); This function will stop the CoDec auto-restart DMA process. INPUTS: None RETURNS: Nothing */ void StopCoDec(void) { WORD 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 < 64; ++i ) if( !(inp(DmacRegP->status) & (0x10 << DmaChan)) ) break; #ifdef USE_CODEC_IRQ ; #else /* clear internal CODEC interrupt status */ outp(WavePort + CD_STATUS_OFF, 0x00); #endif /* unflag CODEC-on status and return */ Flags &= ~FLAG_CODECON; return; } /* void CloseSoundscape(void); This function will make sure the CoDec is stopped, return the Soundscape hardware to it's default Sound Blaster emulation mode, de-install the PCM interrupt, resore the PIC mask, and do other cleanup. INPUTS: None RETURNS: None */ void CloseSoundscape(void) { /* if we're not open, just return */ if( !(Flags & FLAG_OPENED) ) return; /* if the CoDec is running, stop it */ if( Flags & FLAG_CODECON ) StopCoDec(); /* mask the PC DMA Controller */ outp(DmacRegP->mask, 0x04 | DmaChan); /* restore all volumes ... */ CdWrite(CD_DACL_REG, DacSavL); CdWrite(CD_DACR_REG, DacSavR); CdWrite(CD_CDAUXL_REG, CdxSavL); CdWrite(CD_CDAUXL_REG, CdxSavR); /* if necessary, restore gate array resource registers */ GaWrite(GA_INTCFG_REG, IntCfgSav); GaWrite(GA_DMAB_REG, DmaCfgSav); GaWrite(GA_CDCFG_REG, CdCfgSav); #ifdef USE_CODEC_IRQ /* disable the CoDec interrupt pin */ CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd); /* restore pic mask state */ outp(PicBase + 1, inp(PicBase + 1) | IrqMaskSav); /* restore the interrupt vector */ _dos_setvect(Vector, VecSav); #endif /* unflag open status and return */ Flags &= ~FLAG_OPENED; return; } /* void SetDacVol(short lvol, short 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 */ void SetDacVol(short lvol, short rvol) { CdWrite(CD_DACL_REG, (~lvol & 0x7f) >> 1); CdWrite(CD_DACR_REG, (~rvol & 0x7f) >> 1); return; } /* void SetCdRomVol(short lvol, short 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 */ void SetCdRomVol(short lvol, short rvol) { CdWrite(CD_CDAUXL_REG, (~lvol & 0x7f) >> 2); CdWrite(CD_CDAUXR_REG, (~rvol & 0x7f) >> 2); return; } /* WORD GetDmaCount(void); This function returns the DMA controller count value for the CoDec DMA channel. INPUTS: None. RETURNS: 16-bit DMA count value, ffffh to 0000h. */ WORD GetDmaCount(void) { WORD count0, count1; _disable(); outp(DmacRegP->clrff, 0x00); count0 = inp(DmacRegP->count); count0 |= inp(DmacRegP->count) << 8; count1 = inp(DmacRegP->count); count1 |= inp(DmacRegP->count) << 8; _enable(); if( (BYTE) count1 != (BYTE) count0 ) return count1; else return count0; } /**************************************************************************** INTERNAL DRIVER FUNCTIONS ****************************************************************************/ /* short GaRead(WORD 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 */ short GaRead(WORD rnum) { outp(BasePort + GA_ADDR_OFF, rnum); return inp(BasePort + GA_DATA_OFF); } /* short GaWrite(WORD rnum, short 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 */ void GaWrite(WORD rnum, short value) { outp(BasePort + GA_ADDR_OFF, rnum); outp(BasePort + GA_DATA_OFF, value); return; } /* short CdRead(WORD 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 */ short CdRead(WORD rnum) { outp(WavePort + CD_ADDR_OFF, (inp(WavePort + CD_ADDR_OFF) & 0xf0) | rnum); return inp(WavePort + CD_DATA_OFF); } /* short CdWrite(WORD rnum, short 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 */ void CdWrite(WORD rnum, short value) { outp(WavePort + CD_ADDR_OFF, (inp(WavePort + CD_ADDR_OFF) & 0xf0) | rnum); outp(WavePort + CD_DATA_OFF, value); return; } /* short GetConfigEntry(char *dest, char *entry, 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: 0 - if successful -1 - if the right-hand string is not found or has no equate */ short GetConfigEntry(char *dest, char *entry, FILE *fp) { 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 -1; /* 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 0; } #ifdef USE_CODEC_IRQ /* The Wave ISR This ISR sets the global next-buffer pointer based on the DMA controller count at the time of the interrupt. */ void interrupt WaveIsr() { /* verify PIC in-service reg for IRQs 7 and 15 */ if( (WaveIrq & 0x07) == 0x07 ) { outp(PicBase, 0x0b); inp(PicBase); if( !(inp(PicBase) & 0x80) ) return; } /* if the CoDec is interrupting ... */ if( inp(WavePort + CD_STATUS_OFF) & 0x01 ) { /* set the next-buffer to the next buffer half to fill */ if( GetDmaCount() < BUFFERSIZE/2 ) NextBuffer = DmaBuffer; else NextBuffer = DmaBuffer + BUFFERSIZE/2; /* clear the CODEC interrupt */ outp(WavePort + CD_STATUS_OFF, 0x00); } /* clear the PC interrupt controller */ if(WaveIrq >= 8) outp(0xa0, 0x20); outp(0x20, 0x20); return; } #endif --------------7E1E274D67D--