1 /* FCE Ultra - NES/Famicom Emulator
\r
3 * Copyright notice for this file:
\r
4 * Copyright (C) 2002 Ben Parnell
\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
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
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
24 #include <sys/farptr.h>
\r
32 #include "dos-sound.h"
\r
33 #include "dos-joystick.h"
\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
40 static uint8 *WaveBuffer;
\r
41 static unsigned int IVector, SBIRQ, SBDMA, SBDMA16, SBPort;
\r
42 static int DSPV,hsmode;
\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
50 static uint8 PICMask;
\r
51 /* Protected mode interrupt vector info. */
\r
52 static _go32_dpmi_seginfo SBIH,SBIHOld;
\r
54 /* Real mode interrupt vector info. */
\r
55 static _go32_dpmi_seginfo SBIHRM,SBIHRMOld;
\r
56 static _go32_dpmi_registers SBIHRMRegs;
\r
58 static int WriteDSP(uint8 V)
\r
64 if(!(inportb(SBPort+0xC)&0x80))
\r
66 outportb(SBPort+0xC,V);
\r
73 static int ReadDSP(uint8 *V)
\r
77 for(x=65536;x;x--) /* Should be more than enough time... */
\r
79 if(inportb(SBPort+0xE)&0x80)
\r
81 *V=inportb(SBPort+0xA);
\r
89 static int SetVectors(void)
\r
91 SBIH.pm_offset=SBIHRM.pm_offset=(int)SBIRQHandler;
\r
92 SBIH.pm_selector=SBIHRM.pm_selector=_my_cs();
\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
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
107 static void ResetVectors(void)
\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
115 int GetBLASTER(void)
\r
120 if(!(s=getenv("BLASTER")))
\r
122 puts(" Error getting BLASTER environment variable.");
\r
128 switch(toupper(*s))
\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
138 if((check^7)&7 || SBDMA>=4 || (SBDMA16<=4 && check&8) || SBIRQ>15)
\r
140 puts(" Invalid or incomplete BLASTER environment variable.");
\r
148 static int ResetDSP(void)
\r
152 outportb(SBPort+0x6,0x1);
\r
154 outportb(SBPort+0x6,0x0);
\r
163 static int GetDSPVersion(void)
\r
168 if(!WriteDSP(0xE1))
\r
180 static void KillDMABuffer(void)
\r
182 __dpmi_free_dos_memory(LMSelector);
\r
185 static int MakeDMABuffer(void)
\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
193 size<<=1; /* Double the size in case the first 2 buffers
\r
194 cross a 64KB or 128KB page boundary.
\r
196 size=(size+15)>>4; /* Convert to paragraphs */
\r
198 if((tmp=__dpmi_allocate_dos_memory(size,&LMSelector))<0)
\r
203 if(format) /* Check for and fix 128KB page boundary crossing. */
\r
205 if((LMBuffer&0x20000) != ((LMBuffer+fragsize*2*2-1)&0x20000))
\r
206 LMBuffer+=fragsize*2*2;
\r
208 else /* Check for and fix 64KB page boundary crossing. */
\r
210 if((LMBuffer&0x10000) != ((LMBuffer+fragsize*2-1)&0x10000))
\r
211 LMBuffer+=fragsize*2;
\r
214 DOSMemSet(LMBuffer, format?0:128, (fragsize*2)<<format);
\r
219 static void ProgramDMA(void)
\r
221 static int PPorts[8]={0x87,0x83,0x81,0x82,0,0x8b,0x89,0x8a};
\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
233 outportb(0xA,SBDMA|0x4);
\r
235 outportb(0xB,SBDMA|0x58);
\r
239 /* Size of entire buffer. */
\r
240 outportb(tmp,(fragsize*2-1));
\r
241 outportb(tmp,(fragsize*2-1)>>8);
\r
243 /* Page of buffer. */
\r
244 outportb(PPorts[format?SBDMA16:SBDMA],LMBuffer>>16);
\r
246 /* Offset of buffer within page. */
\r
248 tmp=((SBDMA16&3)<<2)+0xc0;
\r
252 outportb(tmp,(LMBuffer>>format));
\r
253 outportb(tmp,(LMBuffer>>(8+format)));
\r
256 int InitSB(int Rate, int bittage)
\r
260 puts("Initializing Sound Blaster...");
\r
262 format=bittage?1:0;
\r
267 else if(Rate<=22050)
\r
272 fragtotal=frags*fragsize;
\r
273 WaveBuffer=malloc(fragtotal<<format);
\r
276 memset(WaveBuffer,0,fragtotal*2);
\r
278 memset(WaveBuffer,128,fragtotal);
\r
280 WritePtr=ReadPtr=0;
\r
282 if((Rate<8192) || (Rate>65535))
\r
284 printf(" Unsupported playback rate: %d samples per second\n",Rate);
\r
291 /* Disable IRQ line in PIC0 or PIC1 */
\r
294 PICMask=inportb(0xA1);
\r
295 outportb(0xA1,PICMask|(1<<(SBIRQ&7)));
\r
299 PICMask=inportb(0x21);
\r
300 outportb(0x21,PICMask|(1<<SBIRQ));
\r
304 puts(" Error resetting the DSP.");
\r
308 if(!(DSPV=GetDSPVersion()))
\r
310 puts(" Error getting the DSP version.");
\r
314 printf(" DSP Version: %d.%d\n",DSPV>>8,DSPV&0xFF);
\r
317 printf(" DSP version number is too low.\n");
\r
323 if(!MakeDMABuffer())
\r
325 puts(" Error creating low-memory DMA buffer.");
\r
329 if(SBIRQ>7) IVector=SBIRQ+0x68;
\r
330 else IVector=SBIRQ+0x8;
\r
334 puts(" Error setting interrupt vectors.");
\r
339 /* Reenable IRQ line. */
\r
341 outportb(0xA1,PICMask&(~(1<<(SBIRQ&7))));
\r
343 outportb(0x21,PICMask&(~(1<<SBIRQ)));
\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
352 WriteDSP(0xD1); // Turn on DAC speaker
\r
356 WriteDSP(0x41); // Set sampling rate
\r
357 WriteDSP(Rate>>8); // High byte
\r
358 WriteDSP(Rate&0xFF); // Low byte
\r
361 WriteDSP(0xC6); // 8-bit output
\r
362 WriteDSP(0x00); // 8-bit mono unsigned PCM
\r
366 WriteDSP(0xB6); // 16-bit output
\r
367 WriteDSP(0x10); // 16-bit mono signed PCM
\r
369 WriteDSP((fragsize-1)&0xFF);// Low byte of size
\r
370 WriteDSP((fragsize-1)>>8); // High byte of size
\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
384 tc=256-(1000000/Rate);
\r
385 Rate=1000000/(256-tc);
\r
386 command=0x1c; // Auto-initialize DMA mode transfer
\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
399 outportb(0xd4,SBDMA16&3);
\r
401 outportb(0xa,SBDMA);
\r
403 printf(" %d hz, %d-bit\n",Rate,8<<format);
\r
407 extern volatile int soundjoyer;
\r
408 extern volatile int soundjoyeron;
\r
409 static int ssilence=0;
\r
411 static void SBIRQHandler(_go32_dpmi_registers *r)
\r
422 outportb(SBPort+4,0x82);
\r
423 status=inportb(SBPort+5);
\r
425 inportb(SBPort+0x0F);
\r
428 inportb(SBPort+0x0E);
\r
434 outportb(SBPort+4,0x82);
\r
435 status=inportb(SBPort+5);
\r
437 inportb(SBPort+0x0E);
\r
439 inportb(SBPort+0x0F);
\r
441 return; // Mysterious interrupt source! *eerie music*
\r
447 outportb(0x20,0x20);
\r
449 outportb(0xA0,0x20);
\r
456 /* This code seems to fail on many SB emulators. Bah.
\r
457 SCREW SB EMULATORS. ^_^ */
\r
463 port=((SBDMA16&3)*4)+0xc2;
\r
467 count=inportb(port);
\r
468 count|=inportb(port)<<8;
\r
470 if(count>=fragsize)
\r
474 dest=LMBuffer+((block*fragsize)<<format);
\r
477 dest=LMBuffer+((whichbuf*fragsize)<<format);
\r
482 _farsetsel(_dos_ds);
\r
484 src=(uint32 *)(WaveBuffer+(ReadPtr<<format));
\r
489 if(format) sby=0; /* 16-bit silence. */
\r
490 else sby=0x80808080; /* 8-bit silence. */
\r
492 for(x=(fragsize<<format)>>2;x;x--,dest+=4)
\r
494 _farnspokel(dest,sby);
\r
499 for(x=(fragsize<<format)>>2;x;x--,dest+=4,src++)
\r
501 _farnspokel(dest,*src);
\r
503 ReadPtr=(ReadPtr+fragsize)&(fragtotal-1);
\r
517 outportb(0x20,0x20);
\r
519 outportb(0xA0,0x20);
\r
522 void SilenceSound(int s)
\r
527 void WriteSBSound(int32 *Buffer, int Count, int NoBlocking)
\r
533 for(x=0;x<Count;x++)
\r
535 while(WritePtr==ReadPtr)
\r
538 WaveBuffer[WritePtr]=(uint8)((Buffer[x])>>8)^128;
\r
539 WritePtr=(WritePtr+1)&(fragtotal-1);
\r
544 for(x=0;x<Count;x++)
\r
546 while(WritePtr==ReadPtr)
\r
549 ((int16 *)WaveBuffer)[WritePtr]=Buffer[x];
\r
550 WritePtr=(WritePtr+1)&(fragtotal-1);
\r
558 ResetDSP(); /* High-speed mode requires a DSP reset. */
\r
560 WriteDSP(format?0xD9:0xDA); /* Exit auto-init DMA transfer mode. */
\r
561 WriteDSP(0xD3); /* Turn speaker off. */
\r
563 outportb((SBIRQ>7)?0xA1:0x21,PICMask|(1<<(SBIRQ&7)));
\r
565 outportb((SBIRQ>7)?0xA1:0x21,PICMask);
\r