add SoundBlaster output driver for DOS based on libMikMod

master
Ozkan Sezer 4 years ago committed by Ozkan Sezer
parent d2431b1eef
commit ffa516a36f
  1. 5
      configure.ac
  2. 5
      src/Makefile.am
  3. 203
      src/dos/dosdma.c
  4. 180
      src/dos/dosdma.h
  5. 312
      src/dos/dosirq.c
  6. 112
      src/dos/dosirq.h
  7. 550
      src/dos/dossb.c
  8. 323
      src/dos/dossb.h
  9. 4
      src/sound.c
  10. 163
      src/sound_sb.c

@ -125,6 +125,7 @@ AM_CONDITIONAL([SOUND_NETBSD], [false])
AM_CONDITIONAL([SOUND_OSS], [false])
AM_CONDITIONAL([SOUND_PULSEAUDIO], [false])
AM_CONDITIONAL([SOUND_QNX], [false])
AM_CONDITIONAL([SOUND_SB], [false])
AM_CONDITIONAL([SOUND_SGI], [false])
AM_CONDITIONAL([SOUND_SNDIO], [false])
AM_CONDITIONAL([SOUND_SOLARIS], [false])
@ -243,6 +244,10 @@ cygwin*|mingw*)
AC_DEFINE(SOUND_WIN32, 1, [ ])
AM_CONDITIONAL([SOUND_WIN32], [true])
;;
*djgpp)
AC_DEFINE(SOUND_SB, 1, [ ])
AM_CONDITIONAL([SOUND_SB], [true])
;;
beos*|haiku*)
AC_DEFINE(SOUND_BEOS, 1, [ ])
CFLAGS="${CFLAGS} -Wno-multichar"

@ -65,6 +65,10 @@ xmp_SOURCES += sound_sgi.c
xmp_LDADD += -laudio
endif
if SOUND_SB
xmp_SOURCES += dos/dosdma.c dos/dosirq.c dos/dossb.c sound_sb.c
endif
if SOUND_QNX
xmp_SOURCES += sound_qnx.c
endif
@ -96,4 +100,5 @@ pkgsysconfdir = ${sysconfdir}/${PACKAGE_NAME}
pkgsysconf_DATA = modules.conf xmp.conf
EXTRA_DIST = ${man_MANS} ${pkgsysconf_DATA} \
dos/dosdma.h dos/dosirq.h dos/dossb.h \
win32/unistd.h watcom/unistd.h

@ -0,0 +1,203 @@
/* Implementation of DOS DMA routines, from libMikMod.
* Copyright (C) 1999 by Andrew Zabolotny <bit@eltech.ru>
*
* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
#include "dosdma.h"
#include <go32.h> /* includes sys/version.h (djgpp >= 2.02) */
#include <dos.h>
#include <dpmi.h>
#include <sys/nearptr.h>
#include <malloc.h>
/* BUG WARNING: there is an error in DJGPP libraries <= 2.01:
* src/libc/dpmi/api/d0102.s loads the selector and allocsize
* arguments in the wrong order. DJGPP >= 2.02 have it fixed. */
#if (!defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__+0) < 2)
#warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken!
#endif
__dma_regs dma[8] = {
/* *INDENT-OFF* */
{DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0,
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
{DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1,
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
{DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2,
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
{DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3,
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
{DMA_ADDR_4, 0, DMA_SIZE_4,
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
{DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5,
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
{DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6,
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
{DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7,
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}
/* *INDENT-ON* */
};
static int __initialized = 0;
static int __buffer_count = 0;
static __dpmi_meminfo __locked_data;
int dma_initialize()
{
if (!__djgpp_nearptr_enable())
return 0;
/* Trick: Avoid re-setting DS selector limit on each memory allocation
call */
__djgpp_selector_limit = 0xffffffff;
__locked_data.address = __djgpp_base_address + (unsigned long)&dma;
__locked_data.size = sizeof(dma);
if (__dpmi_lock_linear_region(&__locked_data))
return 0;
return (__initialized = 1);
}
void dma_finalize()
{
if (!__initialized)
return;
__dpmi_unlock_linear_region(&__locked_data);
__djgpp_nearptr_disable();
}
dma_buffer *dma_allocate(unsigned int channel, unsigned int size)
{
int parsize = (size + 15) >> 4; /* size in paragraphs */
int par = 0; /* Real-mode paragraph */
int selector = 0; /* Protected-mode selector */
int mask = channel <= 3 ? 0xfff : 0x1fff; /* Alignment mask in para. */
int allocsize = parsize; /* Allocated size in paragraphs */
int count; /* Try count */
int bound = 0; /* Nearest bound address */
int maxsize; /* Maximal possible block size */
dma_buffer *buffer = NULL;
__dpmi_meminfo buff_info, struct_info;
if (!dma_initialize())
return NULL;
/* Loop until we'll get a properly aligned memory block */
for (count = 8; count; count--) {
int resize = (selector != 0);
/* Try first to resize (possibly previously) allocated block */
if (resize) {
maxsize = (bound + parsize) - par;
if (maxsize > parsize * 2)
maxsize = parsize * 2;
if (maxsize == allocsize)
resize = 0;
else {
allocsize = maxsize;
if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) !=
0) resize = 0;
}
}
if (!resize) {
if (selector)
__dpmi_free_dos_memory(selector), selector = 0;
par = __dpmi_allocate_dos_memory(allocsize, &selector);
}
if ((par == 0) || (par == -1))
goto exit;
/* If memory block contains a properly aligned portion, quit loop */
bound = (par + mask + 1) & ~mask;
if (par + parsize <= bound)
break;
if (bound + parsize <= par + allocsize) {
par = bound;
break;
}
}
if (!count) {
__dpmi_free_dos_memory(selector);
goto exit;
}
buffer = (dma_buffer *) malloc(sizeof(dma_buffer));
buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16);
buffer->physical = bound * 16;
buffer->size = parsize * 16;
buffer->selector = selector;
buffer->channel = channel;
buff_info.address = buffer->physical;
buff_info.size = buffer->size;
/*
Don't pay attention to return code since under plain DOS it often
returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI)
*/
__dpmi_lock_linear_region(&buff_info);
/* Lock the DMA buffer control structure as well */
struct_info.address = __djgpp_base_address + (unsigned long)buffer;
struct_info.size = sizeof(dma_buffer);
if (__dpmi_lock_linear_region(&struct_info)) {
__dpmi_unlock_linear_region(&buff_info);
__dpmi_free_dos_memory(selector);
free(buffer);
buffer = NULL;
goto exit;
}
exit:
if (buffer)
__buffer_count++;
else if (--__buffer_count == 0)
dma_finalize();
return buffer;
}
void dma_free(dma_buffer * buffer)
{
__dpmi_meminfo buff_info;
if (!buffer)
return;
buff_info.address = buffer->physical;
buff_info.size = buffer->size;
__dpmi_unlock_linear_region(&buff_info);
__dpmi_free_dos_memory(buffer->selector);
free(buffer);
if (--__buffer_count == 0)
dma_finalize();
}
void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode)
{
/* Disable interrupts */
int old_ints = disable();
dma_disable(buffer->channel);
dma_set_mode(buffer->channel, mode);
dma_clear_ff(buffer->channel);
dma_set_addr(buffer->channel, buffer->physical);
dma_clear_ff(buffer->channel);
dma_set_count(buffer->channel, count);
dma_enable(buffer->channel);
/* Re-enable interrupts */
if (old_ints)
enable();
}

@ -0,0 +1,180 @@
/* Interface for DOS DMA routines, from libMikMod.
* Copyright (C) 1999 by Andrew Zabolotny <bit@eltech.ru>
*
* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
#ifndef __DOSDMA_H__
#define __DOSDMA_H__
#include <pc.h>
#define DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */
#define DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */
#define DMA1_CMD_REG 0x08 /* command register (w) */
#define DMA1_STAT_REG 0x08 /* status register (r) */
#define DMA1_REQ_REG 0x09 /* request register (w) */
#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */
#define DMA1_MODE_REG 0x0B /* mode register (w) */
#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */
#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */
#define DMA1_RESET_REG 0x0D /* Master Clear (w) */
#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */
#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */
#define DMA2_CMD_REG 0xD0 /* command register (w) */
#define DMA2_STAT_REG 0xD0 /* status register (r) */
#define DMA2_REQ_REG 0xD2 /* request register (w) */
#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */
#define DMA2_MODE_REG 0xD6 /* mode register (w) */
#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */
#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */
#define DMA2_RESET_REG 0xDA /* Master Clear (w) */
#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */
#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */
#define DMA_ADDR_0 0x00 /* DMA address registers */
#define DMA_ADDR_1 0x02
#define DMA_ADDR_2 0x04
#define DMA_ADDR_3 0x06
#define DMA_ADDR_4 0xC0
#define DMA_ADDR_5 0xC4
#define DMA_ADDR_6 0xC8
#define DMA_ADDR_7 0xCC
#define DMA_SIZE_0 0x01 /* DMA transfer size registers */
#define DMA_SIZE_1 0x03
#define DMA_SIZE_2 0x05
#define DMA_SIZE_3 0x07
#define DMA_SIZE_4 0xC2
#define DMA_SIZE_5 0xC6
#define DMA_SIZE_6 0xCA
#define DMA_SIZE_7 0xCE
#define DMA_PAGE_0 0x87 /* DMA page registers */
#define DMA_PAGE_1 0x83
#define DMA_PAGE_2 0x81
#define DMA_PAGE_3 0x82
#define DMA_PAGE_5 0x8B
#define DMA_PAGE_6 0x89
#define DMA_PAGE_7 0x8A
#define DMA_MODE_AUTOINIT 0x10 /* Auto-init mode bit */
#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */
#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */
#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */
/* Indexable specific DMA registers */
typedef struct __dma_regs_s {
unsigned char addr; /* DMA transfer address register */
unsigned char page; /* DMA page register */
unsigned char size; /* DMA transfer size register */
unsigned char mask; /* DMA mask/unmask register */
unsigned char flip; /* DMA flip-flop reset register */
unsigned char mode; /* DMA mode register */
} __dma_regs;
extern __dma_regs dma[8];
/* Enable a specific DMA channel */
static inline void dma_enable(unsigned int channel)
{
outportb(dma[channel].mask, channel & 3);
}
/* Disable a specific DMA channel */
static inline void dma_disable(unsigned int channel)
{
outportb(dma[channel].mask, (channel & 3) | 0x04);
}
/* Clear the 'DMA Flip Flop' flag */
static inline void dma_clear_ff(unsigned int channel)
{
outportb(dma[channel].flip, 0);
}
/* Set mode for a specific DMA channel */
static inline void dma_set_mode(unsigned int channel, char mode)
{
outportb(dma[channel].mode, mode | (channel & 3));
}
/* Set DMA page register */
static inline void dma_set_page(unsigned int channel, char page)
{
if (channel > 3)
page &= 0xfe;
outportb(dma[channel].page, page);
}
/*
Set transfer address & page bits for specific DMA channel.
Assumes dma flipflop is clear.
*/
static inline void dma_set_addr(unsigned int channel, unsigned int address)
{
unsigned char dma_reg = dma[channel].addr;
dma_set_page(channel, address >> 16);
if (channel <= 3) {
outportb(dma_reg, (address) & 0xff);
outportb(dma_reg, (address >> 8) & 0xff);
} else {
outportb(dma_reg, (address >> 1) & 0xff);
outportb(dma_reg, (address >> 9) & 0xff);
}
}
/*
Set transfer size for a specific DMA channel.
Assumes dma flip-flop is clear.
*/
static inline void dma_set_count(unsigned int channel, unsigned int count)
{
unsigned char dma_reg = dma[channel].size;
count--; /* number of DMA transfers is bigger by one */
if (channel > 3)
count >>= 1;
outportb(dma_reg, (count) & 0xff);
outportb(dma_reg, (count >> 8) & 0xff);
}
/*
Query the number of bytes left to transfer.
Assumes DMA flip-flop is clear.
*/
static inline int dma_get_count(unsigned int channel)
{
unsigned char dma_reg = dma[channel].size;
/* using short to get 16-bit wrap around */
unsigned short count;
count = inportb(dma_reg);
count |= inportb(dma_reg) << 8;
count++;
return (channel <= 3) ? count : (count << 1);
}
typedef struct dma_buffer_s {
unsigned char *linear; /* Linear address */
unsigned long physical; /* Physical address */
unsigned long size; /* Buffer size */
unsigned short selector; /* The selector assigned to this memory */
unsigned char channel; /* The DMA channel */
} dma_buffer;
/* Allocate a block of memory suitable for using as a DMA buffer */
extern dma_buffer *dma_allocate(unsigned int channel, unsigned int size);
/* Deallocate a DMA buffer */
extern void dma_free(dma_buffer * buffer);
/* Start DMA transfer to or from given buffer */
extern void dma_start(dma_buffer * buffer, unsigned long count,
unsigned char mode);
#endif /* __DOSDMA_H__ */

@ -0,0 +1,312 @@
/* Implementation of DOS IRQ routines, from libMikMod.
Copyright (C) 1999 by Andrew Zabolotny <bit@eltech.ru>
*
* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
#include "dosirq.h"
#include <dpmi.h>
#include <go32.h>
#include <dos.h>
#include <sys/nearptr.h>
#include <malloc.h>
#include <string.h>
unsigned int __irq_stack_size = 0x4000;
unsigned int __irq_stack_count = 1;
static void __int_stub_template (void)
{
/* *INDENT-OFF* */
asm(" pushal\n"
" pushl %ds\n"
" pushl %es\n"
" pushl %fs\n"
" pushl %gs\n"
" movw $0x1234,%ax\n" /* Get DPMI data selector */
" movw %ax,%ds\n" /* Set DS and ES to data selector */
" movw %ax,%es\n"
" movl $0x12345678,%ebx\n" /* Interrupt stack top */
" movl (%ebx),%ecx\n"
" movl %ecx,%edx\n"
" subl $0x12345678,%ecx\n" /* Subtract irq_stack_count */
" movl %ecx,(%ebx)\n"
" movw %ss,%si\n" /* Save old SS:ESP */
" movl %esp,%edi\n"
" movl %edx,%esp\n" /* Set SS:ESP to interrupt stack */
" movw %ax,%ss\n"
" pushl %esi\n"
" pushl %edi\n"
" pushl %ebx\n"
" pushl %edx\n"
" call 1f\n" /* Call user interrupt handler */
"1: popl %edx\n"
" popl %ebx\n"
" movl %edx,(%ebx)\n"
" popl %edi\n"
" popl %esi\n"
" movl %edi,%esp\n" /* Restore old SS:ESP */
" movw %si,%ss\n"
" popl %gs\n"
" popl %fs\n"
" popl %es\n"
" popl %ds\n"
" popal\n"
" iret\n");
/* *INDENT-ON* */
}
#include <stdio.h>
static int _allocate_iret_wrapper(_go32_dpmi_seginfo * info)
{
unsigned char *irqtpl = (unsigned char *)__int_stub_template;
unsigned char *irqend, *irqwrapper, *tmp;
__dpmi_meminfo handler_info;
unsigned int wrappersize;
/* First, skip until pushal */
while (*irqtpl != 0x60)
irqtpl++;
/* Now find the iret */
irqend = irqtpl;
while (*irqend++ != 0xcf);
wrappersize = 4 + __irq_stack_size * __irq_stack_count + 4 +
((long)irqend - (long)irqtpl);
irqwrapper = (unsigned char *) malloc(wrappersize);
/* Lock the wrapper */
handler_info.address = __djgpp_base_address + (unsigned long)irqwrapper;
handler_info.size = wrappersize;
if (__dpmi_lock_linear_region(&handler_info)) {
free(irqwrapper);
return -1;
}
/* First comes the interrupt wrapper size */
*(unsigned long *)irqwrapper = wrappersize;
/* Next comes the interrupt stack */
tmp = irqwrapper + 4 + __irq_stack_size * __irq_stack_count;
/* The following dword is interrupt stack pointer */
*((void **)tmp) = tmp;
tmp += 4;
/* Now comes the interrupt wrapper itself */
memcpy(tmp, irqtpl, irqend - irqtpl);
*(unsigned short *)(tmp + 9) = _my_ds();
*(unsigned long *)(tmp + 16) = (unsigned long)tmp - 4;
*(unsigned long *)(tmp + 26) = __irq_stack_size;
*(unsigned long *)(tmp + 46) =
info->pm_offset - (unsigned long)(tmp + 50);
info->pm_offset = (unsigned long)tmp;
info->pm_selector = _my_cs();
return 0;
}
static void _free_iret_wrapper(_go32_dpmi_seginfo * info)
{
__dpmi_meminfo handler_info;
info->pm_offset -= 4 + __irq_stack_size * __irq_stack_count + 4;
handler_info.address = __djgpp_base_address + info->pm_offset;
handler_info.size = *(unsigned long *)info->pm_offset;
__dpmi_unlock_linear_region(&handler_info);
free((void *)info->pm_offset);
}
struct irq_handle *irq_hook(int irqno, void (*handler)(), unsigned long size)
{
int interrupt;
struct irq_handle *irq;
__dpmi_version_ret version;
__dpmi_meminfo handler_info, struct_info;
_go32_dpmi_seginfo info;
unsigned long old_sel, old_ofs;
__dpmi_get_version(&version);
if (irqno < 8)
interrupt = version.master_pic + irqno;
else
interrupt = version.slave_pic + (irqno - 8);
if (_go32_dpmi_get_protected_mode_interrupt_vector(interrupt, &info))
return NULL;
old_sel = info.pm_selector;
old_ofs = info.pm_offset;
info.pm_offset = (unsigned long)handler;
if (_allocate_iret_wrapper(&info))
return NULL;
/* Lock the interrupt handler in memory */
handler_info.address = __djgpp_base_address + (unsigned long)handler;
handler_info.size = size;
if (__dpmi_lock_linear_region(&handler_info)) {
_free_iret_wrapper(&info);
return NULL;
}
irq = (struct irq_handle *) malloc(sizeof(struct irq_handle));
irq->c_handler = handler;
irq->handler_size = size;
irq->handler = info.pm_offset;
irq->prev_selector = old_sel;
irq->prev_offset = old_ofs;
irq->int_num = interrupt;
irq->irq_num = irqno;
irq->pic_base = irqno < 8 ? PIC1_BASE : PIC2_BASE;
struct_info.address = __djgpp_base_address + (unsigned long)irq;
struct_info.size = sizeof(struct irq_handle);
if (__dpmi_lock_linear_region(&struct_info)) {
free(irq);
__dpmi_unlock_linear_region(&handler_info);
_free_iret_wrapper(&info);
return NULL;
}
_go32_dpmi_set_protected_mode_interrupt_vector(interrupt, &info);
irq->pic_mask = irq_state(irq);
return irq;
}
void irq_unhook(struct irq_handle *irq)
{
_go32_dpmi_seginfo info;
__dpmi_meminfo mem_info;
if (!irq)
return;
/* Restore the interrupt vector */
irq_disable(irq);
info.pm_offset = irq->prev_offset;
info.pm_selector = irq->prev_selector;
_go32_dpmi_set_protected_mode_interrupt_vector(irq->int_num, &info);
/* Unlock the interrupt handler */
mem_info.address = __djgpp_base_address + (unsigned long)irq->c_handler;
mem_info.size = irq->handler_size;
__dpmi_unlock_linear_region(&mem_info);
/* Unlock the irq_handle structure */
mem_info.address = __djgpp_base_address + (unsigned long)irq;
mem_info.size = sizeof(struct irq_handle);
__dpmi_unlock_linear_region(&mem_info);
info.pm_offset = irq->handler;
_free_iret_wrapper(&info);
/* If IRQ was enabled before we hooked, restore enabled state */
if (irq->pic_mask)
irq_enable(irq);
else
irq_disable(irq);
free(irq);
}
/*---------------------------------------------- IRQ detection mechanism -----*/
static struct irq_handle *__irqs[16];
static int (*__irq_confirm) (int irqno);
static volatile unsigned int __irq_mask;
static volatile unsigned int __irq_count[16];
#define DECLARE_IRQ_HANDLER(irqno) \
static void __irq##irqno##_handler () \
{ \
if (irq_check (__irqs [irqno]) && __irq_confirm (irqno)) \
{ \
__irq_count [irqno]++; \
__irq_mask |= (1 << irqno); \
} \
irq_ack (__irqs [irqno]); \
}
/* *INDENT-OFF* */
DECLARE_IRQ_HANDLER(0)
DECLARE_IRQ_HANDLER(1)
DECLARE_IRQ_HANDLER(2)
DECLARE_IRQ_HANDLER(3)
DECLARE_IRQ_HANDLER(4)
DECLARE_IRQ_HANDLER(5)
DECLARE_IRQ_HANDLER(6)
DECLARE_IRQ_HANDLER(7)
DECLARE_IRQ_HANDLER(8)
DECLARE_IRQ_HANDLER(9)
DECLARE_IRQ_HANDLER(10)
DECLARE_IRQ_HANDLER(11)
DECLARE_IRQ_HANDLER(12)
DECLARE_IRQ_HANDLER(13)
DECLARE_IRQ_HANDLER(14)
DECLARE_IRQ_HANDLER(15)
/* *INDENT-ON* */
static void (*__irq_handlers[16]) () = {
__irq0_handler, __irq1_handler, __irq2_handler, __irq3_handler,
__irq4_handler, __irq5_handler, __irq6_handler, __irq7_handler,
__irq8_handler, __irq9_handler, __irq10_handler, __irq11_handler,
__irq12_handler, __irq13_handler, __irq14_handler, __irq15_handler};
void irq_detect_start(unsigned int irqs, int (*irq_confirm) (int irqno))
{
int i;
__irq_mask = 0;
__irq_confirm = irq_confirm;
memset(&__irqs, 0, sizeof(__irqs));
memset((void *) &__irq_count, 0, sizeof(__irq_count));
/* Hook all specified IRQs */
for (i = 1; i <= 15; i++)
if (irqs & (1 << i)) {
__irqs[i] = irq_hook(i, __irq_handlers[i], 200);
/* Enable the interrupt */
irq_enable(__irqs[i]);
}
/* Enable IRQ2 if we need at least one IRQ above 7 */
if (irqs & 0xff00)
_irq_enable(2);
}
void irq_detect_end()
{
int i;
for (i = 15; i >= 1; i--)
if (__irqs[i])
irq_unhook(__irqs[i]);
}
int irq_detect_get(int irqno, unsigned int *irqmask)
{
int oldirq = disable();
int count = __irq_count[irqno];
*irqmask = __irq_mask;
__irq_mask = 0;
if (oldirq)
enable();
return count;
}
void irq_detect_clear()
{
int oldirq = disable();
memset((void *) &__irq_count, 0, sizeof(__irq_count));
__irq_mask = 0;
if (oldirq)
enable();
}

@ -0,0 +1,112 @@
/* Interface for DOS IRQ routines, from libMikMod.
* Copyright (C) 1999 by Andrew Zabolotny <bit@eltech.ru>
*
* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
#ifndef __DOSIRQ_H__
#define __DOSIRQ_H__
#include <pc.h>
#define PIC1_BASE 0x20 /* PIC1 base */
#define PIC2_BASE 0xA0 /* PIC2 base */
struct irq_handle {
void (*c_handler) (); /* The real interrupt handler */
unsigned long handler_size; /* The size of interrupt handler */
unsigned long handler; /* Interrupt wrapper address */
unsigned long prev_selector; /* Selector of previous handler */
unsigned long prev_offset; /* Offset of previous handler */
unsigned char irq_num; /* IRQ number */
unsigned char int_num; /* Interrupt number */
unsigned char pic_base; /* PIC base (0x20 or 0xA0) */
unsigned char pic_mask; /* Old PIC mask state */
};
/* Return the enabled state for specific IRQ */
static inline unsigned char irq_state(struct irq_handle * irq)
{
return ((~inportb(irq->pic_base + 1)) & (0x01 << (irq->irq_num & 7)));
}
/* Acknowledge the end of interrupt */
static inline void _irq_ack(int irqno)
{
outportb(irqno > 7 ? PIC2_BASE : PIC1_BASE, 0x60 | (irqno & 7));
/* For second controller we also should acknowledge first controller */
if (irqno > 7)
outportb(PIC1_BASE, 0x20); /* 0x20, 0x62? */
}
/* Acknowledge the end of interrupt */
static inline void irq_ack(struct irq_handle * irq)
{
outportb(irq->pic_base, 0x60 | (irq->irq_num & 7));
/* For second controller we also should acknowledge first controller */
if (irq->pic_base != PIC1_BASE)
outportb(PIC1_BASE, 0x20); /* 0x20, 0x62? */
}
/* Mask (disable) the particular IRQ given his ordinal */
static inline void _irq_disable(int irqno)
{
unsigned int port_no = (irqno < 8 ? PIC1_BASE : PIC2_BASE) + 1;
outportb(port_no, inportb(port_no) | (1 << (irqno & 7)));
}
/* Unmask (enable) the particular IRQ given its ordinal */
static inline void _irq_enable(int irqno)
{
unsigned int port_no = (irqno < 8 ? PIC1_BASE : PIC2_BASE) + 1;
outportb(port_no, inportb(port_no) & ~(1 << (irqno & 7)));
}
/* Mask (disable) the particular IRQ given its irq_handle structure */
static inline void irq_disable(struct irq_handle * irq)
{
outportb(irq->pic_base + 1,
inportb(irq->pic_base + 1) | (1 << (irq->irq_num & 7)));
}
/* Unmask (enable) the particular IRQ given its irq_handle structure */
static inline void irq_enable(struct irq_handle * irq)
{
outportb(irq->pic_base + 1,
inportb(irq->pic_base + 1) & ~(1 << (irq->irq_num & 7)));
}
/* Check if a specific IRQ is pending: return 0 is no */
static inline int irq_check(struct irq_handle * irq)
{
outportb(irq->pic_base, 0x0B); /* Read IRR vector */
return (inportb(irq->pic_base) & (1 << (irq->irq_num & 7)));
}
/* Hook a specific IRQ; NOTE: IRQ is disabled upon return, irq_enable() it */
extern struct irq_handle *irq_hook(int irqno, void (*handler)(),
unsigned long size);
/* Unhook a previously hooked IRQ */
extern void irq_unhook(struct irq_handle * irq);
/* Start IRQ detection process (IRQ list is given with irq mask) */
/* irq_confirm should return "1" if the IRQ really comes from the device */
extern void irq_detect_start(unsigned int irqs,
int (*irq_confirm) (int irqno));
/* Finish IRQ detection process */
extern void irq_detect_end();
/* Get the count of specific irqno that happened */
extern int irq_detect_get(int irqno, unsigned int *irqmask);
/* Clear IRQ counters */
extern void irq_detect_clear();
/* The size of interrupt stack */
extern unsigned int __irq_stack_size;
/* The number of nested interrupts that can be handled */
extern unsigned int __irq_stack_count;
#endif /* __DOSIRQ_H__ */

@ -0,0 +1,550 @@
/* Sound Blaster I/O routines, common for SB8, SBPro, and SB16,
* from libMikMod. Written by Andrew Zabolotny <bit@eltech.ru>
* Further bug fixes by O. Sezer <sezero@users.sourceforge.net>
*
* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
#include <stdlib.h>
#include <dpmi.h>
#include <go32.h>
#include <dos.h>
#include <sys/nearptr.h>
#include <sys/farptr.h>
#include <string.h>
#include "dossb.h"
/********************************************* Private variables/routines *****/
__sb_state sb;
/* Wait for SoundBlaster for some time */
#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ == 0)
# define _func_noinline volatile /* match original code */
# define _func_noclone
#else
/* avoid warnings from newer gcc:
* "function definition has qualified void return type" and
* function return types not compatible due to 'volatile' */
# define _func_noinline __attribute__((__noinline__))
# if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
# define _func_noclone
# else
# define _func_noclone __attribute__((__noclone__))
# endif
#endif
_func_noinline
_func_noclone
void __sb_wait()
{
inportb(SB_DSP_RESET);
inportb(SB_DSP_RESET);
inportb(SB_DSP_RESET);
inportb(SB_DSP_RESET);
inportb(SB_DSP_RESET);
inportb(SB_DSP_RESET);
}
static void sb_irq()
{
/* Make sure its not a spurious IRQ */
if (!irq_check(sb.irq_handle))
return;
sb.irqcount++;
/* Acknowledge DMA transfer is complete */
if (sb.mode & SBMODE_16BITS)
__sb_dsp_ack_dma16();
else
__sb_dsp_ack_dma8();
/* SoundBlaster 1.x cannot do autoinit ... */
if (sb.dspver < SBVER_20)
__sb_dspreg_outwlh(SBDSP_DMA_PCM8, (sb.dma_buff->size >> 1) - 1);
/* Send EOI */
irq_ack(sb.irq_handle);
enable();
if (sb.timer_callback)
sb.timer_callback();
}
static void sb_irq_end()
{
}
static boolean __sb_reset()
{
/* Disable the output */
sb_output(FALSE);
/* Clear pending ints if any */
__sb_dsp_ack_dma8();
__sb_dsp_ack_dma16();
/* Reset the DSP */
outportb(SB_DSP_RESET, SBM_DSP_RESET);
__sb_wait();
__sb_wait();
outportb(SB_DSP_RESET, 0);
/* Now wait for AA coming from datain port */
if (__sb_dsp_in() != 0xaa)
return FALSE;
/* Finally, get the DSP version */
if ((sb.dspver = __sb_dsp_version()) == 0xffff)
return FALSE;
/* Check again */
if (sb.dspver != __sb_dsp_version())
return FALSE;
return TRUE;
}
/***************************************************** SB detection stuff *****/
static int __sb_irq_irqdetect(int irqno)
{
__sb_dsp_ack_dma8();
return 1;
}
static void __sb_irq_dmadetect()
{
/* Make sure its not a spurious IRQ */
if (!irq_check(sb.irq_handle))
return;
sb.irqcount++;
/* Acknowledge DMA transfer is complete */
if (sb.mode & SBMODE_16BITS)
__sb_dsp_ack_dma16();
else
__sb_dsp_ack_dma8();
/* Send EOI */
irq_ack(sb.irq_handle);
}
static boolean __sb_detect()
{
/* First find the port number */
if (!sb.port) {
int i;
for (i = 5; i >= 0; i--) {
sb.port = 0x210 + i * 0x10;
if (__sb_reset())
break;
}
if (i < 0) {
sb.port = 0;
return FALSE;
}
}
/* Now detect the IRQ and DMA numbers */
if (!sb.irq) {
unsigned int irqmask, sbirqmask, sbirqcount;
unsigned long timer;
/* IRQ can be one of 2,3,5,7,10 */
irq_detect_start(0x04ac, __sb_irq_irqdetect);
/* Prepare timeout counter */
_farsetsel(_dos_ds);
timer = _farnspeekl(0x46c);
sbirqmask = 0;
sbirqcount = 10; /* Emit 10 SB irqs */
/* Tell SoundBlaster to emit IRQ for 8-bit transfers */
__sb_dsp_out(SBDSP_GEN_IRQ8);
__sb_wait();
for (;;) {
irq_detect_get(0, &irqmask);
if (irqmask) {
sbirqmask |= irqmask;
if (!--sbirqcount)
break;
__sb_dsp_out(SBDSP_GEN_IRQ8);
}
if (_farnspeekl(0x46c) - timer >= 9) /* Wait ~1/2 secs */
break;
}
if (sbirqmask)
for (sb.irq = 15; sb.irq > 0; sb.irq--)
if (irq_detect_get(sb.irq, &irqmask) == 10)
break;
irq_detect_end();
if (!sb.irq)
return FALSE;
}
/* Detect the 8-bit and 16-bit DMAs */
if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) {
static int __dma8[] = { 0, 1, 3 };
static int __dma16[] = { 5, 6, 7 };
int *dma;
sb_output(FALSE);
/* Temporary hook SB IRQ */
sb.irq_handle = irq_hook(sb.irq, __sb_irq_dmadetect, 200);
irq_enable(sb.irq_handle);
if (sb.irq > 7)
_irq_enable(2);
/* Start a short DMA transfer and check if IRQ happened */
for (;;) {
int i, oldcount;
unsigned int timer;
if (!sb.dma8)
dma = &sb.dma8;
else if ((sb.dspver >= SBVER_16) && !sb.dma16)
dma = &sb.dma16;
else
break;
for (i = 0; i < 3; i++) {
boolean success = 1;
*dma = (dma == &sb.dma8) ? __dma8[i] : __dma16[i];
oldcount = sb.irqcount;
dma_disable(*dma);
dma_set_mode(*dma, DMA_MODE_WRITE);
dma_clear_ff(*dma);
dma_set_count(*dma, 2);
dma_enable(*dma);
__sb_dspreg_out(SBDSP_SET_TIMING, 206); /* 20KHz */
if (dma == &sb.dma8) {
sb.mode = 0;
__sb_dspreg_outwlh(SBDSP_DMA_PCM8, 1);
} else {
sb.mode = SBMODE_16BITS;
__sb_dspreg_out(SBDSP_DMA_GENERIC16, 0);
__sb_dsp_out(0);
__sb_dsp_out(1);
}
_farsetsel(_dos_ds);
timer = _farnspeekl(0x46c);
while (oldcount == sb.irqcount)
if (_farnspeekl(0x46c) - timer >= 2) {
success = 0;
break;
}
dma_disable(*dma);
if (success)
break;
*dma = 0;
}
if (!*dma)
break;
}
irq_unhook(sb.irq_handle);
sb.irq_handle = NULL;
if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16))
return FALSE;
}
return TRUE;
}
/*************************************************** High-level interface *****/
/* Detect whenever SoundBlaster is present and fill "sb" structure */
boolean sb_detect()
{
char *env;
/* Try to find the port and DMA from environment */
env = getenv("BLASTER");
while (env && *env) {
/* Skip whitespace */
while ((*env == ' ') || (*env == '\t'))
env++;
if (!*env)
break;
switch (*env++) {
case 'A':
case 'a':
if (!sb.port)
sb.port = strtol(env, &env, 16);
break;
case 'E':
case 'e':
if (!sb.aweport)
sb.aweport = strtol(env, &env, 16);
break;
case 'I':
case 'i':
if (!sb.irq)
sb.irq = strtol(env, &env, 10);
break;
case 'D':
case 'd':
if (!sb.dma8)
sb.dma8 = strtol(env, &env, 10);
break;
case 'H':
case 'h':
if (!sb.dma16)
sb.dma16 = strtol(env, &env, 10);
break;
default:
/* Skip other values (H == MIDI, T == model, any other?) */
while (*env && (*env != ' ') && (*env != '\t'))
env++;
break;
}
}
/* Try to detect missing sound card parameters */
__sb_detect();
if (!sb.port || !sb.irq || !sb.dma8)
return FALSE;
if (!__sb_reset())
return FALSE;
if ((sb.dspver >= SBVER_16) && !sb.dma16)
return FALSE;
if (sb.dspver >= SBVER_PRO)
sb.caps |= SBMODE_STEREO;
if (sb.dspver >= SBVER_16 && sb.dma16)
sb.caps |= SBMODE_16BITS;
if (sb.dspver < SBVER_20)
sb.maxfreq_mono = 22222;
else
sb.maxfreq_mono = 45454;
if (sb.dspver <= SBVER_16)
sb.maxfreq_stereo = 22727;
else
sb.maxfreq_stereo = 45454;
sb.ok = 1;
return TRUE;
}
/* Reset SoundBlaster */
void sb_reset()
{
sb_stop_dma();
__sb_reset();
}
/* Start working with SoundBlaster */
boolean sb_open()
{
__dpmi_meminfo struct_info;
if (!sb.ok)
if (!sb_detect())
return FALSE;
if (sb.open)
return FALSE;
/* Now lock the sb structure in memory */
struct_info.address = __djgpp_base_address + (unsigned long)&sb;
struct_info.size = sizeof(sb);
if (__dpmi_lock_linear_region(&struct_info))
return FALSE;
/* Hook the SB IRQ */
sb.irq_handle = irq_hook(sb.irq, sb_irq, (long)sb_irq_end - (long)sb_irq);
if (!sb.irq_handle) {
__dpmi_unlock_linear_region(&struct_info);
return FALSE;
}
/* Enable the interrupt */
irq_enable(sb.irq_handle);
if (sb.irq > 7)
_irq_enable(2);
sb.open++;
return TRUE;
}
/* Finish working with SoundBlaster */
boolean sb_close()
{
__dpmi_meminfo struct_info;
if (!sb.open)
return FALSE;
sb.open--;
/* Stop/free DMA buffer */
sb_stop_dma();
/* Unhook IRQ */
irq_unhook(sb.irq_handle);
sb.irq_handle = NULL;
/* Unlock the sb structure */
struct_info.address = __djgpp_base_address + (unsigned long)&sb;
struct_info.size = sizeof(sb);
__dpmi_unlock_linear_region(&struct_info);
return TRUE;
}
/* Enable/disable stereo DSP mode */
/* Enable/disable speaker output */
void sb_output(boolean enable)
{
__sb_dsp_out(enable ? SBDSP_SPEAKER_ENA : SBDSP_SPEAKER_DIS);
}
/* Start playing from DMA buffer */
boolean sb_start_dma(unsigned char mode, unsigned int freq)
{
int dmachannel = (mode & SBMODE_16BITS) ? sb.dma16 : sb.dma8;
int dmabuffsize;
unsigned int tc = 0; /* timing constant (<=sbpro only) */
/* Stop DMA transfer if it is enabled */
sb_stop_dma();
/* Sanity check */
if ((mode & SBMODE_MASK & sb.caps) != (mode & SBMODE_MASK))
return FALSE;
/* Check this SB can perform at requested frequency */
if (((mode & SBMODE_STEREO) && (freq > sb.maxfreq_stereo))
|| (!(mode & SBMODE_STEREO) && (freq > sb.maxfreq_mono)))
return FALSE;
/* Check the timing constant here to avoid failing later */
if (sb.dspver < SBVER_16) {
/* SBpro cannot do signed transfer */
if (mode & SBMODE_SIGNED)
return FALSE;
/* Old SBs have a different way on setting DMA timing constant */
tc = freq;
if (mode & SBMODE_STEREO)
tc *= 2;
tc = 1000000 / tc;
if (tc > 255)
return FALSE;
}
sb.mode = mode;
/* Get a DMA buffer enough for a 1/4sec interval... 4K <= dmasize <= 32K */
dmabuffsize = freq;
if (mode & SBMODE_STEREO)
dmabuffsize *= 2;
if (mode & SBMODE_16BITS)
dmabuffsize *= 2;
dmabuffsize >>= 2;
if (dmabuffsize < 4096)
dmabuffsize = 4096;
if (dmabuffsize > 32768)
dmabuffsize = 32768;
dmabuffsize = (dmabuffsize + 255) & 0xffffff00;
sb.dma_buff = dma_allocate(dmachannel, dmabuffsize);
if (!sb.dma_buff)
return FALSE;
/* Fill DMA buffer with silence */
dmabuffsize = sb.dma_buff->size;
if (mode & SBMODE_SIGNED)
memset(sb.dma_buff->linear, 0, dmabuffsize);
else
memset(sb.dma_buff->linear, 0x80, dmabuffsize);
/* Prime DMA for transfer */
dma_start(sb.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
/* Tell SoundBlaster to start transfer */
if (sb.dspver >= SBVER_16) { /* SB16 */
__sb_dspreg_outwhl(SBDSP_SET_RATE, freq);
/* Start DMA->DAC transfer */
__sb_dspreg_out(SBM_GENDAC_AUTOINIT | SBM_GENDAC_FIFO |
((mode & SBMODE_16BITS) ? SBDSP_DMA_GENERIC16 :
SBDSP_DMA_GENERIC8),
((mode & SBMODE_SIGNED) ? SBM_GENDAC_SIGNED : 0) |
((mode & SBMODE_STEREO) ? SBM_GENDAC_STEREO : 0));
/* Write the length of transfer */
dmabuffsize = (dmabuffsize >> 2) - 1;
__sb_dsp_out(dmabuffsize);
__sb_dsp_out(dmabuffsize >> 8);
} else {
__sb_dspreg_out(SBDSP_SET_TIMING, 256 - tc);
dmabuffsize = (dmabuffsize >> 1) - 1;
if (sb.dspver >= SBVER_20) { /* SB 2.0/Pro */
/* Set stereo mode */
__sb_stereo((mode & SBMODE_STEREO) ? TRUE : FALSE);
__sb_dspreg_outwlh(SBDSP_SET_DMA_BLOCK, dmabuffsize);
if (sb.dspver >= SBVER_PRO)
__sb_dsp_out(SBDSP_HS_DMA_DAC8_AUTO);
else
__sb_dsp_out(SBDSP_DMA_PCM8_AUTO);
} else { /* Original SB */
/* Start DMA->DAC transfer */
__sb_dspreg_outwlh(SBDSP_DMA_PCM8, dmabuffsize);
}
}
return TRUE;
}
/* Stop playing from DMA buffer */
void sb_stop_dma()
{
if (!sb.dma_buff)
return;
if (sb.mode & SBMODE_16BITS)
__sb_dsp_out(SBDSP_DMA_HALT16);
else
__sb_dsp_out(SBDSP_DMA_HALT8);
dma_disable(sb.dma_buff->channel);
dma_free(sb.dma_buff);
sb.dma_buff = NULL;
}
/* Query current position/total size of the DMA buffer */
void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos)
{
unsigned int dma_left;
*dma_size = sb.dma_buff->size;
/* It can happen we try to read DMA count when HI/LO bytes will be
inconsistent */
for (;;) {
unsigned int dma_left_test;
dma_clear_ff(sb.dma_buff->channel);
dma_left_test = dma_get_count(sb.dma_buff->channel);
dma_left = dma_get_count(sb.dma_buff->channel);
if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10))
break;
}
*dma_pos = *dma_size - dma_left;
}

@ -0,0 +1,323 @@
/* SoundBlaster and compatible soundcards definitions --
* from libMikMod. Written by Andrew Zabolotny <bit@eltech.ru>
* Further bug fixes by O.Sezer <sezero@users.sourceforge.net>
*
* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
#ifndef __DOSSB_H__
#define __DOSSB_H__
#include "dosdma.h"
#include "dosirq.h"
#define SB_FM_LEFT_STATUS (sb.port + 0x00) /* (r) Left FM status */
#define SB_FM_LEFT_REGSEL (sb.port + 0x00) /* (w) Left FM register select */
#define SB_FM_LEFT_DATA (sb.port + 0x01) /* (w) Left FM data */
#define SB_FM_RIGHT_STATUS (sb.port + 0x02) /* (r) Right FM status */
#define SB_FM_RIGHT_REGSEL (sb.port + 0x02) /* (w) Right FM register select */
#define SB_FM_RIGHT_DATA (sb.port + 0x03) /* (w) Right FM data */
#define SB_MIXER_REGSEL (sb.port + 0x04) /* (w) Mixer register select */
#define SB_MIXER_DATA (sb.port + 0x05) /* (rw)Mixer data */
#define SB_DSP_RESET (sb.port + 0x06) /* (w) DSP reset */
#define SB_FM_STATUS (sb.port + 0x08) /* (r) FM status */
#define SB_FM_REGSEL (sb.port + 0x08) /* (w) FM register select */
#define SB_FM_DATA (sb.port + 0x09) /* (w) FM data */
#define SB_DSP_DATA_IN (sb.port + 0x0a) /* (r) DSP data input */
#define SB_DSP_DATA_OUT (sb.port + 0x0c) /* (w) DSP data output */
#define SB_DSP_DATA_OUT_STATUS (sb.port + 0x0c) /* (r) DSP data output status */
#define SB_DSP_TIMER_IRQ (sb.port + 0x0d) /* (r) clear timer IRQ? */
#define SB_DSP_DATA_IN_STATUS (sb.port + 0x0e) /* (r) DSP data input status */
#define SB_DSP_DMA8_IRQ (sb.port + 0x0e) /* (r) Acknowledge 8-bit DMA transfer */
#define SB_DSP_DMA16_IRQ (sb.port + 0x0f) /* (r) Acknowledge 16-bit DMA transfer */
/* DSP commands */
#define SBDSP_ASP_STATUS 0x03 /* ASP Status (SB16ASP) */
#define SBDSP_STATUS_OLD 0x04 /* DSP Status (Obsolete) (SB2.0-Pro2) */
#define SBDSP_DIRECT_DAC 0x10 /* Direct DAC, 8-bit (SB) */
#define SBDSP_DMA_PCM8 0x14 /* DMA DAC, 8-bit (SB) */
#define SBDSP_DMA_ADPCM2 0x16 /* DMA DAC, 2-bit ADPCM (SB) */
#define SBDSP_DMA_ADPCM2R 0x17 /* DMA DAC, 2-bit ADPCM Reference (SB) */
#define SBDSP_DMA_PCM8_AUTO 0x1C /* Auto-Initialize DMA DAC, 8-bit (SB2.0) */
#define SBDSP_DMA_ADPCM2R_AUTO 0x1F /* Auto-Initialize DMA DAC, 2-bit ADPCM Reference (SB2.0) */
#define SBDSP_DIRECT_ADC 0x20 /* Direct ADC, 8-bit (SB) */
#define SBDSP_DMA_ADC8 0x24 /* DMA ADC, 8-bit (SB) */
#define SBDSP_DIRECT_ADC8_BURST 0x28 /* Direct ADC, 8-bit (Burst) (SB-Pro2) */
#define SBDSP_DMA_ADC8_AUTO 0x2C /* Auto-Initialize DMA ADC, 8-bit (SB2.0) */
#define SBDSP_MIDI_READ_POLL 0x30 /* MIDI Read Poll (SB) */
#define SBDSP_MIDI_READ_IRQ 0x31 /* MIDI Read Interrupt (SB) */
#define SBDSP_MIDI_READ_TIME 0x32 /* MIDI Read Timestamp Poll (SB???) */
#define SBDSP_MIDI_READ_TIME_IRQ 0x33 /* MIDI Read Timestamp Interrupt (SB???) */
#define SBDSP_MIDI_RW_POLL 0x34 /* MIDI Read Poll + Write Poll (UART) (SB2.0) */
#define SBDSP_MIDI_RW_IRQ 0x35 /* MIDI Read Interrupt + Write Poll (UART) (SB2.0???) */
#define SBDSP_MIDI_RW_TIME_IRQ 0x37 /* MIDI Read Timestamp Interrupt + Write Poll (UART) (SB2.0???) */
#define SBDSP_MIDI_WRITE_POLL 0x38 /* MIDI Write Poll (SB) */
#define SBDSP_SET_TIMING 0x40 /* Set Time Constant (SB) */
#define SBDSP_SET_RATE 0x41 /* Set Sample Rate, Hz (SB16) */
#define SBDSP_DMA_CONT8_AUTO 0x45 /* Continue Auto-Initialize DMA, 8-bit (SB16) */
#define SBDSP_DMA_CONT16_AUTO 0x47 /* Continue Auto-Initialize DMA, 16-bit (SB16) */
#define SBDSP_SET_DMA_BLOCK 0x48 /* Set DMA Block Size (SB2.0) */
#define SBDSP_DMA_ADPCM4 0x74 /* DMA DAC, 4-bit ADPCM (SB) */
#define SBDSP_DMA_ADPCM4_REF 0x75 /* DMA DAC, 4-bit ADPCM Reference (SB) */
#define SBDSP_DMA_ADPCM26 0x76 /* DMA DAC, 2.6-bit ADPCM (SB) */
#define SBDSP_DMA_ADPCM26_REF 0x77 /* DMA DAC, 2.6-bit ADPCM Reference (SB) */
#define SBDSP_DMA_ADPCM4R_AUTO 0x7D /* Auto-Initialize DMA DAC, 4-bit ADPCM Reference (SB2.0) */
#define SBDSP_DMA_ADPCM26R_AUTO 0x7F /* Auto-Initialize DMA DAC, 2.6-bit ADPCM Reference (SB2.0) */
#define SBDSP_DISABLE_DAC 0x80 /* Silence DAC (SB) */
#define SBDSP_HS_DMA_DAC8_AUTO 0x90 /* Auto-Initialize DMA DAC, 8-bit (High Speed) (SB2.0-Pro2) */
#define SBDSP_HS_DMA_ADC8_AUTO 0x98 /* Auto-Initialize DMA ADC, 8-bit (High Speed) (SB2.0-Pro2) */
#define SBDSP_STEREO_ADC_DIS 0xA0 /* Disable Stereo Input Mode (SBPro Only) */
#define SBDSP_STEREO_ADC_ENA 0xA8 /* Enable Stereo Input Mode (SBPro Only) */
#define SBDSP_DMA_GENERIC16 0xB0 /* Generic DAC/ADC DMA (16-bit) (SB16) */
#define SBDSP_DMA_GENERIC8 0xC0 /* Generic DAC/ADC DMA (8-bit) (SB16) */
#define SBDSP_DMA_HALT8 0xD0 /* Halt DMA Operation, 8-bit (SB) */
#define SBDSP_SPEAKER_ENA 0xD1 /* Enable Speaker (SB) */
#define SBDSP_SPEAKER_DIS 0xD3 /* Disable Speaker (SB) */
#define SBDSP_DMA_CONT8 0xD4 /* Continue DMA Operation, 8-bit (SB) */
#define SBDSP_DMA_HALT16 0xD5 /* Halt DMA Operation, 16-bit (SB16) */
#define SBDSP_DMA_CONT16 0xD6 /* Continue DMA Operation, 16-bit (SB16) */
#define SBDSP_SPEAKER_STATUS 0xD8 /* Speaker Status (SB) */
#define SBDSP_DMA_EXIT16_AUTO 0xD9 /* Exit Auto-Initialize DMA Operation, 16-bit (SB16) */
#define SBDSP_DMA_EXIT8_AUTO 0xDA /* Exit Auto-Initialize DMA Operation, 8-bit (SB2.0) */
#define SBDSP_IDENTIFY 0xE0 /* DSP Identification (SB2.0) */
#define SBDSP_VERSION 0xE1 /* DSP Version (SB) */
#define SBDSP_COPYRIGHT 0xE3 /* DSP Copyright (SBPro2???) */
#define SBDSP_WRITE_TEST 0xE4 /* Write Test Register (SB2.0) */
#define SBDSP_READ_TEST 0xE8 /* Read Test Register (SB2.0) */
#define SBDSP_SINE_GEN 0xF0 /* Sine Generator (SB) */
#define SBDSP_AUX_STATUS_PRO 0xF1 /* DSP Auxiliary Status (Obsolete) (SB-Pro2) */
#define SBDSP_GEN_IRQ8 0xF2 /* IRQ Request, 8-bit (SB) */
#define SBDSP_GEN_IRQ16 0xF3 /* IRQ Request, 16-bit (SB16) */
#define SBDSP_STATUS 0xFB /* DSP Status (SB16) */
#define SBDSP_AUX_STATUS_16 0xFC /* DSP Auxiliary Status (SB16) */
#define SBDSP_CMD_STATUS 0xFD /* DSP Command Status (SB16) */
/* Mixer commands */
#define SBMIX_RESET 0x00 /* Reset Write SBPro */
#define SBMIX_STATUS 0x01 /* Status Read SBPro */
#define SBMIX_MASTER_LEVEL1 0x02 /* Master Volume Read/Write SBPro Only */
#define SBMIX_DAC_LEVEL 0x04 /* DAC Level Read/Write SBPro */
#define SBMIX_FM_OUTPUT 0x06 /* FM Output Control Read/Write SBPro Only */
#define SBMIX_MIC_LEVEL 0x0A /* Microphone Level Read/Write SBPro */
#define SBMIX_INPUT_SELECT 0x0C /* Input/Filter Select Read/Write SBPro Only */
#define SBMIX_OUTPUT_SELECT 0x0E /* Output/Stereo Select Read/Write SBPro Only */
#define SBMIX_FM_LEVEL 0x22 /* Master Volume Read/Write SBPro */
#define SBMIX_MASTER_LEVEL 0x26 /* FM Level Read/Write SBPro */
#define SBMIX_CD_LEVEL 0x28 /* CD Audio Level Read/Write SBPro */
#define SBMIX_LINEIN_LEVEL 0x2E /* Line In Level Read/Write SBPro */
#define SBMIX_MASTER_LEVEL_L 0x30 /* Master Volume Left Read/Write SB16 */
#define SBMIX_MASTER_LEVEL_R 0x31 /* Master Volume Right Read/Write SB16 */
#define SBMIX_DAC_LEVEL_L 0x32 /* DAC Level Left Read/Write SB16 */
#define SBMIX_DAC_LEVEL_R 0x33 /* DAC Level Right Read/Write SB16 */
#define SBMIX_FM_LEVEL_L 0x34 /* FM Level Left Read/Write SB16 */
#define SBMIX_FM_LEVEL_R 0x35 /* FM Level Right Read/Write SB16 */
#define SBMIX_CD_LEVEL_L 0x36 /* CD Audio Level Left Read/Write SB16 */
#define SBMIX_CD_LEVEL_R 0x37 /* CD Audio Level Right Read/Write SB16 */
#define SBMIX_LINEIN_LEVEL_L 0x38 /* Line In Level Left Read/Write SB16 */
#define SBMIX_LINEIN_LEVEL_R 0x39 /* Line In Level Right Read/Write SB16 */
#define SBMIX_MIC_LEVEL_16 0x3A /* Microphone Level Read/Write SB16 */
#define SBMIX_PCSPK_LEVEL 0x3B /* PC Speaker Level Read/Write SB16 */
#define SBMIX_OUTPUT_CONTROL 0x3C /* Output Control Read/Write SB16 */
#define SBMIX_INPUT_CONTROL_L 0x3D /* Input Control Left Read/Write SB16 */
#define SBMIX_INPUT_CONTROL_R 0x3E /* Input Control Right Read/Write SB16 */
#define SBMIX_INPUT_GAIN_L 0x3F /* Input Gain Control Left Read/Write SB16 */
#define SBMIX_INPUT_GAIN_R 0x40 /* Input Gain Control Right Read/Write SB16 */
#define SBMIX_OUTPUT_GAIN_L 0x41 /* Output Gain Control Left Read/Write SB16 */
#define SBMIX_OUTPUT_GAIN_R 0x42 /* Output Gain Control Right Read/Write SB16 */
#define SBMIX_AGC_CONTROL 0x43 /* Automatic Gain Control (AGC) Read/Write SB16 */
#define SBMIX_TREBLE_L 0x44 /* Treble Left Read/Write SB16 */
#define SBMIX_TREBLE_R 0x45 /* Treble Right Read/Write SB16 */
#define SBMIX_BASS_L 0x46 /* Bass Left Read/Write SB16 */
#define SBMIX_BASS_R 0x47 /* Bass Right Read/Write SB16 */
#define SBMIX_IRQ_SELECT 0x80 /* IRQ Select Read/Write SB16 */
#define SBMIX_DMA_SELECT 0x81 /* DMA Select Read/Write SB16 */
#define SBMIX_IRQ_STATUS 0x82 /* IRQ Status Read SB16 */
/* SB_DSP_DATA_OUT_STATUS and SB_DSP_DATA_IN_STATUS bits */
#define SBM_DSP_READY 0x80
/* SB_DSP_RESET / SBMIX_RESET */
#define SBM_DSP_RESET 0x01
/* SBMIX_OUTPUT_SELECT */
#define SBM_MIX_STEREO 0x02
#define SBM_MIX_FILTER 0x20
/* SBDSP_DMA_GENERIC16/SBDSP_DMA_GENERIC8 */
#define SBM_GENDAC_FIFO 0x02
#define SBM_GENDAC_AUTOINIT 0x04
#define SBM_GENDAC_ADC 0x08
/* Second (mode) byte */
#define SBM_GENDAC_SIGNED 0x10
#define SBM_GENDAC_STEREO 0x20
/* DSP version masks */
#define SBVER_10 0x0100 /* Original SoundBlaster */
#define SBVER_15 0x0105 /* SoundBlaster 1.5 */
#define SBVER_20 0x0200 /* SoundBlaster 2.0 */
#define SBVER_PRO 0x0300 /* SoundBlaster Pro */
#define SBVER_PRO2 0x0301 /* SoundBlaster Pro 2 */
#define SBVER_16 0x0400 /* SoundBlaster 16 */
#define SBVER_AWE32 0x040c /* SoundBlaster AWE32 */
typedef unsigned char boolean;
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
/* Play mode bits */
#define SBMODE_16BITS 0x0001
#define SBMODE_STEREO 0x0002
#define SBMODE_SIGNED 0x0004
/* Mask for capabilities that never change */
#define SBMODE_MASK (SBMODE_16BITS | SBMODE_STEREO)
/* You can fill some members of this struct (i.e. port,irq,dma) before
* calling sb_detect() or sb_open()... this will ignore environment settings.
*/
typedef struct __sb_state_s {
boolean ok; /* Are structure contents valid? */
int port, aweport; /* sb/awe32 base port */
int irq; /* SoundBlaster IRQ */
int dma8, dma16; /* 8-bit and 16-bit DMAs */
int maxfreq_mono; /* Maximum discretization frequency / mono mode */
int maxfreq_stereo; /* Maximum discretization frequency / stereo mode */
unsigned short dspver; /* DSP version number */
struct irq_handle *irq_handle; /* The interrupt handler */
dma_buffer *dma_buff; /* Pre-allocated DMA buffer */
unsigned char caps; /* SoundBlaster capabilities (SBMODE_XXX) */
unsigned char mode; /* Current SB mode (SBMODE_XXX) */
boolean open; /* Whenever the card has been opened */
volatile int irqcount; /* Incremented on each IRQ... for diagnostics */
void (*timer_callback) (); /* Called TWICE per buffer play */
} __sb_state;
extern __sb_state sb;
extern void __sb_wait();
static inline boolean __sb_dsp_ready_in()
{
int count;
for (count = 10000; count >= 0; count--)
if (inportb(SB_DSP_DATA_IN_STATUS) & SBM_DSP_READY)
return TRUE;
return FALSE;
}
static inline boolean __sb_dsp_ready_out()
{
int count;
for (count = 10000; count >= 0; count--)
if ((inportb(SB_DSP_DATA_OUT_STATUS) & SBM_DSP_READY) == 0)
return TRUE;
return FALSE;
}
static inline void __sb_dsp_out(unsigned char reg)
{
__sb_dsp_ready_out();
outportb(SB_DSP_DATA_OUT, reg);
}
static inline unsigned char __sb_dsp_in()
{
__sb_dsp_ready_in();
return inportb(SB_DSP_DATA_IN);
}
static inline void __sb_dspreg_out(unsigned char reg, unsigned char val)
{
__sb_dsp_out(reg);
__sb_dsp_out(val);
}
static inline void __sb_dspreg_outwlh(unsigned char reg, unsigned short val)
{
__sb_dsp_out(reg);
__sb_dsp_out(val);
__sb_dsp_out(val >> 8);
}
static inline void __sb_dspreg_outwhl(unsigned char reg, unsigned short val)
{
__sb_dsp_out(reg);
__sb_dsp_out(val >> 8);
__sb_dsp_out(val);
}
static inline unsigned char __sb_dspreg_in(unsigned char reg)
{
__sb_dsp_out(reg);
return __sb_dsp_in();
}
static inline void __sb_dsp_ack_dma8()
{
inportb(SB_DSP_DMA8_IRQ);
}
static inline void __sb_dsp_ack_dma16()
{
inportb(SB_DSP_DMA16_IRQ);
}
static inline unsigned short __sb_dsp_version()
{
unsigned short ver;
__sb_dsp_out(SBDSP_VERSION);
__sb_dsp_ready_in();
ver = ((unsigned short)__sb_dsp_in()) << 8;
ver |= __sb_dsp_in();
return ver;
}
static inline void __sb_mixer_out(unsigned char reg, unsigned char val)
{
outportb(SB_MIXER_REGSEL, reg);
outportb(SB_MIXER_DATA, val);
}
static inline unsigned char __sb_mixer_in(unsigned char reg)
{
outportb(SB_MIXER_REGSEL, reg);
return inportb(SB_MIXER_DATA);
}
/* Enable stereo transfers: sbpro mode only */
static inline void __sb_stereo(boolean stereo)
{
unsigned char val = __sb_mixer_in(SBMIX_OUTPUT_SELECT);
if (stereo)
val |= SBM_MIX_STEREO;
else
val &= ~SBM_MIX_STEREO;
__sb_mixer_out(SBMIX_OUTPUT_SELECT, val);
}
/* Detect whenever SoundBlaster is present and fill "sb" structure */
extern boolean sb_detect();
/* Reset SoundBlaster */
extern void sb_reset();
/* Start working with SoundBlaster */
extern boolean sb_open();
/* Finish working with SoundBlaster */
extern boolean sb_close();
/* Enable/disable speaker output */
extern void sb_output(boolean enable);
/* Start playing from DMA buffer in either 8/16 bit mono/stereo */
extern boolean sb_start_dma(unsigned char mode, unsigned int freq);
/* Stop playing from DMA buffer */
extern void sb_stop_dma();
/* Query current position/total size of the DMA buffer */
extern void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos);
#endif /* __DOSSB_H__ */

@ -31,6 +31,7 @@ extern struct sound_driver sound_bsd;
extern struct sound_driver sound_beos;
extern struct sound_driver sound_aix;
extern struct sound_driver sound_ahi;
extern struct sound_driver sound_sb;
LIST_HEAD(sound_driver_list);
@ -91,6 +92,9 @@ void init_sound_drivers(void)
#endif
#ifdef SOUND_QNX
register_sound_driver(&sound_qnx);
#endif
#ifdef SOUND_SB
register_sound_driver(&sound_sb);
#endif
register_sound_driver(&sound_wav);
register_sound_driver(&sound_aiff);

@ -0,0 +1,163 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU General Public License. See the COPYING
* file for more information.
*/
/* SoundBlaster/Pro/16/AWE32 output driver for DOS from libMikMod,
* authored by Andrew Zabolotny <bit@eltech.ru>, with further SB16
* fixes by O. Sezer <sezero@users.sourceforge.net>.
* Timer callback functionality replaced by a push mechanism.
*/
#include <string.h>
#include "dos/dossb.h"
#include "sound.h"
/* The last buffer byte filled with sound */
static unsigned int buff_tail = 0;
static int write_sb_output(char *data, unsigned int siz) {
unsigned int dma_size, dma_pos;
unsigned int cnt;
sb_query_dma(&dma_size, &dma_pos);
/* There isn't much sense in filling less than 256 bytes */
dma_pos &= ~255;
/* If nothing to mix, quit */
if (buff_tail == dma_pos)
return 0;
/* If DMA pointer still didn't wrapped around ... */
if (dma_pos > buff_tail) {
if ((cnt = dma_pos - buff_tail) > siz)
cnt = siz;
memcpy(sb.dma_buff->linear + buff_tail, data, cnt);
buff_tail += cnt;
/* If we arrived right to the DMA buffer end, jump to the beginning */
if (buff_tail >= dma_size)
buff_tail = 0;
} else {
/* If wrapped around, fill first to the end of buffer */
if ((cnt = dma_size - buff_tail) > siz)
cnt = siz;
memcpy(sb.dma_buff->linear + buff_tail, data, cnt);
buff_tail += cnt;
siz -= cnt;
if (!siz) return cnt;
/* Now fill from buffer beginning to current DMA pointer */
if (dma_pos > siz) dma_pos = siz;
data += cnt;
cnt += dma_pos;
memcpy(sb.dma_buff->linear, data, dma_pos);
buff_tail = dma_pos;
}
return cnt;
}
static int init(struct options *options)
{
const char *card = NULL;
const char *mode = NULL;
int bits = 0;
if (!sb_open()) {
fprintf(stderr, "Sound Blaster initialization failed.\n");
return -1;
}
if (options->rate < 4000) options->rate = 4000;
if (sb.caps & SBMODE_16BITS) {
card = "16";
mode = "stereo";
bits = 16;
options->format &= ~XMP_FORMAT_8BIT;
} else {
options->format |= XMP_FORMAT_8BIT|XMP_FORMAT_UNSIGNED;
}
if (sb.caps & SBMODE_STEREO) {
if (!card) {
card = "Pro";
mode = "stereo";
bits = 8;
}
if (options->rate > sb.maxfreq_stereo)
options->rate = sb.maxfreq_stereo;
options->format &= ~XMP_FORMAT_MONO;
} else {
mode = "mono";
card = (sb.dspver < SBVER_20)? "1" : "2";
bits = 8;
if (options->rate > sb.maxfreq_mono)
options->rate = sb.maxfreq_mono;
options->format |= XMP_FORMAT_MONO;
}
/* Enable speaker output */
sb_output(TRUE);
/* Set our routine to be called during SB IRQs */
buff_tail = 0;
sb.timer_callback = NULL;/* see above */
/* Start cyclic DMA transfer */
if (!sb_start_dma(((sb.caps & SBMODE_16BITS) ? SBMODE_16BITS | SBMODE_SIGNED : 0) |
(sb.caps & SBMODE_STEREO), options->rate)) {
sb_output(FALSE);
sb_close();
fprintf(stderr, "Sound Blaster: DMA start failed.\n");
return -1;
}
printf("Sound Blaster %s or compatible (%d bit, %s, %u Hz)\n", card, bits, mode, options->rate);
return 0;
}
static void deinit(void)
{
sb.timer_callback = NULL;
sb_output(FALSE);
sb_stop_dma();
sb_close();
}
static void play(void *data, int siz) {
int i;
for (;;) {
i = write_sb_output(data, siz);
if ((siz -= i) <= 0) return;
data = (char *)data + i;
/*delay_ms(1);*/
}
}
static void flush(void)
{
}
static void onpause(void) {
const int silence = (sb.caps & SBMODE_16BITS) ? 0 : 0x80;
memset(sb.dma_buff->linear, silence, sb.dma_buff->size);
}
static void onresume(void)
{
}
struct sound_driver sound_sb = {
"sb",
"Sound Blaster for DOS",
NULL,
init,
deinit,
play,
flush,
onpause,
onresume
};
Loading…
Cancel
Save