📄 tl_sb.c
字号:
/*
** thinlib (c) 2001 Matthew Conte (matt@conte.com)
**
**
** 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: $
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dos.h>
#include <dpmi.h>
#include "tl_types.h"
#include "tl_djgpp.h"
#include "tl_int.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
/* get the irq vector number from an irq channel */
#define SB_IRQVEC(chan) ((chan < 8) ? (0x08 + (chan)) : (0x70 + ((chan) - 8)))
/* DOS low-memory buffer info */
static struct
{
_go32_dpmi_seginfo buffer;
uint32 bufaddr; /* linear address */
uint32 offset;
uint32 page;
} dos;
/* 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;
void *user_data;
} 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;
if (get_env_item(blaster, &sb.dma16, 'H', 10, 8))
sb.dma16 = (uint8) INVALID;
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 const 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 5
static const uint8 irq_channels[NUM_IRQ_CHANNELS] = { 2, 3, 5, 7, 10 };
static volatile bool irq_hit[NUM_IRQ_CHANNELS];
#define MAKE_IRQ_HANDLER(num) \
static int chan##num##_handler(void) { irq_hit[num] = true; return 0; } \
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 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)
{
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);
THIN_DISABLE_INTS();
/* install temp handlers */
thin_int_install(SB_IRQVEC(irq_channels[0]), chan0_handler);
thin_int_install(SB_IRQVEC(irq_channels[1]), chan1_handler);
thin_int_install(SB_IRQVEC(irq_channels[2]), chan2_handler);
thin_int_install(SB_IRQVEC(irq_channels[3]), chan3_handler);
thin_int_install(SB_IRQVEC(irq_channels[4]), chan4_handler);
/* enable IRQs */
for (i = 0; i < NUM_IRQ_CHANNELS; i++)
{
thin_irq_enable(irq_channels[i]);
irq_hit[i] = false;
}
THIN_ENABLE_INTS();
/* wait to see which 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);
break;
}
}
/* 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);
break;
}
}
}
/* reset DSP just in case */
dsp_reset();
THIN_DISABLE_INTS();
/* restore IRQs to previous state, uninstall handlers */
for (i = 0; i < NUM_IRQ_CHANNELS; i++)
{
thin_irq_restore(irq_channels[i]);
thin_int_remove(SB_IRQVEC(irq_channels[i]));
}
THIN_ENABLE_INTS();
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.irq = detect_irq();
if ((uint8) INVALID == sb.irq)
return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -