| 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 |