source: libmikmod/vendor/current/drivers/dos/dossb.c@ 1454

Last change on this file since 1454 was 1454, checked in by valerius, 9 years ago

libmikmod: Import version 3.3.8 from vendor.

Source URL: https://sourceforge.net/projects/mikmod/files/libmikmod/3.3.8/libmikmod-3.3.8.tar.gz/download

File size: 12.8 KB
Line 
1/* MikMod sound library
2 (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
3 complete list.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19*/
20
21/*==============================================================================
22
23 Sound Blaster I/O routines, common for SB8, SBPro and SB16
24 Written by Andrew Zabolotny <bit@eltech.ru>
25
26==============================================================================*/
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#ifdef DRV_SB
33
34#include <stdlib.h>
35#include <dpmi.h>
36#include <go32.h>
37#include <dos.h>
38#include <sys/nearptr.h>
39#include <sys/farptr.h>
40#include <string.h>
41
42#include "dossb.h"
43
44/********************************************* Private variables/routines *****/
45
46__sb_state sb;
47
48/* Wait for SoundBlaster for some time */
49#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ == 0)
50# define _func_noinline volatile /* match original code */
51# define _func_noclone
52#else
53/* avoid warnings from newer gcc:
54 * "function definition has qualified void return type" and
55 * function return types not compatible due to 'volatile' */
56# define _func_noinline __attribute__((__noinline__))
57# if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
58# define _func_noclone
59# else
60# define _func_noclone __attribute__((__noclone__))
61# endif
62#endif
63_func_noinline
64_func_noclone
65 void __sb_wait()
66{
67 inportb(SB_DSP_RESET);
68 inportb(SB_DSP_RESET);
69 inportb(SB_DSP_RESET);
70 inportb(SB_DSP_RESET);
71 inportb(SB_DSP_RESET);
72 inportb(SB_DSP_RESET);
73}
74
75static void sb_irq()
76{
77 /* Make sure its not a spurious IRQ */
78 if (!irq_check(sb.irq_handle))
79 return;
80
81 sb.irqcount++;
82
83 /* Acknowledge DMA transfer is complete */
84 if (sb.mode & SBMODE_16BITS)
85 __sb_dsp_ack_dma16();
86 else
87 __sb_dsp_ack_dma8();
88
89 /* SoundBlaster 1.x cannot do autoinit ... */
90 if (sb.dspver < SBVER_20)
91 __sb_dspreg_outwlh(SBDSP_DMA_PCM8, (sb.dma_buff->size >> 1) - 1);
92
93 /* Send EOI */
94 irq_ack(sb.irq_handle);
95
96 enable();
97 if (sb.timer_callback)
98 sb.timer_callback();
99}
100
101static void sb_irq_end()
102{
103}
104
105static boolean __sb_reset()
106{
107 /* Disable the output */
108 sb_output(FALSE);
109
110 /* Clear pending ints if any */
111 __sb_dsp_ack_dma8();
112 __sb_dsp_ack_dma16();
113
114 /* Reset the DSP */
115 outportb(SB_DSP_RESET, SBM_DSP_RESET);
116 __sb_wait();
117 __sb_wait();
118 outportb(SB_DSP_RESET, 0);
119
120 /* Now wait for AA coming from datain port */
121 if (__sb_dsp_in() != 0xaa)
122 return FALSE;
123
124 /* Finally, get the DSP version */
125 if ((sb.dspver = __sb_dsp_version()) == 0xffff)
126 return FALSE;
127 /* Check again */
128 if (sb.dspver != __sb_dsp_version())
129 return FALSE;
130
131 return TRUE;
132}
133
134/***************************************************** SB detection stuff *****/
135
136static int __sb_irq_irqdetect(int irqno)
137{
138 __sb_dsp_ack_dma8();
139 return 1;
140}
141
142static void __sb_irq_dmadetect()
143{
144 /* Make sure its not a spurious IRQ */
145 if (!irq_check(sb.irq_handle))
146 return;
147
148 sb.irqcount++;
149
150 /* Acknowledge DMA transfer is complete */
151 if (sb.mode & SBMODE_16BITS)
152 __sb_dsp_ack_dma16();
153 else
154 __sb_dsp_ack_dma8();
155
156 /* Send EOI */
157 irq_ack(sb.irq_handle);
158}
159
160static boolean __sb_detect()
161{
162 /* First find the port number */
163 if (!sb.port) {
164 int i;
165 for (i = 5; i >= 0; i--) {
166 sb.port = 0x210 + i * 0x10;
167 if (__sb_reset())
168 break;
169 }
170 if (i < 0) {
171 sb.port = 0;
172 return FALSE;
173 }
174 }
175
176 /* Now detect the IRQ and DMA numbers */
177 if (!sb.irq) {
178 unsigned int irqmask, sbirqmask, sbirqcount;
179 unsigned long timer;
180
181 /* IRQ can be one of 2,3,5,7,10 */
182 irq_detect_start(0x04ac, __sb_irq_irqdetect);
183
184 /* Prepare timeout counter */
185 _farsetsel(_dos_ds);
186 timer = _farnspeekl(0x46c);
187
188 sbirqmask = 0;
189 sbirqcount = 10; /* Emit 10 SB irqs */
190
191 /* Tell SoundBlaster to emit IRQ for 8-bit transfers */
192 __sb_dsp_out(SBDSP_GEN_IRQ8);
193 __sb_wait();
194 for (;;) {
195 irq_detect_get(0, &irqmask);
196 if (irqmask) {
197 sbirqmask |= irqmask;
198 if (!--sbirqcount)
199 break;
200 __sb_dsp_out(SBDSP_GEN_IRQ8);
201 }
202 if (_farnspeekl(0x46c) - timer >= 9) /* Wait ~1/2 secs */
203 break;
204 }
205 if (sbirqmask)
206 for (sb.irq = 15; sb.irq > 0; sb.irq--)
207 if (irq_detect_get(sb.irq, &irqmask) == 10)
208 break;
209
210 irq_detect_end();
211 if (!sb.irq)
212 return FALSE;
213 }
214
215 /* Detect the 8-bit and 16-bit DMAs */
216 if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) {
217 static int __dma8[] = { 0, 1, 3 };
218 static int __dma16[] = { 5, 6, 7 };
219 int *dma;
220
221 sb_output(FALSE);
222 /* Temporary hook SB IRQ */
223 sb.irq_handle = irq_hook(sb.irq, __sb_irq_dmadetect, 200);
224 irq_enable(sb.irq_handle);
225 if (sb.irq > 7)
226 _irq_enable(2);
227
228 /* Start a short DMA transfer and check if IRQ happened */
229 for (;;) {
230 int i;
231 unsigned int timer, oldcount;
232
233 if (!sb.dma8)
234 dma = &sb.dma8;
235 else if ((sb.dspver >= SBVER_16) && !sb.dma16)
236 dma = &sb.dma16;
237 else
238 break;
239
240 for (i = 0; i < 3; i++) {
241 boolean success = 1;
242
243 *dma = (dma == &sb.dma8) ? __dma8[i] : __dma16[i];
244 oldcount = sb.irqcount;
245
246 dma_disable(*dma);
247 dma_set_mode(*dma, DMA_MODE_WRITE);
248 dma_clear_ff(*dma);
249 dma_set_count(*dma, 2);
250 dma_enable(*dma);
251
252 __sb_dspreg_out(SBDSP_SET_TIMING, 206); /* 20KHz */
253 if (dma == &sb.dma8) {
254 sb.mode = 0;
255 __sb_dspreg_outwlh(SBDSP_DMA_PCM8, 1);
256 } else {
257 sb.mode = SBMODE_16BITS;
258 __sb_dspreg_out(SBDSP_DMA_GENERIC16, 0);
259 __sb_dsp_out(0);
260 __sb_dsp_out(1);
261 }
262
263 _farsetsel(_dos_ds);
264 timer = _farnspeekl(0x46c);
265
266 while (oldcount == sb.irqcount)
267 if (_farnspeekl(0x46c) - timer >= 2) {
268 success = 0;
269 break;
270 }
271 dma_disable(*dma);
272 if (success)
273 break;
274 *dma = 0;
275 }
276 if (!*dma)
277 break;
278 }
279
280 irq_unhook(sb.irq_handle);
281 sb.irq_handle = NULL;
282 if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16))
283 return FALSE;
284 }
285 return TRUE;
286}
287
288/*************************************************** High-level interface *****/
289
290/* Detect whenever SoundBlaster is present and fill "sb" structure */
291boolean sb_detect()
292{
293 char *env;
294
295 /* Try to find the port and DMA from environment */
296 env = getenv("BLASTER");
297
298 while (env && *env) {
299 /* Skip whitespace */
300 while ((*env == ' ') || (*env == '\t'))
301 env++;
302 if (!*env)
303 break;
304
305 switch (*env++) {
306 case 'A':
307 case 'a':
308 if (!sb.port)
309 sb.port = strtol(env, &env, 16);
310 break;
311 case 'E':
312 case 'e':
313 if (!sb.aweport)
314 sb.aweport = strtol(env, &env, 16);
315 break;
316 case 'I':
317 case 'i':
318 if (!sb.irq)
319 sb.irq = strtol(env, &env, 10);
320 break;
321 case 'D':
322 case 'd':
323 if (!sb.dma8)
324 sb.dma8 = strtol(env, &env, 10);
325 break;
326 case 'H':
327 case 'h':
328 if (!sb.dma16)
329 sb.dma16 = strtol(env, &env, 10);
330 break;
331 default:
332 /* Skip other values (H == MIDI, T == model, any other?) */
333 while (*env && (*env != ' ') && (*env != '\t'))
334 env++;
335 break;
336 }
337 }
338
339 /* Try to detect missing sound card parameters */
340 __sb_detect();
341
342 if (!sb.port || !sb.irq || !sb.dma8)
343 return FALSE;
344
345 if (!__sb_reset())
346 return FALSE;
347
348 if ((sb.dspver >= SBVER_16) && !sb.dma16)
349 return FALSE;
350
351 if (sb.dspver >= SBVER_PRO)
352 sb.caps |= SBMODE_STEREO;
353 if (sb.dspver >= SBVER_16 && sb.dma16)
354 sb.caps |= SBMODE_16BITS;
355 if (sb.dspver < SBVER_20)
356 sb.maxfreq_mono = 22222;
357 else
358 sb.maxfreq_mono = 45454;
359 if (sb.dspver <= SBVER_16)
360 sb.maxfreq_stereo = 22727;
361 else
362 sb.maxfreq_stereo = 45454;
363
364 sb.ok = 1;
365 return TRUE;
366}
367
368/* Reset SoundBlaster */
369void sb_reset()
370{
371 sb_stop_dma();
372 __sb_reset();
373}
374
375/* Start working with SoundBlaster */
376boolean sb_open()
377{
378 __dpmi_meminfo struct_info;
379
380 if (!sb.ok)
381 if (!sb_detect())
382 return FALSE;
383
384 if (sb.open)
385 return FALSE;
386
387 /* Now lock the sb structure in memory */
388 struct_info.address = __djgpp_base_address + (unsigned long)&sb;
389 struct_info.size = sizeof(sb);
390 if (__dpmi_lock_linear_region(&struct_info))
391 return FALSE;
392
393 /* Hook the SB IRQ */
394 sb.irq_handle = irq_hook(sb.irq, sb_irq, (long)sb_irq_end - (long)sb_irq);
395 if (!sb.irq_handle) {
396 __dpmi_unlock_linear_region(&struct_info);
397 return FALSE;
398 }
399
400 /* Enable the interrupt */
401 irq_enable(sb.irq_handle);
402 if (sb.irq > 7)
403 _irq_enable(2);
404
405 sb.open++;
406
407 return TRUE;
408}
409
410/* Finish working with SoundBlaster */
411boolean sb_close()
412{
413 __dpmi_meminfo struct_info;
414 if (!sb.open)
415 return FALSE;
416
417 sb.open--;
418
419 /* Stop/free DMA buffer */
420 sb_stop_dma();
421
422 /* Unhook IRQ */
423 irq_unhook(sb.irq_handle);
424 sb.irq_handle = NULL;
425
426 /* Unlock the sb structure */
427 struct_info.address = __djgpp_base_address + (unsigned long)&sb;
428 struct_info.size = sizeof(sb);
429 __dpmi_unlock_linear_region(&struct_info);
430
431 return TRUE;
432}
433
434/* Enable/disable stereo DSP mode */
435/* Enable/disable speaker output */
436void sb_output(boolean enable)
437{
438 __sb_dsp_out(enable ? SBDSP_SPEAKER_ENA : SBDSP_SPEAKER_DIS);
439}
440
441/* Start playing from DMA buffer */
442boolean sb_start_dma(unsigned char mode, unsigned int freq)
443{
444 int dmachannel = (mode & SBMODE_16BITS) ? sb.dma16 : sb.dma8;
445 int dmabuffsize;
446 unsigned int tc = 0; /* timing constant (<=sbpro only) */
447
448 /* Stop DMA transfer if it is enabled */
449 sb_stop_dma();
450
451 /* Sanity check */
452 if ((mode & SBMODE_MASK & sb.caps) != (mode & SBMODE_MASK))
453 return FALSE;
454
455 /* Check this SB can perform at requested frequency */
456 if (((mode & SBMODE_STEREO) && (freq > sb.maxfreq_stereo))
457 || (!(mode & SBMODE_STEREO) && (freq > sb.maxfreq_mono)))
458 return FALSE;
459
460 /* Check the timing constant here to avoid failing later */
461 if (sb.dspver < SBVER_16) {
462 /* SBpro cannot do signed transfer */
463 if (mode & SBMODE_SIGNED)
464 return FALSE;
465
466 /* Old SBs have a different way on setting DMA timing constant */
467 tc = freq;
468 if (mode & SBMODE_STEREO)
469 tc *= 2;
470 tc = 1000000 / tc;
471 if (tc > 255)
472 return FALSE;
473 }
474
475 sb.mode = mode;
476
477 /* Get a DMA buffer enough for a 1/4sec interval... 4K <= dmasize <= 32K */
478 dmabuffsize = freq;
479 if (mode & SBMODE_STEREO)
480 dmabuffsize *= 2;
481 if (mode & SBMODE_16BITS)
482 dmabuffsize *= 2;
483 dmabuffsize >>= 2;
484 if (dmabuffsize < 4096)
485 dmabuffsize = 4096;
486 if (dmabuffsize > 32768)
487 dmabuffsize = 32768;
488 dmabuffsize = (dmabuffsize + 255) & 0xffffff00;
489
490 sb.dma_buff = dma_allocate(dmachannel, dmabuffsize);
491 if (!sb.dma_buff)
492 return FALSE;
493
494 /* Fill DMA buffer with silence */
495 dmabuffsize = sb.dma_buff->size;
496 if (mode & SBMODE_SIGNED)
497 memset(sb.dma_buff->linear, 0, dmabuffsize);
498 else
499 memset(sb.dma_buff->linear, 0x80, dmabuffsize);
500
501 /* Prime DMA for transfer */
502 dma_start(sb.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
503
504 /* Tell SoundBlaster to start transfer */
505 if (sb.dspver >= SBVER_16) { /* SB16 */
506 __sb_dspreg_outwhl(SBDSP_SET_RATE, freq);
507
508 /* Start DMA->DAC transfer */
509 __sb_dspreg_out(SBM_GENDAC_AUTOINIT | SBM_GENDAC_FIFO |
510 ((mode & SBMODE_16BITS) ? SBDSP_DMA_GENERIC16 :
511 SBDSP_DMA_GENERIC8),
512 ((mode & SBMODE_SIGNED) ? SBM_GENDAC_SIGNED : 0) |
513 ((mode & SBMODE_STEREO) ? SBM_GENDAC_STEREO : 0));
514
515 /* Write the length of transfer */
516 dmabuffsize = (dmabuffsize >> 2) - 1;
517 __sb_dsp_out(dmabuffsize);
518 __sb_dsp_out(dmabuffsize >> 8);
519 } else {
520 __sb_dspreg_out(SBDSP_SET_TIMING, 256 - tc);
521 dmabuffsize = (dmabuffsize >> 1) - 1;
522 if (sb.dspver >= SBVER_20) { /* SB 2.0/Pro */
523 /* Set stereo mode */
524 __sb_stereo((mode & SBMODE_STEREO) ? TRUE : FALSE);
525 __sb_dspreg_outwlh(SBDSP_SET_DMA_BLOCK, dmabuffsize);
526 if (sb.dspver >= SBVER_PRO)
527 __sb_dsp_out(SBDSP_HS_DMA_DAC8_AUTO);
528 else
529 __sb_dsp_out(SBDSP_DMA_PCM8_AUTO);
530 } else { /* Original SB */
531 /* Start DMA->DAC transfer */
532 __sb_dspreg_outwlh(SBDSP_DMA_PCM8, dmabuffsize);
533 }
534 }
535
536 return TRUE;
537}
538
539/* Stop playing from DMA buffer */
540void sb_stop_dma()
541{
542 if (!sb.dma_buff)
543 return;
544
545 if (sb.mode & SBMODE_16BITS)
546 __sb_dsp_out(SBDSP_DMA_HALT16);
547 else
548 __sb_dsp_out(SBDSP_DMA_HALT8);
549
550 dma_disable(sb.dma_buff->channel);
551 dma_free(sb.dma_buff);
552 sb.dma_buff = NULL;
553}
554
555/* Query current position/total size of the DMA buffer */
556void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos)
557{
558 unsigned int dma_left;
559 *dma_size = sb.dma_buff->size;
560 /* It can happen we try to read DMA count when HI/LO bytes will be
561 inconsistent */
562 for (;;) {
563 unsigned int dma_left_test;
564 dma_clear_ff(sb.dma_buff->channel);
565 dma_left_test = dma_get_count(sb.dma_buff->channel);
566 dma_left = dma_get_count(sb.dma_buff->channel);
567 if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10))
568 break;
569 }
570 *dma_pos = *dma_size - dma_left;
571}
572
573#endif /* DRV_SB */
574
575/* ex:set ts=4: */
Note: See TracBrowser for help on using the repository browser.