cli added for debug
[fceu.git] / drivers / cli / dos-sound.c
1 /* FCE Ultra - NES/Famicom Emulator\r
2  *\r
3  * Copyright notice for this file:\r
4  *  Copyright (C) 2002 Ben Parnell\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  */\r
20 \r
21 #include <stdio.h>\r
22 #include <stdlib.h>\r
23 #include <string.h>\r
24 #include <sys/farptr.h>\r
25 #include <pc.h>\r
26 #include <dos.h>\r
27 #include <dpmi.h>\r
28 #include <go32.h>\r
29 #include <ctype.h>\r
30 \r
31 #include "dos.h"\r
32 #include "dos-sound.h"\r
33 #include "dos-joystick.h"\r
34 \r
35 \r
36 static void SBIRQHandler(_go32_dpmi_registers *r);\r
37 static uint32 LMBuffer;         /* Address of low memory DMA playback buffer. */\r
38 static int LMSelector;\r
39 \r
40 static uint8 *WaveBuffer;\r
41 static unsigned int IVector, SBIRQ, SBDMA, SBDMA16, SBPort;\r
42 static int DSPV,hsmode;\r
43 static int format;\r
44 static int frags, fragsize, fragtotal;\r
45 static volatile int WritePtr, ReadPtr;\r
46 static volatile int hbusy;\r
47 static volatile int whichbuf;\r
48 \r
49 \r
50 static uint8 PICMask;\r
51 /* Protected mode interrupt vector info. */\r
52 static _go32_dpmi_seginfo SBIH,SBIHOld;\r
53 \r
54 /* Real mode interrupt vector info. */\r
55 static _go32_dpmi_seginfo SBIHRM,SBIHRMOld;\r
56 static _go32_dpmi_registers SBIHRMRegs;\r
57 \r
58 static int WriteDSP(uint8 V)\r
59 {\r
60  int x;\r
61 \r
62  for(x=65536;x;x--)\r
63  {\r
64   if(!(inportb(SBPort+0xC)&0x80))\r
65   {\r
66    outportb(SBPort+0xC,V);\r
67    return(1);\r
68   }\r
69  }\r
70  return(0);\r
71 }\r
72 \r
73 static int ReadDSP(uint8 *V)\r
74 {\r
75  int x;\r
76 \r
77  for(x=65536;x;x--)             /* Should be more than enough time... */\r
78  {\r
79   if(inportb(SBPort+0xE)&0x80)\r
80   {\r
81    *V=inportb(SBPort+0xA);\r
82    return(1);\r
83   }\r
84  }\r
85  return(0);\r
86 }\r
87 \r
88 \r
89 static int SetVectors(void)\r
90 {\r
91  SBIH.pm_offset=SBIHRM.pm_offset=(int)SBIRQHandler;\r
92  SBIH.pm_selector=SBIHRM.pm_selector=_my_cs();\r
93 \r
94  /* Get and set real mode interrupt vector.  */\r
95  _go32_dpmi_get_real_mode_interrupt_vector(IVector,&SBIHRMOld);\r
96  _go32_dpmi_allocate_real_mode_callback_iret(&SBIHRM, &SBIHRMRegs);\r
97  _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRM);  \r
98 \r
99  /* Get and set protected mode interrupt vector. */\r
100  _go32_dpmi_get_protected_mode_interrupt_vector(IVector,&SBIHOld);\r
101  _go32_dpmi_allocate_iret_wrapper(&SBIH);\r
102  _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIH); \r
103 \r
104  return(1);\r
105 }\r
106 \r
107 static void ResetVectors(void)\r
108 {\r
109  _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIHOld);\r
110  _go32_dpmi_free_iret_wrapper(&SBIH);\r
111  _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRMOld);\r
112  _go32_dpmi_free_real_mode_callback(&SBIHRM);\r
113 }\r
114 \r
115 int GetBLASTER(void)\r
116 {\r
117  int check=0;\r
118  char *s;\r
119 \r
120  if(!(s=getenv("BLASTER")))\r
121  {\r
122   puts(" Error getting BLASTER environment variable.");\r
123   return(0);\r
124  }\r
125 \r
126  while(*s)\r
127  {\r
128   switch(toupper(*s))\r
129   {\r
130    case 'A': check|=(sscanf(s+1,"%x",&SBPort)==1)?1:0;break;\r
131    case 'I': check|=(sscanf(s+1,"%d",&SBIRQ)==1)?2:0;break;\r
132    case 'D': check|=(sscanf(s+1,"%d",&SBDMA)==1)?4:0;break;\r
133    case 'H': check|=(sscanf(s+1,"%d",&SBDMA16)==1)?8:0;break;\r
134   }\r
135   s++;\r
136  }\r
137  \r
138  if((check^7)&7 || SBDMA>=4 || (SBDMA16<=4 && check&8) || SBIRQ>15)\r
139  {\r
140   puts(" Invalid or incomplete BLASTER environment variable.");\r
141   return(0);\r
142  }\r
143  if(!(check&8))\r
144   format=0;\r
145  return(1);\r
146 }\r
147 \r
148 static int ResetDSP(void)\r
149 {\r
150  uint8 b;\r
151 \r
152  outportb(SBPort+0x6,0x1);\r
153  delay(10);\r
154  outportb(SBPort+0x6,0x0);\r
155  delay(10);\r
156 \r
157  if(ReadDSP(&b))\r
158   if(b==0xAA)\r
159    return(1); \r
160  return(0);\r
161 }\r
162 \r
163 static int GetDSPVersion(void)\r
164 {\r
165  int ret;\r
166  uint8 t;\r
167 \r
168  if(!WriteDSP(0xE1))\r
169   return(0);\r
170  if(!ReadDSP(&t))\r
171   return(0);\r
172  ret=t<<8;\r
173  if(!ReadDSP(&t))\r
174   return(0);\r
175  ret|=t;\r
176 \r
177  return(ret);\r
178 }\r
179 \r
180 static void KillDMABuffer(void)\r
181 {\r
182  __dpmi_free_dos_memory(LMSelector);\r
183 }\r
184 \r
185 static int MakeDMABuffer(void)\r
186 {\r
187  uint32 size;\r
188  int32 tmp;\r
189 \r
190  size=fragsize*2;       /* Two buffers in the DMA buffer. */\r
191  size<<=format;         /* Twice the size for 16-bit than for 8-bit. */\r
192 \r
193  size<<=1;              /* Double the size in case the first 2 buffers\r
194                            cross a 64KB or 128KB page boundary.\r
195                         */\r
196  size=(size+15)>>4;     /* Convert to paragraphs */\r
197 \r
198  if((tmp=__dpmi_allocate_dos_memory(size,&LMSelector))<0)\r
199   return(0);\r
200 \r
201  LMBuffer=tmp<<=4;\r
202 \r
203  if(format)   /* Check for and fix 128KB page boundary crossing. */\r
204  {\r
205   if((LMBuffer&0x20000) != ((LMBuffer+fragsize*2*2-1)&0x20000))\r
206    LMBuffer+=fragsize*2*2;\r
207  }\r
208  else   /* Check for and fix 64KB page boundary crossing. */\r
209  {\r
210   if((LMBuffer&0x10000) != ((LMBuffer+fragsize*2-1)&0x10000))\r
211    LMBuffer+=fragsize*2;\r
212  }\r
213 \r
214  DOSMemSet(LMBuffer, format?0:128, (fragsize*2)<<format);\r
215 \r
216  return(1);\r
217 }\r
218 \r
219 static void ProgramDMA(void)\r
220 {\r
221  static int PPorts[8]={0x87,0x83,0x81,0x82,0,0x8b,0x89,0x8a};\r
222  uint32 tmp;\r
223 \r
224  if(format)\r
225  {\r
226   outportb(0xd4,(SBDMA16&0x3)|0x4);\r
227   outportb(0xd8,0x0);\r
228   outportb(0xd6,(SBDMA16&0x3)|0x58);\r
229   tmp=((SBDMA16&3)<<2)+0xC2;\r
230  }\r
231  else\r
232  {\r
233   outportb(0xA,SBDMA|0x4);\r
234   outportb(0xC,0x0);\r
235   outportb(0xB,SBDMA|0x58);\r
236   tmp=(SBDMA<<1)+1;\r
237  }\r
238 \r
239  /* Size of entire buffer. */\r
240  outportb(tmp,(fragsize*2-1));\r
241  outportb(tmp,(fragsize*2-1)>>8);\r
242 \r
243  /* Page of buffer. */\r
244  outportb(PPorts[format?SBDMA16:SBDMA],LMBuffer>>16);\r
245 \r
246  /* Offset of buffer within page. */\r
247  if(format)\r
248   tmp=((SBDMA16&3)<<2)+0xc0;\r
249  else\r
250   tmp=SBDMA<<1;\r
251 \r
252  outportb(tmp,(LMBuffer>>format));\r
253  outportb(tmp,(LMBuffer>>(8+format)));\r
254 }\r
255 \r
256 int InitSB(int Rate, int bittage)\r
257 {\r
258  hsmode=hbusy=0;\r
259  whichbuf=1;\r
260  puts("Initializing Sound Blaster...");\r
261 \r
262  format=bittage?1:0;\r
263  frags=8;\r
264 \r
265  if(Rate<=11025)\r
266   fragsize=1<<5;\r
267  else if(Rate<=22050)\r
268   fragsize=1<<6;\r
269  else\r
270   fragsize=1<<7;\r
271 \r
272  fragtotal=frags*fragsize;\r
273  WaveBuffer=malloc(fragtotal<<format);\r
274 \r
275  if(format)\r
276   memset(WaveBuffer,0,fragtotal*2);\r
277  else\r
278   memset(WaveBuffer,128,fragtotal);\r
279 \r
280  WritePtr=ReadPtr=0;\r
281 \r
282  if((Rate<8192) || (Rate>65535))\r
283  {\r
284   printf(" Unsupported playback rate: %d samples per second\n",Rate);\r
285   return(0);\r
286  }\r
287 \r
288  if(!GetBLASTER())\r
289   return(0);\r
290  \r
291  /* Disable IRQ line in PIC0 or PIC1 */\r
292  if(SBIRQ>7)\r
293  {\r
294   PICMask=inportb(0xA1);\r
295   outportb(0xA1,PICMask|(1<<(SBIRQ&7)));\r
296  }\r
297  else\r
298  {\r
299   PICMask=inportb(0x21);\r
300   outportb(0x21,PICMask|(1<<SBIRQ));\r
301  }\r
302  if(!ResetDSP())\r
303  {\r
304   puts(" Error resetting the DSP.");\r
305   return(0);\r
306  }\r
307  \r
308  if(!(DSPV=GetDSPVersion()))\r
309  {\r
310   puts(" Error getting the DSP version.");\r
311   return(0);\r
312  }\r
313 \r
314  printf(" DSP Version: %d.%d\n",DSPV>>8,DSPV&0xFF);\r
315  if(DSPV<0x201)\r
316  {\r
317   printf(" DSP version number is too low.\n");\r
318   return(0);\r
319  }\r
320 \r
321  if(DSPV<0x400)\r
322   format=0;\r
323  if(!MakeDMABuffer())\r
324  {\r
325   puts(" Error creating low-memory DMA buffer.");\r
326   return(0);\r
327  }\r
328 \r
329  if(SBIRQ>7) IVector=SBIRQ+0x68;\r
330  else IVector=SBIRQ+0x8;\r
331 \r
332  if(!SetVectors())\r
333  {\r
334   puts(" Error setting interrupt vectors.");\r
335   KillDMABuffer();\r
336   return(0);\r
337  }\r
338 \r
339  /* Reenable IRQ line. */\r
340  if(SBIRQ>7)\r
341   outportb(0xA1,PICMask&(~(1<<(SBIRQ&7))));\r
342  else\r
343   outportb(0x21,PICMask&(~(1<<SBIRQ)));\r
344 \r
345  ProgramDMA();\r
346 \r
347  /* Note that the speaker must always be turned on before the mode transfer\r
348     byte is sent to the DSP if we're going into high-speed mode, since\r
349     a real Sound Blaster(at least my SBPro) won't accept DSP commands(except\r
350     for the reset "command") after it goes into high-speed mode.\r
351  */\r
352  WriteDSP(0xD1);                 // Turn on DAC speaker\r
353 \r
354  if(DSPV>=0x400)\r
355  {\r
356   WriteDSP(0x41);                // Set sampling rate\r
357   WriteDSP(Rate>>8);             // High byte\r
358   WriteDSP(Rate&0xFF);           // Low byte\r
359   if(!format)\r
360   {\r
361    WriteDSP(0xC6);                // 8-bit output\r
362    WriteDSP(0x00);                // 8-bit mono unsigned PCM\r
363   }\r
364   else\r
365   {\r
366    WriteDSP(0xB6);                // 16-bit output\r
367    WriteDSP(0x10);                // 16-bit mono signed PCM\r
368   }\r
369   WriteDSP((fragsize-1)&0xFF);// Low byte of size\r
370   WriteDSP((fragsize-1)>>8);  // High byte of size\r
371  }\r
372  else\r
373  {\r
374   int tc,command;\r
375   if(Rate>22050)\r
376   {\r
377    tc=(65536-(256000000/Rate))>>8;\r
378    Rate=256000000/(65536-(tc<<8));\r
379    command=0x90;                  // High-speed auto-initialize DMA mode transfer\r
380    hsmode=1;\r
381   }\r
382   else\r
383   {\r
384    tc=256-(1000000/Rate);\r
385    Rate=1000000/(256-tc);\r
386    command=0x1c;                  // Auto-initialize DMA mode transfer\r
387   }\r
388   WriteDSP(0x40);       // Set DSP time constant\r
389   WriteDSP(tc);         // time constant\r
390   WriteDSP(0x48);       // Set DSP block transfer size\r
391   WriteDSP((fragsize-1)&0xFF);\r
392   WriteDSP((fragsize-1)>>8);\r
393 \r
394   WriteDSP(command);\r
395  }\r
396 \r
397  /* Enable DMA */\r
398  if(format)\r
399   outportb(0xd4,SBDMA16&3);\r
400  else\r
401   outportb(0xa,SBDMA);\r
402 \r
403  printf(" %d hz, %d-bit\n",Rate,8<<format);\r
404  return(Rate);\r
405 }\r
406 \r
407 extern volatile int soundjoyer;\r
408 extern volatile int soundjoyeron;\r
409 static int ssilence=0;\r
410 \r
411 static void SBIRQHandler(_go32_dpmi_registers *r)\r
412 {\r
413         uint32 *src;\r
414         uint32 dest;\r
415         int32 x;\r
416 \r
417 \r
418         if(format)\r
419         {\r
420          uint8 status;\r
421 \r
422          outportb(SBPort+4,0x82);\r
423          status=inportb(SBPort+5);\r
424          if(status&2)\r
425           inportb(SBPort+0x0F);\r
426         }\r
427         else\r
428          inportb(SBPort+0x0E);\r
429 \r
430         #ifdef OLD\r
431         {\r
432          uint8 status;\r
433 \r
434          outportb(SBPort+4,0x82);\r
435          status=inportb(SBPort+5);\r
436          if(status&1)\r
437           inportb(SBPort+0x0E);\r
438          else if(status&2)\r
439           inportb(SBPort+0x0F);\r
440          else\r
441           return;               // Mysterious interrupt source!  *eerie music*\r
442         }         \r
443         #endif\r
444 \r
445         if(hbusy)\r
446         {\r
447          outportb(0x20,0x20);\r
448          if(SBIRQ>=8)\r
449           outportb(0xA0,0x20);\r
450          whichbuf^=1;         \r
451          return;\r
452         }\r
453         hbusy=1;\r
454 \r
455         {\r
456          /* This code seems to fail on many SB emulators.  Bah.\r
457             SCREW SB EMULATORS. ^_^ */\r
458          uint32 count;\r
459          uint32 block;\r
460          uint32 port;\r
461        \r
462          if(format)\r
463           port=((SBDMA16&3)*4)+0xc2;\r
464          else\r
465           port=(SBDMA*2)+1;\r
466 \r
467          count=inportb(port);\r
468          count|=inportb(port)<<8;\r
469 \r
470          if(count>=fragsize)\r
471           block=1;\r
472          else\r
473           block=0;\r
474          dest=LMBuffer+((block*fragsize)<<format);\r
475 \r
476          #ifdef MOO\r
477          dest=LMBuffer+((whichbuf*fragsize)<<format);\r
478          whichbuf^=1;\r
479          #endif\r
480         }\r
481 \r
482         _farsetsel(_dos_ds);\r
483 \r
484         src=(uint32 *)(WaveBuffer+(ReadPtr<<format));\r
485 \r
486         if(ssilence)\r
487         {\r
488          uint32 sby;\r
489          if(format) sby=0;      /* 16-bit silence.  */\r
490          else sby=0x80808080;   /* 8-bit silence.   */\r
491 \r
492          for(x=(fragsize<<format)>>2;x;x--,dest+=4)\r
493          {\r
494           _farnspokel(dest,sby);\r
495          }\r
496         }\r
497         else\r
498         {\r
499          for(x=(fragsize<<format)>>2;x;x--,dest+=4,src++)\r
500          {\r
501           _farnspokel(dest,*src);\r
502          }\r
503          ReadPtr=(ReadPtr+fragsize)&(fragtotal-1);\r
504         }\r
505 \r
506         if(soundjoyeron)\r
507         {\r
508          static int coot=0;\r
509          if(!coot)\r
510          {\r
511           UpdateJoyData();\r
512           soundjoyer=1;\r
513          }\r
514          coot=(coot+1)&3;\r
515         }\r
516         hbusy=0;\r
517         outportb(0x20,0x20);\r
518         if(SBIRQ>=8)        \r
519          outportb(0xA0,0x20);\r
520 }\r
521 \r
522 void SilenceSound(int s)\r
523 {\r
524  ssilence=s;\r
525 }\r
526 \r
527 void WriteSBSound(int32 *Buffer, int Count, int NoBlocking)\r
528 {\r
529  int x;\r
530 \r
531  if(!format)\r
532  {\r
533    for(x=0;x<Count;x++)\r
534    {\r
535     while(WritePtr==ReadPtr)\r
536      if(NoBlocking)\r
537       return;\r
538     WaveBuffer[WritePtr]=(uint8)((Buffer[x])>>8)^128;\r
539     WritePtr=(WritePtr+1)&(fragtotal-1);\r
540    }\r
541  }      \r
542  else // 16 bit\r
543  {\r
544    for(x=0;x<Count;x++)\r
545    {\r
546     while(WritePtr==ReadPtr)\r
547      if(NoBlocking)\r
548       return;\r
549     ((int16 *)WaveBuffer)[WritePtr]=Buffer[x];\r
550     WritePtr=(WritePtr+1)&(fragtotal-1);\r
551    }\r
552  }\r
553 }\r
554 \r
555 void KillSB(void)\r
556 {\r
557  if(hsmode)\r
558   ResetDSP();                   /* High-speed mode requires a DSP reset. */\r
559  else\r
560   WriteDSP(format?0xD9:0xDA);    /* Exit auto-init DMA transfer mode. */ \r
561  WriteDSP(0xD3);                /* Turn speaker off. */\r
562 \r
563  outportb((SBIRQ>7)?0xA1:0x21,PICMask|(1<<(SBIRQ&7)));\r
564  ResetVectors();\r
565  outportb((SBIRQ>7)?0xA1:0x21,PICMask);\r
566  KillDMABuffer();\r
567 }\r