added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / drv_sb.c
diff --git a/libs/oldmik/src/drv_sb.c b/libs/oldmik/src/drv_sb.c
new file mode 100644 (file)
index 0000000..6d85d47
--- /dev/null
@@ -0,0 +1,511 @@
+/*\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