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