This is vicesb.c in view mode; [Download] [Up]
/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * By Shawn Hargreaves, * 1 Salisbury Road, * Market Drayton, * Shropshire, * England, TF9 1AJ. * * Soundblaster driver: supports DMA driven sample playback. * * Adapted for use with VICE by Ettore Perazzoli (ettore@comm2000.it). * * This file is now part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * 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. * */ /* Warning: This might not work with versions of Allegro other than 3.0. */ #ifndef DJGPP #error This file should only be used by the djgpp version of Allegro #endif #include <stdlib.h> #include <stdio.h> #include <dos.h> #include <go32.h> #include <dpmi.h> #include <limits.h> #include <sys/farptr.h> #include <allegro.h> /* Extern declarations for internal Allegro stuff. */ extern int _sb_port; extern int _sb_dma; extern int _sb_irq; extern int _dma_allocate_mem(int bytes, int *sel, unsigned long *phys); extern void _dma_start(int channel, unsigned long addr, int size, int auto_init); extern void _dma_stop(int channel); extern unsigned long _dma_todo(int channel); extern int _install_irq(int num, int (*handler)()); extern void _remove_irq(int num); static char vicesb_desc[80] = "not initialised"; static int vicesb_in_use = FALSE; /* is SB being used? */ static int vicesb_stereo = FALSE; /* in stereo mode? */ static int vicesb_16bit = FALSE; /* in 16 bit mode? */ static int vicesb_int = -1; /* interrupt vector */ static int vicesb_dsp_ver = -1; /* SB DSP version */ static int vicesb_hw_dsp_ver = -1; /* as reported by autodetect */ static int vicesb_dma_size = -1; /* size of dma transfer in bytes */ static int vicesb_dma_count = 0; /* need to resync with dma? */ static int vicesb_max_freq = 0; /* maximum frequency */ static int vicesb_freq = 0; /* sample rate */ static volatile int vicesb_semaphore = FALSE; /* reentrant interrupt? */ static void (*vicesb_interrupt_function)(unsigned long buf); static int vicesb_sel[2]; /* selectors for the buffers */ static unsigned long vicesb_buf[2]; /* pointers to the two buffers */ static int vicesb_bufnum = 0; /* the one currently in use */ static unsigned char vicesb_default_pic1; /* PIC mask flags to restore */ static unsigned char vicesb_default_pic2; static int vicesb_master_vol; /* stored mixer settings */ static int vicesb_digi_vol; static void vicesb_lock_mem(); /* vicesb_read_dsp: * Reads a byte from the VICESB DSP chip. Returns -1 if it times out. */ static inline volatile int vicesb_read_dsp() { int x; for (x = 0; x < 0xffff; x++) if (inportb(0x0E + _sb_port) & 0x80) return inportb(0x0A + _sb_port); return -1; } /* vicesb_write_dsp: * Writes a byte to the SB DSP chip. Returns -1 if it times out. */ static inline volatile int vicesb_write_dsp(unsigned char byte) { int x; for (x = 0; x < 0xffff; x++) { if (!(inportb(0x0C + _sb_port) & 0x80)) { outportb(0x0C + _sb_port, byte); return 0; } } return -1; } /* vicesb_voice: * Turns the SB speaker on or off. */ static void vicesb_voice(int state) { if (state) { vicesb_write_dsp(0xD1); if (vicesb_hw_dsp_ver >= 0x300) { /* set up the mixer */ outportb(_sb_port + 4, 0x22); /* store master volume */ vicesb_master_vol = inportb(_sb_port + 5); outportb(_sb_port + 4, 4); /* store DAC level */ vicesb_digi_vol = inportb(_sb_port + 5); } } else { vicesb_write_dsp(0xD3); if (vicesb_hw_dsp_ver >= 0x300) { /* reset previous mixer settings */ outportb(_sb_port + 4, 0x22); /* restore master volume */ outportb(_sb_port + 5, vicesb_master_vol); outportb(_sb_port + 4, 4); /* restore DAC level */ outportb(_sb_port + 5, vicesb_digi_vol); } } } /* _vicesb_set_mixer: * Alters the SB-Pro hardware mixer. */ int vicesb_set_volume(int volume) { if (vicesb_hw_dsp_ver < 0x300) return -1; if (volume >= 0) { /* set DAC level */ outportb(_sb_port + 4, 4); outportb(_sb_port + 5, (volume & 0xF0) | (volume >> 4)); } return 0; } /* vicesb_stereo_mode: * Enables or disables stereo output for SB-Pro. */ static void vicesb_stereo_mode(int enable) { outportb(_sb_port + 0x04, 0x0E); outportb(_sb_port + 0x05, (enable ? 2 : 0)); } /* vicesb_set_sample_rate: * The parameter is the rate to set in Hz (samples per second). */ static void vicesb_set_sample_rate(unsigned int rate) { if (vicesb_16bit) { vicesb_write_dsp(0x41); vicesb_write_dsp(rate >> 8); vicesb_write_dsp(rate & 0xff); } else { if (vicesb_stereo) rate *= 2; vicesb_write_dsp(0x40); vicesb_write_dsp((unsigned char) (256 - 1000000 / rate)); } } /* vicesb_reset_dsp: * Resets the SB DSP chip, returning -1 on error. */ static int vicesb_reset_dsp() { int x; outportb(0x06 + _sb_port, 1); for (x = 0; x < 8; x++) inportb(0x06 + _sb_port); outportb(0x06 + _sb_port, 0); if (vicesb_read_dsp() != 0xAA) return -1; return 0; } /* vicesb_read_dsp_version: * Reads the version number of the SB DSP chip, returning -1 on error. */ static int vicesb_read_dsp_version(void) { int x, y; if (vicesb_hw_dsp_ver > 0) return vicesb_hw_dsp_ver; if (_sb_port <= 0) _sb_port = 0x220; if (vicesb_reset_dsp() != 0) vicesb_hw_dsp_ver = -1; else { vicesb_write_dsp(0xE1); x = vicesb_read_dsp(); y = vicesb_read_dsp(); vicesb_hw_dsp_ver = ((x << 8) | y); } return vicesb_hw_dsp_ver; } /* vicesb_play_buffer: * Starts a dma transfer of size bytes. On cards capable of it, the * transfer will use auto-initialised dma, so there is no need to call * this routine more than once. On older cards it must be called from * the end-of-buffer handler to switch to the new buffer. */ static void vicesb_play_buffer(int size) { if (vicesb_dsp_ver <= 0x200) { /* 8 bit single-shot */ vicesb_write_dsp(0x14); vicesb_write_dsp((size - 1) & 0xFF); vicesb_write_dsp((size - 1) >> 8); } else if (vicesb_dsp_ver < 0x400) { /* 8 bit auto-initialised */ vicesb_write_dsp(0x48); vicesb_write_dsp((size - 1) & 0xff); vicesb_write_dsp((size - 1) >> 8); vicesb_write_dsp(0x90); } else { /* 16 bit */ size /= 2; vicesb_write_dsp(0xB6); vicesb_write_dsp(0x10); /* vicesb_write_dsp(0x20); */ vicesb_write_dsp((size - 1) & 0xFF); vicesb_write_dsp((size - 1) >> 8); } } static END_OF_FUNCTION(vicesb_play_buffer); /* vicesb_interrupt: * The SB end-of-buffer interrupt handler. Swaps to the other buffer * if the card doesn't have auto-initialised dma, and then refills the * buffer that just finished playing. */ static int vicesb_interrupt(void) { if (vicesb_dsp_ver <= 0x200) { /* not auto-initialised */ _dma_start(_sb_dma, vicesb_buf[1 - vicesb_bufnum], vicesb_dma_size, FALSE); vicesb_play_buffer(vicesb_dma_size); } else { /* poll dma position */ vicesb_dma_count++; if (vicesb_dma_count > 16) { vicesb_bufnum = ((_dma_todo(_sb_dma) > (unsigned) vicesb_dma_size) ? 1 : 0); vicesb_dma_count = 0; } } if (!vicesb_semaphore) { vicesb_semaphore = TRUE; asm volatile ("sti"); vicesb_interrupt_function((unsigned long) vicesb_buf[vicesb_bufnum]); asm volatile ("cli"); vicesb_semaphore = FALSE; } vicesb_bufnum = 1 - vicesb_bufnum; if (vicesb_16bit) /* acknowlege SB */ inportb(_sb_port + 0x0F); else inportb(_sb_port + 0x0E); outportb(0x20, 0x20); /* acknowledge interrupt */ outportb(0xA0, 0x20); return 0; } static END_OF_FUNCTION(vicesb_interrupt); /* vicesb_detect: * SB detection routine. Uses the BLASTER environment variable, * or 'sensible' guesses if that doesn't exist. */ int vicesb_detect(int *is_16bit) { char *blaster = getenv("BLASTER"); char *msg; int dma8 = 1; int dma16 = 5; int default_freq; /* what breed of SB are we looking for? */ switch (digi_card) { case DIGI_SB10: vicesb_dsp_ver = 0x100; break; case DIGI_SB15: vicesb_dsp_ver = 0x200; break; case DIGI_SB20: vicesb_dsp_ver = 0x201; break; case DIGI_SBPRO: vicesb_dsp_ver = 0x300; break; case DIGI_SB16: vicesb_dsp_ver = 0x400; break; default: vicesb_dsp_ver = -1; break; } /* parse BLASTER env */ if (blaster) { while (*blaster) { while ((*blaster == ' ') || (*blaster == '\t')) blaster++; if (*blaster) { switch (*blaster) { case 'a': case 'A': if (_sb_port < 0) _sb_port = strtol(blaster + 1, NULL, 16); break; case 'i': case 'I': if (_sb_irq < 0) _sb_irq = strtol(blaster + 1, NULL, 10); break; case 'd': case 'D': dma8 = strtol(blaster + 1, NULL, 10); break; case 'h': case 'H': dma16 = strtol(blaster + 1, NULL, 10); break; } while ((*blaster) && (*blaster != ' ') && (*blaster != '\t')) blaster++; } } } if (_sb_port < 0) _sb_port = 0x220; if (_sb_irq < 0) /* _sb_irq = 7; */ _sb_irq = 5; /* make sure we got a good port address */ if (vicesb_reset_dsp() != 0) { static int bases[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0}; int i; for (i = 0; bases[i]; i++) { _sb_port = bases[i]; if (vicesb_reset_dsp() == 0) break; } } /* check if the card really exists */ vicesb_read_dsp_version(); if (vicesb_hw_dsp_ver < 0) { strcpy(allegro_error, "Sound Blaster not found"); return FALSE; } if (vicesb_dsp_ver < 0) vicesb_dsp_ver = vicesb_hw_dsp_ver; else { if (vicesb_dsp_ver > vicesb_hw_dsp_ver) { vicesb_hw_dsp_ver = vicesb_dsp_ver = -1; strcpy(allegro_error, "Older SB version detected"); return FALSE; } } /* figure out the hardware interrupt number */ if (_sb_irq > 7) vicesb_int = _sb_irq + 104; else vicesb_int = _sb_irq + 8; /* what breed of SB? */ if (vicesb_dsp_ver >= 0x400) { msg = "SB 16"; vicesb_max_freq = 45454; } else if (vicesb_dsp_ver >= 0x300) { msg = "SB Pro"; vicesb_max_freq = 22727; } else if (vicesb_dsp_ver >= 0x201) { msg = "SB 2.0"; vicesb_max_freq = 45454; } else if (vicesb_dsp_ver >= 0x200) { msg = "SB 1.5"; vicesb_max_freq = 16129; } else { msg = "SB 1.0"; vicesb_max_freq = 16129; } /* can we handle 16 bit playback? */ if (vicesb_dsp_ver >= 0x400) { if (_sb_dma < 0) _sb_dma = dma16; vicesb_16bit = TRUE; } else { if (_sb_dma < 0) _sb_dma = dma8; vicesb_16bit = FALSE; } #if 0 /* can we handle stereo? */ if (vicesb_dsp_ver >= 0x300) { vicesb_stereo = TRUE; vicesb_dma_size <<= 1; } else vicesb_stereo = FALSE; #else vicesb_stereo = FALSE; #endif /* set up the card description */ sprintf(vicesb_desc, "%s on port %X, using IRQ %d and DMA channel %d", msg, _sb_port, _sb_irq, _sb_dma); puts(vicesb_desc); *is_16bit = vicesb_16bit; return TRUE; } /* vicesb_init: * SB init routine: returns zero on success, -1 on failure. */ int vicesb_init(int *frequency, int *dma_size, void (*interrupt_func)(unsigned long)) { /* XXX: Do we really need this? */ if ((digi_card == DIGI_SB) || (digi_card == DIGI_AUTODETECT)) { if (vicesb_dsp_ver <= 0x100) digi_card = DIGI_SB10; else if (vicesb_dsp_ver <= 0x200) digi_card = DIGI_SB15; else if (vicesb_dsp_ver < 0x300) digi_card = DIGI_SB20; else if (vicesb_dsp_ver < 0x400) digi_card = DIGI_SBPRO; else digi_card = DIGI_SB16; } if (*frequency > vicesb_max_freq) *frequency = vicesb_max_freq; vicesb_freq = *frequency; /* Check DMA size. */ { int min_factor; printf("%s(): Fixing DMA size; requested %d\n", __FUNCTION__, *dma_size); if (vicesb_dsp_ver <= 0x200) min_factor = 4; else min_factor = 1; if (vicesb_freq < 20000) { if (*dma_size < 128 * min_factor) *dma_size = 128 * min_factor; } else if (vicesb_freq < 40000) { if (*dma_size < 256 * min_factor) *dma_size = 256 * min_factor; } else { if (*dma_size < 512 * min_factor) *dma_size = 512 * min_factor; } vicesb_dma_size = *dma_size; printf("%s(): Fixing DMA size; given %d\n", __FUNCTION__, *dma_size); } if (vicesb_dsp_ver <= 0x200) { /* two conventional mem buffers */ if ((_dma_allocate_mem(vicesb_dma_size, &vicesb_sel[0], &vicesb_buf[0]) != 0) || (_dma_allocate_mem(vicesb_dma_size, &vicesb_sel[1], &vicesb_buf[1]) != 0)) return -1; } else { /* auto-init dma, one big buffer */ if (_dma_allocate_mem(vicesb_dma_size * 2, &vicesb_sel[0], &vicesb_buf[0]) != 0) return -1; vicesb_sel[1] = vicesb_sel[0]; vicesb_buf[1] = vicesb_buf[0] + vicesb_dma_size; } { /* Clear buffers. */ int i; _farsetsel(_dos_ds); if (vicesb_16bit) { for (i = 0; i < vicesb_dma_size; i += 2) { _farnspokew(vicesb_buf[0] + i, 0x0); _farnspokew(vicesb_buf[1] + i, 0x0); } } else { for (i = 0; i < vicesb_dma_size; i++) { _farnspokeb(vicesb_buf[0] + i, 0x80); _farnspokeb(vicesb_buf[1] + i, 0x80); } } } vicesb_lock_mem(); vicesb_bufnum = 0; vicesb_default_pic1 = inportb(0x21); vicesb_default_pic2 = inportb(0xA1); if (_sb_irq > 7) { /* enable irq2 and PIC-2 irq */ outportb(0x21, vicesb_default_pic1 & 0xFB); outportb(0xA1, vicesb_default_pic2 & (~(1 << (_sb_irq - 8)))); } else /* enable PIC-1 irq */ outportb(0x21, vicesb_default_pic1 & (~(1 << _sb_irq))); if (_install_irq(vicesb_int, vicesb_interrupt) < 0) { fprintf(stderr, "Cannot install IRQ %d handler for SB!\n", vicesb_int); return FALSE; } vicesb_voice(1); vicesb_set_sample_rate(*frequency); #if 0 if ((vicesb_hw_dsp_ver >= 0x300) && (vicesb_dsp_ver < 0x400)) vicesb_stereo_mode(vicesb_stereo); #else vicesb_stereo_mode(0); #endif if (vicesb_dsp_ver <= 0x200) _dma_start(_sb_dma, vicesb_buf[0], vicesb_dma_size, FALSE); else _dma_start(_sb_dma, vicesb_buf[0], vicesb_dma_size * 2, TRUE); vicesb_play_buffer(vicesb_dma_size); vicesb_in_use = TRUE; vicesb_interrupt_function = interrupt_func; return TRUE; } /* vicesb_close: * SB driver cleanup routine, removes ints, stops dma, frees buffers, etc. */ void vicesb_close(void) { /* halt sound output */ vicesb_voice(0); /* stop dma transfer */ _dma_stop(_sb_dma); if (vicesb_dsp_ver <= 0x0200) vicesb_write_dsp(0xD0); vicesb_reset_dsp(); /* restore interrupts */ _remove_irq(vicesb_int); /* reset PIC channels */ outportb(0x21, vicesb_default_pic1); outportb(0xA1, vicesb_default_pic2); /* free conventional memory buffer */ __dpmi_free_dos_memory(vicesb_sel[0]); if (vicesb_sel[1] != vicesb_sel[0]) __dpmi_free_dos_memory(vicesb_sel[1]); vicesb_in_use = FALSE; } /* vicesb_lock_mem: * Locks all the memory touched by parts of the SB code that are executed * in an interrupt context. */ static void vicesb_lock_mem() { LOCK_VARIABLE(digi_sb); LOCK_VARIABLE(midi_sb_out); LOCK_VARIABLE(_sb_port); LOCK_VARIABLE(_sb_dma); LOCK_VARIABLE(_sb_irq); LOCK_VARIABLE(vicesb_int); LOCK_VARIABLE(vicesb_in_use); LOCK_VARIABLE(vicesb_dsp_ver); LOCK_VARIABLE(vicesb_hw_dsp_ver); LOCK_VARIABLE(vicesb_dma_size); LOCK_VARIABLE(vicesb_freq); LOCK_VARIABLE(vicesb_sel); LOCK_VARIABLE(vicesb_buf); LOCK_VARIABLE(vicesb_bufnum); LOCK_VARIABLE(vicesb_default_pic1); LOCK_VARIABLE(vicesb_default_pic2); LOCK_VARIABLE(vicesb_dma_count); LOCK_VARIABLE(vicesb_semaphore); LOCK_FUNCTION(vicesb_play_buffer); LOCK_FUNCTION(vicesb_interrupt); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.