parent
d2431b1eef
commit
ffa516a36f
10 changed files with 1857 additions and 0 deletions
@ -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__ */ |
||||
@ -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…
Reference in new issue