/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2002 Xodnizel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include "dos.h" #include "dos-sound.h" #include "dos-joystick.h" static void SBIRQHandler(_go32_dpmi_registers *r); static uint32 LMBuffer; /* Address of low memory DMA playback buffer. */ static int LMSelector; static uint8 *WaveBuffer; static unsigned int IVector, SBIRQ, SBDMA, SBDMA16, SBPort; static int DSPV,hsmode; static int format; static int frags, fragsize, fragtotal; static volatile int WritePtr, ReadPtr; static volatile int hbusy; static volatile int whichbuf; static uint8 PICMask; /* Protected mode interrupt vector info. */ static _go32_dpmi_seginfo SBIH,SBIHOld; /* Real mode interrupt vector info. */ static _go32_dpmi_seginfo SBIHRM,SBIHRMOld; static _go32_dpmi_registers SBIHRMRegs; static int WriteDSP(uint8 V) { int x; for(x=65536;x;x--) { if(!(inportb(SBPort+0xC)&0x80)) { outportb(SBPort+0xC,V); return(1); } } return(0); } static int ReadDSP(uint8 *V) { int x; for(x=65536;x;x--) /* Should be more than enough time... */ { if(inportb(SBPort+0xE)&0x80) { *V=inportb(SBPort+0xA); return(1); } } return(0); } static int SetVectors(void) { SBIH.pm_offset=SBIHRM.pm_offset=(int)SBIRQHandler; SBIH.pm_selector=SBIHRM.pm_selector=_my_cs(); /* Get and set real mode interrupt vector. */ _go32_dpmi_get_real_mode_interrupt_vector(IVector,&SBIHRMOld); _go32_dpmi_allocate_real_mode_callback_iret(&SBIHRM, &SBIHRMRegs); _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRM); /* Get and set protected mode interrupt vector. */ _go32_dpmi_get_protected_mode_interrupt_vector(IVector,&SBIHOld); _go32_dpmi_allocate_iret_wrapper(&SBIH); _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIH); return(1); } static void ResetVectors(void) { _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIHOld); _go32_dpmi_free_iret_wrapper(&SBIH); _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRMOld); _go32_dpmi_free_real_mode_callback(&SBIHRM); } int GetBLASTER(void) { int check=0; char *s; if(!(s=getenv("BLASTER"))) { puts(" Error getting BLASTER environment variable."); return(0); } while(*s) { switch(toupper(*s)) { case 'A': check|=(sscanf(s+1,"%x",&SBPort)==1)?1:0;break; case 'I': check|=(sscanf(s+1,"%d",&SBIRQ)==1)?2:0;break; case 'D': check|=(sscanf(s+1,"%d",&SBDMA)==1)?4:0;break; case 'H': check|=(sscanf(s+1,"%d",&SBDMA16)==1)?8:0;break; } s++; } if((check^7)&7 || SBDMA>=4 || (SBDMA16<=4 && check&8) || SBIRQ>15) { puts(" Invalid or incomplete BLASTER environment variable."); return(0); } if(!(check&8)) format=0; return(1); } static int ResetDSP(void) { uint8 b; outportb(SBPort+0x6,0x1); delay(10); outportb(SBPort+0x6,0x0); delay(10); if(ReadDSP(&b)) if(b==0xAA) return(1); return(0); } static int GetDSPVersion(void) { int ret; uint8 t; if(!WriteDSP(0xE1)) return(0); if(!ReadDSP(&t)) return(0); ret=t<<8; if(!ReadDSP(&t)) return(0); ret|=t; return(ret); } static void KillDMABuffer(void) { __dpmi_free_dos_memory(LMSelector); } static int MakeDMABuffer(void) { uint32 size; int32 tmp; size=fragsize*2; /* Two buffers in the DMA buffer. */ size<<=format; /* Twice the size for 16-bit than for 8-bit. */ size<<=1; /* Double the size in case the first 2 buffers cross a 64KB or 128KB page boundary. */ size=(size+15)>>4; /* Convert to paragraphs */ if((tmp=__dpmi_allocate_dos_memory(size,&LMSelector))<0) return(0); LMBuffer=tmp<<=4; if(format) /* Check for and fix 128KB page boundary crossing. */ { if((LMBuffer&0x20000) != ((LMBuffer+fragsize*2*2-1)&0x20000)) LMBuffer+=fragsize*2*2; } else /* Check for and fix 64KB page boundary crossing. */ { if((LMBuffer&0x10000) != ((LMBuffer+fragsize*2-1)&0x10000)) LMBuffer+=fragsize*2; } DOSMemSet(LMBuffer, format?0:128, (fragsize*2)<>8); /* Page of buffer. */ outportb(PPorts[format?SBDMA16:SBDMA],LMBuffer>>16); /* Offset of buffer within page. */ if(format) tmp=((SBDMA16&3)<<2)+0xc0; else tmp=SBDMA<<1; outportb(tmp,(LMBuffer>>format)); outportb(tmp,(LMBuffer>>(8+format))); } int InitSB(int Rate, int bittage) { hsmode=hbusy=0; whichbuf=1; puts("Initializing Sound Blaster..."); format=bittage?1:0; frags=8; if(Rate<=11025) fragsize=1<<5; else if(Rate<=22050) fragsize=1<<6; else fragsize=1<<7; fragtotal=frags*fragsize; WaveBuffer=malloc(fragtotal<65535)) { printf(" Unsupported playback rate: %d samples per second\n",Rate); return(0); } if(!GetBLASTER()) return(0); /* Disable IRQ line in PIC0 or PIC1 */ if(SBIRQ>7) { PICMask=inportb(0xA1); outportb(0xA1,PICMask|(1<<(SBIRQ&7))); } else { PICMask=inportb(0x21); outportb(0x21,PICMask|(1<>8,DSPV&0xFF); if(DSPV<0x201) { printf(" DSP version number is too low.\n"); return(0); } if(DSPV<0x400) format=0; if(!MakeDMABuffer()) { puts(" Error creating low-memory DMA buffer."); return(0); } if(SBIRQ>7) IVector=SBIRQ+0x68; else IVector=SBIRQ+0x8; if(!SetVectors()) { puts(" Error setting interrupt vectors."); KillDMABuffer(); return(0); } /* Reenable IRQ line. */ if(SBIRQ>7) outportb(0xA1,PICMask&(~(1<<(SBIRQ&7)))); else outportb(0x21,PICMask&(~(1<=0x400) { WriteDSP(0x41); // Set sampling rate WriteDSP(Rate>>8); // High byte WriteDSP(Rate&0xFF); // Low byte if(!format) { WriteDSP(0xC6); // 8-bit output WriteDSP(0x00); // 8-bit mono unsigned PCM } else { WriteDSP(0xB6); // 16-bit output WriteDSP(0x10); // 16-bit mono signed PCM } WriteDSP((fragsize-1)&0xFF);// Low byte of size WriteDSP((fragsize-1)>>8); // High byte of size } else { int tc,command; if(Rate>22050) { tc=(65536-(256000000/Rate))>>8; Rate=256000000/(65536-(tc<<8)); command=0x90; // High-speed auto-initialize DMA mode transfer hsmode=1; } else { tc=256-(1000000/Rate); Rate=1000000/(256-tc); command=0x1c; // Auto-initialize DMA mode transfer } WriteDSP(0x40); // Set DSP time constant WriteDSP(tc); // time constant WriteDSP(0x48); // Set DSP block transfer size WriteDSP((fragsize-1)&0xFF); WriteDSP((fragsize-1)>>8); WriteDSP(command); } /* Enable DMA */ if(format) outportb(0xd4,SBDMA16&3); else outportb(0xa,SBDMA); printf(" %d hz, %d-bit\n",Rate,8<=8) outportb(0xA0,0x20); whichbuf^=1; return; } hbusy=1; { /* This code seems to fail on many SB emulators. Bah. SCREW SB EMULATORS. ^_^ */ uint32 count; uint32 block; uint32 port; if(format) port=((SBDMA16&3)*4)+0xc2; else port=(SBDMA*2)+1; count=inportb(port); count|=inportb(port)<<8; if(count>=fragsize) block=1; else block=0; dest=LMBuffer+((block*fragsize)<>2;x;x--,dest+=4) { _farnspokel(dest,sby); } } else { for(x=(fragsize<>2;x;x--,dest+=4,src++) { _farnspokel(dest,*src); } ReadPtr=(ReadPtr+fragsize)&(fragtotal-1); } if(soundjoyeron) { static int coot=0; if(!coot) { UpdateJoyData(); soundjoyer=1; } coot=(coot+1)&3; } hbusy=0; outportb(0x20,0x20); if(SBIRQ>=8) outportb(0xA0,0x20); } void SilenceSound(int s) { ssilence=s; } void WriteSBSound(int32 *Buffer, int Count, int NoBlocking) { int x; if(!format) { for(x=0;x>8)^128; WritePtr=(WritePtr+1)&(fragtotal-1); } } else // 16 bit { for(x=0;x7)?0xA1:0x21,PICMask|(1<<(SBIRQ&7))); ResetVectors(); outportb((SBIRQ>7)?0xA1:0x21,PICMask); KillDMABuffer(); }