--- /dev/null
+/*\r
+\r
+Name:\r
+DRV_SB.C\r
+\r
+Description:\r
+Mikmod driver for output on Creative Labs Soundblasters & compatibles\r
+(through DSP)\r
+\r
+Portability:\r
+\r
+MSDOS: BC(y) Watcom(y) DJGPP(y)\r
+Win95: n\r
+Os2: n\r
+Linux: n\r
+\r
+(y) - yes\r
+(n) - no (not possible or not useful)\r
+(?) - may be possible, but not tested\r
+\r
+*/\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <dos.h>\r
+#include <malloc.h>\r
+#include <conio.h>\r
+#ifndef __DJGPP__\r
+#include <mem.h>\r
+#endif\r
+\r
+#include "mikmod.h"\r
+#include "mdma.h"\r
+#include "mirq.h"\r
+\r
+/***************************************************************************\r
+>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SB stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
+***************************************************************************/\r
+\r
+static UWORD sb_port; /* sb base port */\r
+\r
+/*\r
+ Define some important SB i/o ports:\r
+*/\r
+\r
+#define MIXER_ADDRESS (sb_port+0x4)\r
+#define MIXER_DATA (sb_port+0x5)\r
+#define DSP_RESET (sb_port+0x6)\r
+#define DSP_READ_DATA (sb_port+0xa)\r
+#define DSP_WRITE_DATA (sb_port+0xc)\r
+#define DSP_WRITE_STATUS (sb_port+0xc)\r
+#define DSP_DATA_AVAIL (sb_port+0xe)\r
+\r
+\r
+static void SB_MixerStereo(void)\r
+/*\r
+ Enables stereo output for DSP versions 3.00 >= ver < 4.00\r
+*/\r
+{\r
+ outportb(MIXER_ADDRESS,0xe);\r
+ outportb(MIXER_DATA,inportb(MIXER_DATA)|2);\r
+}\r
+\r
+\r
+\r
+static void SB_MixerMono(void)\r
+/*\r
+ Disables stereo output for DSP versions 3.00 >= ver < 4.00\r
+*/\r
+{\r
+ outportb(MIXER_ADDRESS,0xe);\r
+ outportb(MIXER_DATA,inportb(MIXER_DATA)&0xfd);\r
+}\r
+\r
+\r
+\r
+static BOOL SB_WaitDSPWrite(void)\r
+/*\r
+ Waits until the DSP is ready to be written to.\r
+\r
+ returns FALSE on timeout\r
+*/\r
+{\r
+ UWORD timeout=32767;\r
+\r
+ while(timeout--){\r
+ if(!(inportb(DSP_WRITE_STATUS)&0x80)) return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+\r
+static BOOL SB_WaitDSPRead(void)\r
+/*\r
+ Waits until the DSP is ready to read from.\r
+\r
+ returns FALSE on timeout\r
+*/\r
+{\r
+ UWORD timeout=32767;\r
+\r
+ while(timeout--){\r
+ if(inportb(DSP_DATA_AVAIL)&0x80) return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+\r
+static BOOL SB_WriteDSP(UBYTE data)\r
+/*\r
+ Writes byte 'data' to the DSP.\r
+\r
+ returns FALSE on timeout.\r
+*/\r
+{\r
+ if(!SB_WaitDSPWrite()) return 0;\r
+ outportb(DSP_WRITE_DATA,data);\r
+ return 1;\r
+}\r
+\r
+\r
+\r
+static UWORD SB_ReadDSP(void)\r
+/*\r
+ Reads a byte from the DSP.\r
+\r
+ returns 0xffff on timeout.\r
+*/\r
+{\r
+ if(!SB_WaitDSPRead()) return 0xffff;\r
+ return(inportb(DSP_READ_DATA));\r
+}\r
+\r
+\r
+\r
+static void SB_SpeakerOn(void)\r
+/*\r
+ Enables DAC speaker output.\r
+*/\r
+{\r
+ SB_WriteDSP(0xd1);\r
+}\r
+\r
+\r
+\r
+static void SB_SpeakerOff(void)\r
+/*\r
+ Disables DAC speaker output\r
+*/\r
+{\r
+ SB_WriteDSP(0xd3);\r
+}\r
+\r
+\r
+\r
+static void SB_ResetDSP(void)\r
+/*\r
+ Resets the DSP.\r
+*/\r
+{\r
+ int t;\r
+ /* reset the DSP by sending 1, (delay), then 0 */\r
+ outportb(DSP_RESET,1);\r
+ for(t=0;t<8;t++) inportb(DSP_RESET);\r
+ outportb(DSP_RESET,0);\r
+}\r
+\r
+\r
+\r
+static BOOL SB_Ping(void)\r
+/*\r
+ Checks if a SB is present at the current baseport by\r
+ resetting the DSP and checking if it returned the value 0xaa.\r
+\r
+ returns: TRUE => SB is present\r
+ FALSE => No SB detected\r
+*/\r
+{\r
+ SB_ResetDSP();\r
+ return(SB_ReadDSP()==0xaa);\r
+}\r
+\r
+\r
+\r
+static UWORD SB_GetDSPVersion(void)\r
+/*\r
+ Gets SB-dsp version. returns 0xffff if dsp didn't respond.\r
+*/\r
+{\r
+ UWORD hi,lo;\r
+\r
+ if(!SB_WriteDSP(0xe1)) return 0xffff;\r
+\r
+ hi=SB_ReadDSP();\r
+ lo=SB_ReadDSP();\r
+\r
+ return((hi<<8)|lo);\r
+}\r
+\r
+\r
+\r
+/***************************************************************************\r
+>>>>>>>>>>>>>>>>>>>>>>>>> The actual SB driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
+***************************************************************************/\r
+\r
+static DMAMEM *SB_DMAMEM;\r
+static signed char *SB_DMABUF;\r
+\r
+static UBYTE SB_TIMECONSTANT;\r
+\r
+static UBYTE PIC1MSK;\r
+static UBYTE PIC2MSK;\r
+\r
+static UWORD sb_int; /* interrupt vector that belongs to sb_irq */\r
+static UWORD sb_ver; /* DSP version number */\r
+static UBYTE sb_irq; /* sb irq */\r
+static UBYTE sb_lodma; /* 8 bit dma channel (1.0/2.0/pro) */\r
+static UBYTE sb_hidma; /* 16 bit dma channel (16/16asp) */\r
+static UBYTE sb_dma; /* current dma channel */\r
+\r
+\r
+static BOOL SB_IsThere(void)\r
+{\r
+ char *envptr,c;\r
+ static char *endptr;\r
+\r
+ sb_port =0xffff;\r
+ sb_irq =0xff;\r
+ sb_lodma=0xff;\r
+ sb_hidma=0xff;\r
+\r
+ if((envptr=getenv("BLASTER"))==NULL) return 0;\r
+\r
+ while(1){\r
+\r
+ /* skip whitespace */\r
+\r
+ do c=*(envptr++); while(c==' ' || c=='\t');\r
+\r
+ /* reached end of string? -> exit */\r
+\r
+ if(c==0) break;\r
+\r
+ switch(c){\r
+\r
+ case 'a':\r
+ case 'A':\r
+ sb_port=strtol(envptr,&endptr,16);\r
+ break;\r
+\r
+ case 'i':\r
+ case 'I':\r
+ sb_irq=strtol(envptr,&endptr,10);\r
+ break;\r
+\r
+ case 'd':\r
+ case 'D':\r
+ sb_lodma=strtol(envptr,&endptr,10);\r
+ break;\r
+\r
+ case 'h':\r
+ case 'H':\r
+ sb_hidma=strtol(envptr,&endptr,10);\r
+ break;\r
+\r
+ default:\r
+ strtol(envptr,&endptr,16);\r
+ break;\r
+ }\r
+ envptr=endptr;\r
+ }\r
+\r
+ if(sb_port==0xffff || sb_irq==0xff || sb_lodma==0xff) return 0;\r
+\r
+ /* determine interrupt vector */\r
+\r
+ sb_int = (sb_irq>7) ? sb_irq+104 : sb_irq+8;\r
+\r
+ if(!SB_Ping()) return 0;\r
+\r
+ /* get dsp version. */\r
+\r
+ if((sb_ver=SB_GetDSPVersion())==0xffff) return 0;\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+static void interrupt newhandler(MIRQARGS)\r
+{\r
+ if(sb_ver<0x200){\r
+ SB_WriteDSP(0x14);\r
+ SB_WriteDSP(0xff);\r
+ SB_WriteDSP(0xfe);\r
+ }\r
+\r
+ if(md_mode & DMODE_16BITS)\r
+ inportb(sb_port+0xf);\r
+ else\r
+ inportb(DSP_DATA_AVAIL);\r
+\r
+ MIrq_EOI(sb_irq);\r
+}\r
+\r
+\r
+static PVI oldhandler;\r
+\r
+\r
+static BOOL SB_Init(void)\r
+{\r
+ ULONG t;\r
+\r
+ if(!SB_IsThere()){\r
+ myerr="No such hardware detected, check your 'BLASTER' env. variable";\r
+ return 0;\r
+ }\r
+\r
+/* printf("SB version %x\n",sb_ver); */\r
+/* if(sb_ver>0x200) sb_ver=0x200; */\r
+\r
+ if(sb_ver>=0x400 && sb_hidma==0xff){\r
+ myerr="High-dma setting in 'BLASTER' variable is required for SB-16";\r
+ return 0;\r
+ }\r
+\r
+ if(sb_ver<0x400 && md_mode&DMODE_16BITS){\r
+ /* DSP versions below 4.00 can't do 16 bit sound. */\r
+ md_mode&=~DMODE_16BITS;\r
+ }\r
+\r
+ if(sb_ver<0x300 && md_mode&DMODE_STEREO){\r
+ /* DSP versions below 3.00 can't do stereo sound. */\r
+ md_mode&=~DMODE_STEREO;\r
+ }\r
+\r
+ /* Use low dma channel for 8 bit, high dma for 16 bit */\r
+\r
+ sb_dma=(md_mode & DMODE_16BITS) ? sb_hidma : sb_lodma;\r
+\r
+ if(sb_ver<0x400){\r
+\r
+ t=md_mixfreq;\r
+ if(md_mode & DMODE_STEREO) t<<=1;\r
+\r
+ SB_TIMECONSTANT=256-(1000000L/t);\r
+\r
+ if(sb_ver<0x201){\r
+ if(SB_TIMECONSTANT>210) SB_TIMECONSTANT=210;\r
+ }\r
+ else{\r
+ if(SB_TIMECONSTANT>233) SB_TIMECONSTANT=233;\r
+ }\r
+\r
+ md_mixfreq=1000000L/(256-SB_TIMECONSTANT);\r
+ if(md_mode & DMODE_STEREO) md_mixfreq>>=1;\r
+ }\r
+\r
+ if(!VC_Init()) return 0;\r
+\r
+ SB_DMAMEM=MDma_AllocMem(md_dmabufsize);\r
+\r
+ if(SB_DMAMEM==NULL){\r
+ VC_Exit();\r
+ myerr="Couldn't allocate page-contiguous dma-buffer";\r
+ return 0;\r
+ }\r
+\r
+ SB_DMABUF=(char *)MDma_GetPtr(SB_DMAMEM);\r
+\r
+ oldhandler=MIrq_SetHandler(sb_irq,newhandler);\r
+ return 1;\r
+}\r
+\r
+\r
+\r
+static void SB_Exit(void)\r
+{\r
+ MIrq_SetHandler(sb_irq,oldhandler);\r
+ MDma_FreeMem(SB_DMAMEM);\r
+ VC_Exit();\r
+}\r
+\r
+\r
+static UWORD last=0;\r
+static UWORD curr=0;\r
+\r
+\r
+static void SB_Update(void)\r
+{\r
+ UWORD todo,index;\r
+\r
+ curr=(md_dmabufsize-MDma_Todo(sb_dma))&0xfffc;\r
+\r
+ if(curr==last) return;\r
+\r
+ if(curr>last){\r
+ todo=curr-last; index=last;\r
+ last+=VC_WriteBytes(&SB_DMABUF[index],todo);\r
+ MDma_Commit(SB_DMAMEM,index,todo);\r
+ if(last>=md_dmabufsize) last=0;\r
+ }\r
+ else{\r
+ todo=md_dmabufsize-last;\r
+ VC_WriteBytes(&SB_DMABUF[last],todo);\r
+ MDma_Commit(SB_DMAMEM,last,todo);\r
+ last=VC_WriteBytes(SB_DMABUF,curr);\r
+ MDma_Commit(SB_DMAMEM,0,curr);\r
+ }\r
+}\r
+\r
+\r
+\r
+\r
+static void SB_PlayStart(void)\r
+{\r
+ VC_PlayStart();\r
+\r
+ MIrq_OnOff(sb_irq,1);\r
+\r
+ if(sb_ver>=0x300 && sb_ver<0x400){\r
+ if(md_mode & DMODE_STEREO){\r
+ SB_MixerStereo();\r
+ }\r
+ else{\r
+ SB_MixerMono();\r
+ }\r
+ }\r
+\r
+ /* clear the dma buffer */\r
+\r
+ VC_SilenceBytes(SB_DMABUF,md_dmabufsize);\r
+ MDma_Commit(SB_DMAMEM,0,md_dmabufsize);\r
+\r
+ if(!MDma_Start(sb_dma,SB_DMAMEM,md_dmabufsize,INDEF_WRITE)){\r
+ return;\r
+ }\r
+\r
+ if(sb_ver<0x400){\r
+ SB_SpeakerOn();\r
+\r
+ SB_WriteDSP(0x40);\r
+ SB_WriteDSP(SB_TIMECONSTANT);\r
+\r
+ if(sb_ver<0x200){\r
+ SB_WriteDSP(0x14);\r
+ SB_WriteDSP(0xff);\r
+ SB_WriteDSP(0xfe);\r
+ }\r
+ else if(sb_ver==0x200){\r
+ SB_WriteDSP(0x48);\r
+ SB_WriteDSP(0xff);\r
+ SB_WriteDSP(0xfe);\r
+ SB_WriteDSP(0x1c);\r
+ }\r
+ else{\r
+ SB_WriteDSP(0x48);\r
+ SB_WriteDSP(0xff);\r
+ SB_WriteDSP(0xfe);\r
+ SB_WriteDSP(0x90);\r
+ }\r
+ }\r
+ else{\r
+ SB_WriteDSP(0x41);\r
+\r
+ SB_WriteDSP(md_mixfreq>>8);\r
+ SB_WriteDSP(md_mixfreq&0xff);\r
+\r
+ if(md_mode & DMODE_16BITS){\r
+ SB_WriteDSP(0xb6);\r
+ SB_WriteDSP((md_mode & DMODE_STEREO) ? 0x30 : 0x10);\r
+ }\r
+ else{\r
+ SB_WriteDSP(0xc6);\r
+ SB_WriteDSP((md_mode & DMODE_STEREO) ? 0x20 : 0x00);\r
+ }\r
+\r
+ SB_WriteDSP(0xff);\r
+ SB_WriteDSP(0xef);\r
+ }\r
+}\r
+\r
+\r
+static void SB_PlayStop(void)\r
+{\r
+ VC_PlayStop();\r
+ SB_SpeakerOff();\r
+ SB_ResetDSP();\r
+ SB_ResetDSP();\r
+ MDma_Stop(sb_dma);\r
+ MIrq_OnOff(sb_irq,0);\r
+}\r
+\r
+\r
+DRIVER drv_sb={\r
+ NULL,\r
+ "Soundblaster & compatibles",\r
+ "MikMod Soundblaster Driver v2.1 for 1.0 / 2.0 / Pro / 16",\r
+ SB_IsThere,\r
+ VC_SampleLoad,\r
+ VC_SampleUnload,\r
+ SB_Init,\r
+ SB_Exit,\r
+ SB_PlayStart,\r
+ SB_PlayStop,\r
+ SB_Update,\r
+ VC_VoiceSetVolume,\r
+ VC_VoiceSetFrequency,\r
+ VC_VoiceSetPanning,\r
+ VC_VoicePlay\r
+};\r