?? tl_sb.c
字號:
/*** thinlib (c) 2000 Matthew Conte (matt@conte.com)****** This program is free software; you can redistribute it and/or** modify it under the terms of version 2 of the GNU Library General ** Public License as published by the Free Software Foundation.**** 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 ** Library General Public License for more details. To obtain a ** copy of the GNU Library General Public License, write to the Free ** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.**** Any permitted reproduction of these routines, in whole or in part,** must bear this legend.****** tl_sb.c**** DOS Sound Blaster routines**** Note: the information in this file has been gathered from many** Internet documents, and from source code written by Ethan Brodsky.** $Id: tl_sb.c,v 1.11 2001/03/12 06:06:55 matt Exp $*/#include <stdlib.h>#include <string.h>#include <time.h>#include <dos.h>#include <go32.h>#include <dpmi.h>#include "tl_types.h"#include "tl_djgpp.h"#include "tl_sb.h"#include "tl_log.h"/* General defines */#define LOW_BYTE(x) (uint8) ((x) & 0xFF)#define HIGH_BYTE(x) (uint8) ((x) >> 8)#define INVALID 0xFFFFFFFF#define DEFAULT_TIMEOUT 20000#define DETECT_POLL_REPS 1000#define DSP_VERSION_SB_15 0x0200#define DSP_VERSION_SB_20 0x0201#define DSP_VERSION_SB_PRO 0x0300#define DSP_VERSION_SB16 0x0400/* DSP register offsets */#define DSP_RESET 0x06#define DSP_READ 0x0A#define DSP_READ_READY 0x0E#define DSP_WRITE 0x0C#define DSP_WRITE_BUSY 0x0C#define DSP_DMA_ACK_8BIT 0x0E#define DSP_DMA_ACK_16BIT 0x0F#define DSP_RESET_SUCCESS 0xAA/* SB 1.0 commands */#define DSP_DMA_TIME_CONST 0x40#define DSP_DMA_DAC_8BIT 0x14#define DSP_DMA_PAUSE_8BIT 0xD0#define DSP_DMA_CONT_8BIT 0xD4#define DSP_SPEAKER_ON 0xD1#define DSP_SPEAKER_OFF 0xD3#define DSP_GET_VERSION 0xE1/* SB 1.5 - Pro commands */#define DSP_DMA_BLOCK_SIZE 0x48#define DSP_DMA_DAC_AI_8BIT 0x1C /* low-speed autoinit */#define DSP_DMA_DAC_HS_8BIT 0x90 /* high-speed autoinit *//* SB16 commands */#define DSP_DMA_DAC_RATE 0x41#define DSP_DMA_START_16BIT 0xB0#define DSP_DMA_START_8BIT 0xC0#define DSP_DMA_DAC_MODE 0x06#define DSP_DMA_PAUSE_16BIT 0xD5#define DSP_DMA_CONT_16BIT 0xD6#define DSP_DMA_STOP_8BIT 0xDA/* DMA flags */#define DSP_DMA_UNSIGNED 0x00#define DSP_DMA_SIGNED 0x10#define DSP_DMA_MONO 0x00#define DSP_DMA_STEREO 0x20/* DMA address/port/command defines */#define DMA_MASKPORT_16BIT 0xD4#define DMA_MODEPORT_16BIT 0xD6#define DMA_CLRPTRPORT_16BIT 0xD8#define DMA_ADDRBASE_16BIT 0xC0#define DMA_COUNTBASE_16BIT 0XC2#define DMA_MASKPORT_8BIT 0x0A#define DMA_MODEPORT_8BIT 0x0B#define DMA_CLRPTRPORT_8BIT 0x0C#define DMA_ADDRBASE_8BIT 0x00#define DMA_COUNTBASE_8BIT 0x01#define DMA_STOPMASK_BASE 0x04#define DMA_STARTMASK_BASE 0x00#define DMA_AUTOINIT_MODE 0x58#define DMA_ONESHOT_MODE 0x48/* centerline */#define SILENCE_SIGNED 0x00#define SILENCE_UNSIGNED 0x80/* DOS low-memory buffer info */static struct{ _go32_dpmi_seginfo buffer; uint32 bufaddr; /* linear address */ uint32 offset; uint32 page;} dos;/* Interrupt stuff */static struct{ _go32_dpmi_seginfo old_interrupt; _go32_dpmi_seginfo new_interrupt; uint8 irq_vector; uint8 pic_rotateport; uint8 pic_maskport; uint8 irq_stopmask; uint8 irq_startmask;} intr;/* DMA information */static struct{ volatile int count; uint16 addrport; uint16 ackport; bool autoinit;} dma;/* 8 and 16 bit DMA ports */static const uint8 dma8_ports[4] = { 0x87, 0x83, 0x81, 0x82 };static const uint8 dma16_ports[4] = { 0xFF, 0x8B, 0x89, 0x8A };/* Sound Blaster context */static struct{ bool initialized; uint16 baseio; uint16 dsp_version; uint16 sample_rate; uint8 format; uint8 irq, dma, dma16; uint8 *buffer; uint32 buf_size; uint32 buf_chunk; sbmix_t callback;} sb;/*** Basic DSP routines*/static void dsp_write(uint8 value){ int timeout = DEFAULT_TIMEOUT; /* wait until DSP is ready... */ while (timeout-- && (inportb(sb.baseio + DSP_WRITE_BUSY) & 0x80)) ; /* loop */ outportb(sb.baseio + DSP_WRITE, value);}static uint8 dsp_read(void){ int timeout = DEFAULT_TIMEOUT; while (timeout-- && (0 == (inportb(sb.baseio + DSP_READ_READY) & 0x80))) ; /* loop */ return inportb(sb.baseio + DSP_READ);}/* returns zero if DSP found and successfully reset, nonzero otherwise */static int dsp_reset(void){ outportb(sb.baseio + DSP_RESET, 1); /* reset command */ delay(5); /* 5 usec delay */ outportb(sb.baseio + DSP_RESET, 0); /* clear */ delay(5); /* 5 usec delay */ if (DSP_RESET_SUCCESS == dsp_read()) return 0; /* BLEH, we failed */ return -1;}/* return DSP version in 8:8 major:minor format */static uint16 dsp_getversion(void){ uint8 major, minor; dsp_write(DSP_GET_VERSION); major = dsp_read(); minor = dsp_read(); return ((uint16) (major << 8) | minor);}/*** BLASTER environment variable parsing*/static int get_env_item(char *env, void *ptr, char find, int base, int width){ char *item; int value; item = strrchr(env, find); if (NULL == item) return -1; item++; value = strtol(item, NULL, base); switch (width) { case 32: *(uint32 *) ptr = value; break; case 16: *(uint16 *) ptr = value; break; case 8: *(uint8 *) ptr = value; break; default: break; } return 0;}/* parse the BLASTER environment variable */static int parse_blaster_env(void){ char blaster[255 + 1], *penv; penv = getenv("BLASTER"); /* bail out if we can't find it... */ if (NULL == penv) return -1; /* copy it, normalize case */ strncpy(blaster, penv, 255); strupr(blaster); if (get_env_item(blaster, &sb.baseio, 'A', 16, 16)) return -1; if (get_env_item(blaster, &sb.irq, 'I', 10, 8)) return -1; if (get_env_item(blaster, &sb.dma, 'D', 10, 8)) return -1; get_env_item(blaster, &sb.dma16, 'H', 10, 8); return 0;}/*** Brute force autodetection code*//* detect the base IO by attempting to ** reset the DSP at known addresses */static uint16 detect_baseio(void){ int i; static uint16 port_val[] = { 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x280, (uint16) INVALID }; for (i = 0; (uint16) INVALID != port_val[i]; i++) { sb.baseio = port_val[i]; if (0 == dsp_reset()) break; } /* will return INVALID if not found */ return port_val[i];}/* stop all DSP activity */static void dsp_stop(void){ /* pause 8/16 bit DMA mode digitized sound IO */ dsp_reset(); dsp_write(DSP_DMA_PAUSE_8BIT); dsp_write(DSP_DMA_PAUSE_16BIT);}/* return number of set bits in byte x */static int bitcount(uint8 x){ int i, set_count = 0; for (i = 0; i < 8; i++) if (x & (1 << i)) set_count++; return set_count;}/* returns position of lowest bit set in byte x (INVALID if none) */static int bitpos(uint8 x){ int i; for (i = 0; i < 8; i++) if (x & (1 << i)) return i; return INVALID;}static uint8 detect_dma(bool high_dma){ uint8 dma_maskout, dma_mask; int i; /* stop DSP activity */ dsp_stop(); dma_maskout = ~0x10; /* initially mask only DMA4 */ /* poll to find out which dma channels are in use */ for (i = 0; i < DETECT_POLL_REPS; i++) dma_maskout &= ~(inportb(0xD0) & 0xF0) | (inportb(0x08) >> 4); /* TODO: this causes a pretty nasty sound */ /* program card, see whch channel becomes active */ if (false == high_dma) { /* 8 bit */ dsp_write(DSP_DMA_DAC_8BIT); } else { dsp_write(DSP_DMA_START_16BIT); /* 16-bit, D/A, S/C, FIFO off */ dsp_write(DSP_DMA_SIGNED | DSP_DMA_MONO); /* 16-bit mono signed PCM */ } dsp_write(0xF0); /* send some default length */ dsp_write(0xFF); /* poll to find out which DMA channels are in use with sound */ dma_mask = 0; /* dma channels active during audio, minus masked out */ for (i = 0; i < DETECT_POLL_REPS; i++) dma_mask |= (((inportb(0xD0) & 0xF0) | (inportb(0x08) >> 4)) & dma_maskout); /* stop all DSP activity */ dsp_stop(); if (1 == bitcount(dma_mask)) return (uint8) bitpos(dma_mask); else return (uint8) INVALID;}static void dsp_transfer(uint8 dma){ outportb(DMA_MASKPORT_8BIT, DMA_STOPMASK_BASE | dma); /* write DMA mode: single-cycle read transfer */ outportb(DMA_MODEPORT_8BIT, DMA_ONESHOT_MODE | dma); outportb(DMA_CLRPTRPORT_8BIT, 0x00); /* one transfer */ outportb(DMA_COUNTBASE_8BIT + (2 * dma), 0x00); /* low */ outportb(DMA_COUNTBASE_8BIT + (2 * dma), 0x00); /* high */ /* address */ outportb(DMA_ADDRBASE_8BIT + (2 * dma), 0x00); outportb(DMA_ADDRBASE_8BIT + (2 * dma), 0x00); outportb(dma8_ports[dma], 0x00); /* unmask DMA channel */ outportb(DMA_MASKPORT_8BIT, DMA_STARTMASK_BASE | dma); /* 8-bit single cycle DMA mode */ dsp_write(DSP_DMA_DAC_8BIT); dsp_write(0x00); dsp_write(0x00);}/*** IRQ autodetection*/#define NUM_IRQ_CHANNELS 5static _go32_dpmi_seginfo old_handler[NUM_IRQ_CHANNELS];static _go32_dpmi_seginfo new_handler[NUM_IRQ_CHANNELS];static const uint8 irq_channels[NUM_IRQ_CHANNELS] = { 2, 3, 5, 7, 10 };static const uint8 irq_vectors[NUM_IRQ_CHANNELS] = { 0x0A, 0x0B, 0x0D, 0x0F, 0x72 };static volatile bool irq_hit[NUM_IRQ_CHANNELS];#define MAKE_IRQ_HANDLER(num) \static void chan##num##_handler(void) { irq_hit[num] = true; } \THIN_LOCKED_STATIC_FUNC(chan##num##_handler)MAKE_IRQ_HANDLER(0)MAKE_IRQ_HANDLER(1)MAKE_IRQ_HANDLER(2)MAKE_IRQ_HANDLER(3)MAKE_IRQ_HANDLER(4)static void set_handler(int handler, int index, int vector){ new_handler[index].pm_offset = handler; new_handler[index].pm_selector = _go32_my_cs(); _go32_dpmi_get_protected_mode_interrupt_vector(vector, &old_handler[index]); _go32_dpmi_chain_protected_mode_interrupt_vector(vector, &new_handler[index]);}static void release_handler(int index, int vector){ _go32_dpmi_set_protected_mode_interrupt_vector(vector, &old_handler[index]);}static void ack_interrupt(uint8 irq){ /* acknowledge the interrupts! */ inportb(sb.baseio + 0x0E); if (irq > 7) outportb(0xA0, 0x20); outportb(0x20, 0x20);}static uint8 detect_irq(void){ int pic1_oldmask, pic2_oldmask; bool irq_mask[NUM_IRQ_CHANNELS]; uint8 irq = (uint8) INVALID; int i; THIN_LOCK_FUNC(chan0_handler); THIN_LOCK_FUNC(chan1_handler); THIN_LOCK_FUNC(chan2_handler); THIN_LOCK_FUNC(chan3_handler); THIN_LOCK_FUNC(chan4_handler); THIN_LOCK_VAR(irq_hit); /* install temp handlers */ set_handler((int) chan0_handler, 0, irq_vectors[0]); set_handler((int) chan1_handler, 1, irq_vectors[1]); set_handler((int) chan2_handler, 2, irq_vectors[2]); set_handler((int) chan3_handler, 3, irq_vectors[3]); set_handler((int) chan4_handler, 4, irq_vectors[4]); for (i = 0; i < NUM_IRQ_CHANNELS; i++) irq_hit[i] = false; /* save old IRQ mask and unmask IRQs */ pic1_oldmask = inportb(0x21); outportb(0x21, pic1_oldmask & 0x53); pic2_oldmask = inportb(0xA1); outportb(0xA1, pic1_oldmask & 0xFB); /* wait to see what interrupts are triggered without sound */ delay(100); /* mask out any interrupts triggered without sound */ for (i = 0; i < NUM_IRQ_CHANNELS; i++) { irq_mask[i] = irq_hit[i]; irq_hit[i] = false; } /* try to trigger an interrupt using DSP command F2 */ dsp_write(0xF2); delay(100); /* detect triggered interrupts */ for (i = 0; i < NUM_IRQ_CHANNELS; i++) { if (true == irq_hit[i] && false == irq_mask[i]) { irq = irq_channels[i]; ack_interrupt(irq); } } /* if F2 fails to trigger an int, run a short transfer */ if ((uint8) INVALID == irq) { dsp_reset(); dsp_transfer(sb.dma); delay(100); /* detect triggered interrupts */ for (i = 0; i < NUM_IRQ_CHANNELS; i++) { if (true == irq_hit[i] && false == irq_mask[i]) { irq = irq_channels[i]; ack_interrupt(irq); } } } /* reset DSP just in case */ dsp_reset(); /* remask IRQs */ outportb(0x21, pic1_oldmask); outportb(0xA1, pic2_oldmask); /* uninstall handlers */ for (i = 0; i < NUM_IRQ_CHANNELS; i++) release_handler(i, irq_vectors[i]); return irq;}/* try and detect an SB without environment variables */static int sb_detect(void){ sb.baseio = detect_baseio(); if ((uint16) INVALID == sb.baseio) return -1; sb.dma = detect_dma(false); if ((uint8) INVALID == sb.dma) return -1; sb.dma16 = detect_dma(true); if ((uint8) INVALID == sb.dma16) return -1; sb.irq = detect_irq(); if ((uint8) INVALID == sb.irq) return -1; return 0;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -