From 77db1ca18d5446dcda9e524261399b63c2cd1813 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Tue, 20 Sep 2016 02:42:10 +0300 Subject: [PATCH] added an old version of mikmod for dos --- Makefile | 17 +- libs/mikmod/Makefile | 37 + libs/mikmod/drivers/dos/dosdma.c | 214 ---- libs/mikmod/drivers/dos/dosdma.h | 190 ---- libs/mikmod/drivers/dos/dosgus.c | 1907 ------------------------------------ libs/mikmod/drivers/dos/dosgus.h | 514 ---------- libs/mikmod/drivers/dos/dosirq.c | 323 ------- libs/mikmod/drivers/dos/dosirq.h | 122 --- libs/mikmod/drivers/dos/dossb.c | 575 ----------- libs/mikmod/drivers/dos/dossb.h | 341 ------- libs/mikmod/drivers/dos/doswss.c | 577 ----------- libs/mikmod/drivers/dos/doswss.h | 217 ----- libs/mikmod/drivers/dos/libgus.h | 402 -------- libs/mikmod/drvdos/dosdma.c | 214 ++++ libs/mikmod/drvdos/dosdma.h | 190 ++++ libs/mikmod/drvdos/dosgus.c | 1907 ++++++++++++++++++++++++++++++++++++ libs/mikmod/drvdos/dosgus.h | 514 ++++++++++ libs/mikmod/drvdos/dosirq.c | 323 +++++++ libs/mikmod/drvdos/dosirq.h | 122 +++ libs/mikmod/drvdos/dossb.c | 575 +++++++++++ libs/mikmod/drvdos/dossb.h | 341 +++++++ libs/mikmod/drvdos/doswss.c | 577 +++++++++++ libs/mikmod/drvdos/doswss.h | 217 +++++ libs/mikmod/drvdos/libgus.h | 402 ++++++++ libs/oldmik/Makefile | 39 + libs/oldmik/credits.txt | 26 + libs/oldmik/src/drv_gus.c | 1985 ++++++++++++++++++++++++++++++++++++++ libs/oldmik/src/drv_nos.c | 96 ++ libs/oldmik/src/drv_raw.c | 87 ++ libs/oldmik/src/drv_sb.c | 511 ++++++++++ libs/oldmik/src/drv_ss.c | 714 ++++++++++++++ libs/oldmik/src/load_m15.c | 323 +++++++ libs/oldmik/src/load_mod.c | 358 +++++++ libs/oldmik/src/load_mtm.c | 285 ++++++ libs/oldmik/src/load_s3m.c | 570 +++++++++++ libs/oldmik/src/load_stm.c | 379 ++++++++ libs/oldmik/src/load_ult.c | 313 ++++++ libs/oldmik/src/load_uni.c | 190 ++++ libs/oldmik/src/load_xm.c | 603 ++++++++++++ libs/oldmik/src/mdma.c | 515 ++++++++++ libs/oldmik/src/mdma.h | 42 + libs/oldmik/src/mdriver.c | 264 +++++ libs/oldmik/src/mikmod.h | 580 +++++++++++ libs/oldmik/src/mirq.c | 99 ++ libs/oldmik/src/mirq.h | 35 + libs/oldmik/src/mloader.c | 406 ++++++++ libs/oldmik/src/mmio.c | 216 +++++ libs/oldmik/src/mplayer.c | 1386 ++++++++++++++++++++++++++ libs/oldmik/src/mtypes.h | 73 ++ libs/oldmik/src/munitrk.c | 346 +++++++ libs/oldmik/src/resample.asm | 132 +++ libs/oldmik/src/virtch.c | 852 ++++++++++++++++ mklibs.bat | 2 + src/demo.c | 1 - src/dos/music.c | 81 ++ src/music.c | 62 -- src/music.h | 1 + src/sdl/music.c | 63 ++ 58 files changed, 17000 insertions(+), 5453 deletions(-) create mode 100644 libs/mikmod/Makefile delete mode 100644 libs/mikmod/drivers/dos/dosdma.c delete mode 100644 libs/mikmod/drivers/dos/dosdma.h delete mode 100644 libs/mikmod/drivers/dos/dosgus.c delete mode 100644 libs/mikmod/drivers/dos/dosgus.h delete mode 100644 libs/mikmod/drivers/dos/dosirq.c delete mode 100644 libs/mikmod/drivers/dos/dosirq.h delete mode 100644 libs/mikmod/drivers/dos/dossb.c delete mode 100644 libs/mikmod/drivers/dos/dossb.h delete mode 100644 libs/mikmod/drivers/dos/doswss.c delete mode 100644 libs/mikmod/drivers/dos/doswss.h delete mode 100644 libs/mikmod/drivers/dos/libgus.h create mode 100644 libs/mikmod/drvdos/dosdma.c create mode 100644 libs/mikmod/drvdos/dosdma.h create mode 100644 libs/mikmod/drvdos/dosgus.c create mode 100644 libs/mikmod/drvdos/dosgus.h create mode 100644 libs/mikmod/drvdos/dosirq.c create mode 100644 libs/mikmod/drvdos/dosirq.h create mode 100644 libs/mikmod/drvdos/dossb.c create mode 100644 libs/mikmod/drvdos/dossb.h create mode 100644 libs/mikmod/drvdos/doswss.c create mode 100644 libs/mikmod/drvdos/doswss.h create mode 100644 libs/mikmod/drvdos/libgus.h create mode 100644 libs/oldmik/Makefile create mode 100644 libs/oldmik/credits.txt create mode 100644 libs/oldmik/src/drv_gus.c create mode 100644 libs/oldmik/src/drv_nos.c create mode 100644 libs/oldmik/src/drv_raw.c create mode 100644 libs/oldmik/src/drv_sb.c create mode 100644 libs/oldmik/src/drv_ss.c create mode 100644 libs/oldmik/src/load_m15.c create mode 100644 libs/oldmik/src/load_mod.c create mode 100644 libs/oldmik/src/load_mtm.c create mode 100644 libs/oldmik/src/load_s3m.c create mode 100644 libs/oldmik/src/load_stm.c create mode 100644 libs/oldmik/src/load_ult.c create mode 100644 libs/oldmik/src/load_uni.c create mode 100644 libs/oldmik/src/load_xm.c create mode 100644 libs/oldmik/src/mdma.c create mode 100644 libs/oldmik/src/mdma.h create mode 100644 libs/oldmik/src/mdriver.c create mode 100644 libs/oldmik/src/mikmod.h create mode 100644 libs/oldmik/src/mirq.c create mode 100644 libs/oldmik/src/mirq.h create mode 100644 libs/oldmik/src/mloader.c create mode 100644 libs/oldmik/src/mmio.c create mode 100644 libs/oldmik/src/mplayer.c create mode 100644 libs/oldmik/src/mtypes.h create mode 100644 libs/oldmik/src/munitrk.c create mode 100644 libs/oldmik/src/resample.asm create mode 100644 libs/oldmik/src/virtch.c create mode 100644 src/dos/music.c delete mode 100644 src/music.c create mode 100644 src/sdl/music.c diff --git a/Makefile b/Makefile index 810d2ca..6e73142 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,22 @@ baseobj = main.obj -demoobj = demo.obj screen.obj gfxutil.obj 3dgfx.obj polyfill.obj +demoobj = demo.obj screen.obj music.obj gfxutil.obj 3dgfx.obj polyfill.obj scrobj = tunnel.obj fract.obj grise.obj polytest.obj plasma.obj sysobj = gfx.obj vbe.obj dpmi.obj timer.obj keyb.obj mouse.obj logger.obj tinyfps.obj obj = $(baseobj) $(demoobj) $(sysobj) $(scrobj) bin = demo.exe -libs = imago.lib +libs = imago.lib mikmod.lib def = -dM_PI=3.141592653589793 opt = -5 -fp5 -otexan -oh -oi -ei dbg = -d1 !ifdef __UNIX__ -incpath = -Isrc -Isrc/dos -Ilibs/imago/src -libpath = libs/imago +incpath = -Isrc -Isrc/dos -Ilibs/imago/src -Ilibs/oldmik/src +libpath = libpath libs/imago libpath libs/oldmik !else -incpath = -Isrc -Isrc\dos -Ilibs\imago\src -libpath = libs\imago +incpath = -Isrc -Isrc\dos -Ilibs\imago\src -Ilibs\oldmik\src +libpath = libpath libs\imago libpath libs\oldmik !endif AS = nasm @@ -25,12 +25,13 @@ CXX = wpp386 ASFLAGS = -fobj CFLAGS = $(dbg) $(opt) $(def) -zq -bt=dos $(incpath) CXXFLAGS = $(CFLAGS) -LDFLAGS = libpath $(libpath) library { $(libs) } +LDFLAGS = $(libpath) library { $(libs) } LD = wlink $(bin): cflags.occ $(obj) libs/imago/imago.lib %write objects.lnk $(obj) - $(LD) debug all name $@ system dos4g file { @objects } $(LDFLAGS) + %write ldflags.lnk $(LDFLAGS) + $(LD) debug all name $@ system dos4g file { @objects } @ldflags .c: src;src/dos .cc: src;src/dos diff --git a/libs/mikmod/Makefile b/libs/mikmod/Makefile new file mode 100644 index 0000000..dd93135 --- /dev/null +++ b/libs/mikmod/Makefile @@ -0,0 +1,37 @@ +odrivers = drv_oss.obj drv_wss.obj drv_ultra.obj drv_sb.obj drv_sdl.obj drv_nos.obj & +dossb.obj dosirq.obj dosgus.obj dosdma.obj doswss.obj +oloaders = load_s3m.obj load_mod.obj load_it.obj load_xm.obj +ommio = mmalloc.obj mmerror.obj mmio.obj +odepackers = mmcmp.obj pp20.obj s404.obj xpk.obj +oposix = memcmp.obj strcasecmp.obj strstr.obj +oplayercode = mdreg.obj mlreg.obj mloader.obj virtch_common.obj munitrk.obj mplayer.obj & +mlutil.obj sloader.obj npertab.obj virtch.obj mdulaw.obj mwav.obj virtch2.obj mdriver.obj + +obj = $(odrivers) $(oloaders) $(ommio) $(odepackers) $(oposix) $(oplayercode) + +alib = mikmod.lib + +opt = -5 -fp5 -otexan -zu +dbg = -d1 +def = -DHAVE_CONFIG_H -DMIKMOD_BUILD + +CC = wcc386 +CFLAGS = $(dbg) $(opt) $(def) -zq -bt=dos -I. -Iinclude -Idrvdos + +$(alib): cflags.occ $(obj) + %write objects.lbc $(obj) + wlib -b -n $@ @objects + +.c: drivers;drvdos;loaders;mmio;depackers;posix;playercode + +cflags.occ: Makefile + %write $@ $(CFLAGS) + +.c.obj: .autodepend + $(CC) -fo=$@ @cflags.occ $[* + +clean: .symbolic + del *.obj + del *.occ + del *.lbc + del $(alib) diff --git a/libs/mikmod/drivers/dos/dosdma.c b/libs/mikmod/drivers/dos/dosdma.c deleted file mode 100644 index e232c30..0000000 --- a/libs/mikmod/drivers/dos/dosdma.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - Implementation of DMA routines on DOS - Copyright (C) 1999 by Andrew Zabolotny, - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "dosdma.h" - -#include /* includes sys/version.h (djgpp >= 2.02) */ -#include -#include -#include -#include -#include "mikmod.h" /* for MikMod_malloc() & co */ - -/* 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 *) MikMod_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); - MikMod_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); - MikMod_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(); -} - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosdma.h b/libs/mikmod/drivers/dos/dosdma.h deleted file mode 100644 index 882c9ac..0000000 --- a/libs/mikmod/drivers/dos/dosdma.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - Interface for DMA routines on DOS - Copyright (C) 1999 by Andrew Zabolotny, - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __DOSDMA_H__ -#define __DOSDMA_H__ - -#include - -#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__ */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosgus.c b/libs/mikmod/drivers/dos/dosgus.c deleted file mode 100644 index 637b526..0000000 --- a/libs/mikmod/drivers/dos/dosgus.c +++ /dev/null @@ -1,1907 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - Driver for GUS cards under DOS - Written by Andrew Zabolotny - -==============================================================================*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef DRV_ULTRA - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dosgus.h" -#include "mikmod.h" /* for MikMod_malloc() & co */ - -/********************************************* Private variables/routines *****/ - -/* The Gravis Ultrasound state/info */ -__gus_state gus; - -/* Try to avoid holes in DRAM less than this size */ -#define DRAM_HOLE_THRESHOLD 8192 -/* If hole is larger than that, create a free block describing it */ -#define DRAM_SPLIT_THRESHOLD 64 -/* The size of DMA buffer used for RAM->DRAM transfers */ -#define GF1_DMA_BUFFER_SIZE 8192 - -/* Debug macro: useful to change screen locations when some event occurs */ -#ifdef MIKMOD_DEBUG -# define DEBUG_PRINT(x) printf x; -# define DEBUG_OFS(addr, attr) \ - { \ - unsigned short x; \ - _dosmemgetw (0xb8780 + addr*2, 1, &x); \ - if ((x >> 8) != attr) x = '0'; \ - x = ((x + 1) & 0xff) | (attr << 8); \ - _dosmemputw (&x, 1, 0xb8780 + addr*2); \ - } -#else -# define DEBUG_PRINT(x) -# define DEBUG_OFS(addr, attr) -#endif - -static unsigned short __gus_volume_table[512] = { - 0x0000, 0x7000, 0x7ff0, 0x8800, 0x8ff0, 0x9400, 0x9800, 0x9c00, - 0x9ff0, 0xa200, 0xa400, 0xa600, 0xa800, 0xaa00, 0xac00, 0xae00, - 0xaff0, 0xb100, 0xb200, 0xb300, 0xb400, 0xb500, 0xb600, 0xb700, - 0xb800, 0xb900, 0xba00, 0xbb00, 0xbc00, 0xbd00, 0xbe00, 0xbf00, - 0xbff0, 0xc080, 0xc100, 0xc180, 0xc200, 0xc280, 0xc300, 0xc380, - 0xc400, 0xc480, 0xc500, 0xc580, 0xc600, 0xc680, 0xc700, 0xc780, - 0xc800, 0xc880, 0xc900, 0xc980, 0xca00, 0xca80, 0xcb00, 0xcb80, - 0xcc00, 0xcc80, 0xcd00, 0xcd80, 0xce00, 0xce80, 0xcf00, 0xcf80, - 0xcff0, 0xd040, 0xd080, 0xd0c0, 0xd100, 0xd140, 0xd180, 0xd1c0, - 0xd200, 0xd240, 0xd280, 0xd2c0, 0xd300, 0xd340, 0xd380, 0xd3c0, - 0xd400, 0xd440, 0xd480, 0xd4c0, 0xd500, 0xd540, 0xd580, 0xd5c0, - 0xd600, 0xd640, 0xd680, 0xd6c0, 0xd700, 0xd740, 0xd780, 0xd7c0, - 0xd800, 0xd840, 0xd880, 0xd8c0, 0xd900, 0xd940, 0xd980, 0xd9c0, - 0xda00, 0xda40, 0xda80, 0xdac0, 0xdb00, 0xdb40, 0xdb80, 0xdbc0, - 0xdc00, 0xdc40, 0xdc80, 0xdcc0, 0xdd00, 0xdd40, 0xdd80, 0xddc0, - 0xde00, 0xde40, 0xde80, 0xdec0, 0xdf00, 0xdf40, 0xdf80, 0xdfc0, - 0xdff0, 0xe020, 0xe040, 0xe060, 0xe080, 0xe0a0, 0xe0c0, 0xe0e0, - 0xe100, 0xe120, 0xe140, 0xe160, 0xe180, 0xe1a0, 0xe1c0, 0xe1e0, - 0xe200, 0xe220, 0xe240, 0xe260, 0xe280, 0xe2a0, 0xe2c0, 0xe2e0, - 0xe300, 0xe320, 0xe340, 0xe360, 0xe380, 0xe3a0, 0xe3c0, 0xe3e0, - 0xe400, 0xe420, 0xe440, 0xe460, 0xe480, 0xe4a0, 0xe4c0, 0xe4e0, - 0xe500, 0xe520, 0xe540, 0xe560, 0xe580, 0xe5a0, 0xe5c0, 0xe5e0, - 0xe600, 0xe620, 0xe640, 0xe660, 0xe680, 0xe6a0, 0xe6c0, 0xe6e0, - 0xe700, 0xe720, 0xe740, 0xe760, 0xe780, 0xe7a0, 0xe7c0, 0xe7e0, - 0xe800, 0xe820, 0xe840, 0xe860, 0xe880, 0xe8a0, 0xe8c0, 0xe8e0, - 0xe900, 0xe920, 0xe940, 0xe960, 0xe980, 0xe9a0, 0xe9c0, 0xe9e0, - 0xea00, 0xea20, 0xea40, 0xea60, 0xea80, 0xeaa0, 0xeac0, 0xeae0, - 0xeb00, 0xeb20, 0xeb40, 0xeb60, 0xeb80, 0xeba0, 0xebc0, 0xebe0, - 0xec00, 0xec20, 0xec40, 0xec60, 0xec80, 0xeca0, 0xecc0, 0xece0, - 0xed00, 0xed20, 0xed40, 0xed60, 0xed80, 0xeda0, 0xedc0, 0xede0, - 0xee00, 0xee20, 0xee40, 0xee60, 0xee80, 0xeea0, 0xeec0, 0xeee0, - 0xef00, 0xef20, 0xef40, 0xef60, 0xef80, 0xefa0, 0xefc0, 0xefe0, - 0xeff0, 0xf010, 0xf020, 0xf030, 0xf040, 0xf050, 0xf060, 0xf070, - 0xf080, 0xf090, 0xf0a0, 0xf0b0, 0xf0c0, 0xf0d0, 0xf0e0, 0xf0f0, - 0xf100, 0xf110, 0xf120, 0xf130, 0xf140, 0xf150, 0xf160, 0xf170, - 0xf180, 0xf190, 0xf1a0, 0xf1b0, 0xf1c0, 0xf1d0, 0xf1e0, 0xf1f0, - 0xf200, 0xf210, 0xf220, 0xf230, 0xf240, 0xf250, 0xf260, 0xf270, - 0xf280, 0xf290, 0xf2a0, 0xf2b0, 0xf2c0, 0xf2d0, 0xf2e0, 0xf2f0, - 0xf300, 0xf310, 0xf320, 0xf330, 0xf340, 0xf350, 0xf360, 0xf370, - 0xf380, 0xf390, 0xf3a0, 0xf3b0, 0xf3c0, 0xf3d0, 0xf3e0, 0xf3f0, - 0xf400, 0xf410, 0xf420, 0xf430, 0xf440, 0xf450, 0xf460, 0xf470, - 0xf480, 0xf490, 0xf4a0, 0xf4b0, 0xf4c0, 0xf4d0, 0xf4e0, 0xf4f0, - 0xf500, 0xf510, 0xf520, 0xf530, 0xf540, 0xf550, 0xf560, 0xf570, - 0xf580, 0xf590, 0xf5a0, 0xf5b0, 0xf5c0, 0xf5d0, 0xf5e0, 0xf5f0, - 0xf600, 0xf610, 0xf620, 0xf630, 0xf640, 0xf650, 0xf660, 0xf670, - 0xf680, 0xf690, 0xf6a0, 0xf6b0, 0xf6c0, 0xf6d0, 0xf6e0, 0xf6f0, - 0xf700, 0xf710, 0xf720, 0xf730, 0xf740, 0xf750, 0xf760, 0xf770, - 0xf780, 0xf790, 0xf7a0, 0xf7b0, 0xf7c0, 0xf7d0, 0xf7e0, 0xf7f0, - 0xf800, 0xf810, 0xf820, 0xf830, 0xf840, 0xf850, 0xf860, 0xf870, - 0xf880, 0xf890, 0xf8a0, 0xf8b0, 0xf8c0, 0xf8d0, 0xf8e0, 0xf8f0, - 0xf900, 0xf910, 0xf920, 0xf930, 0xf940, 0xf950, 0xf960, 0xf970, - 0xf980, 0xf990, 0xf9a0, 0xf9b0, 0xf9c0, 0xf9d0, 0xf9e0, 0xf9f0, - 0xfa00, 0xfa10, 0xfa20, 0xfa30, 0xfa40, 0xfa50, 0xfa60, 0xfa70, - 0xfa80, 0xfa90, 0xfaa0, 0xfab0, 0xfac0, 0xfad0, 0xfae0, 0xfaf0, - 0xfb00, 0xfb10, 0xfb20, 0xfb30, 0xfb40, 0xfb50, 0xfb60, 0xfb70, - 0xfb80, 0xfb90, 0xfba0, 0xfbb0, 0xfbc0, 0xfbd0, 0xfbe0, 0xfbf0, - 0xfc00, 0xfc10, 0xfc20, 0xfc30, 0xfc40, 0xfc50, 0xfc60, 0xfc70, - 0xfc80, 0xfc90, 0xfca0, 0xfcb0, 0xfcc0, 0xfcd0, 0xfce0, 0xfcf0, - 0xfd00, 0xfd10, 0xfd20, 0xfd30, 0xfd40, 0xfd50, 0xfd60, 0xfd70, - 0xfd80, 0xfd90, 0xfda0, 0xfdb0, 0xfdc0, 0xfdd0, 0xfde0, 0xfdf0, - 0xfe00, 0xfe10, 0xfe20, 0xfe30, 0xfe40, 0xfe50, 0xfe60, 0xfe70, - 0xfe80, 0xfe90, 0xfea0, 0xfeb0, 0xfec0, 0xfed0, 0xfee0, 0xfef0, - 0xff00, 0xff10, 0xff20, 0xff30, 0xff40, 0xff50, 0xff60, 0xff70, - 0xff80, 0xff90, 0xffa0, 0xffb0, 0xffc0, 0xffd0, 0xffe0, 0xfff0 -}; - -/* Wait a bit for GUS before doing something - * Mark function as volatile: don't allow it to be inlined. - * It *should* be slow, no need to make it work faster :-) - */ -#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 __gus_delay() -{ - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); - inportb(GF1_MIX_CTRL); -} - -static void __gus_stop_controller(unsigned char gf1reg) -{ - register unsigned char value = __gus_inregb(gf1reg); - __gus_outregb(gf1reg, (value | GF1VC_STOPPED | GF1VC_STOP) & - ~(GF1VC_IRQ_PENDING | GF1VC_IRQ)); -} - -/* Returns 1 if volume is already at given position */ -static boolean __gus_volume_ramp_to(unsigned short volume, - unsigned char rate, - unsigned char vol_ctrl) -{ - int svol = __gus_inregw(GF1R_VOLUME) & 0xfff0; - int evol = volume; - - /* First of all, disable volume ramp */ - __gus_stop_controller(GF1R_VOLUME_CONTROL); - - /* If voice is stopped, set the volume to zero and return */ - if (__gus_inregb(GF1R_VOICE_CONTROL) & GF1VC_STOPPED) { - __gus_outregw(GF1R_VOLUME, 0); - return 1; - } - - /* Avoid clicks when volume ramp goes too high or too low */ - if (svol < 0x0400) - svol = 0x0400; - if (svol > 0xfc00) - svol = 0xfc00; - if (evol < 0x0400) - evol = 0x0400; - if (evol > 0xfc00) - evol = 0xfc00; - - /* Adjust start/end positions */ - if (svol > evol) { - unsigned short tmp = evol; - evol = svol; - svol = tmp; - vol_ctrl |= GF1VL_BACKWARD; - } - - /* If we already are (near) the target volume, quit */ - if (evol - svol < 0x1000) { - __gus_outregw(GF1R_VOLUME, volume); - return 1; - } - - __gus_outregb(GF1R_VOLUME_START, svol >> 8); - __gus_outregb(GF1R_VOLUME_END, evol >> 8); - __gus_outregb(GF1R_VOLUME_RATE, rate); - __gus_outregb_slow(GF1R_VOLUME_CONTROL, vol_ctrl); - return 0; -} - -static inline void __gus_stop_voice() -{ - __gus_stop_controller(GF1R_VOICE_CONTROL); - __gus_outregb_slow(GF1R_VOICE_CONTROL, GF1VC_STOPPED | GF1VC_STOP); -} - -/* The GUS IRQ handler */ -static void gf1_irq() -{ - unsigned char irq_source; /* The contents of GF1_IRQ_STATUS register */ - boolean timer_cb = 0; /* Call timer callback function */ - - DEBUG_OFS(0, 0xCE) - gus.eow_ignore = 0; - while ((irq_source = inportb(GF1_IRQ_STATUS))) { - DEBUG_OFS(1, 0xCE) - - if (irq_source & GF1M_IRQ_DMA_COMPLETE) { - DEBUG_OFS(4, 0x9F) - /* reset the IRQ pending bit */ - __gus_inregb(GF1R_DMA_CONTROL); - gus.dma_active = 0; - - if (gus.dma_callback) - gus.dma_callback(); - } - - if (irq_source & (GF1M_IRQ_WAVETABLE | GF1M_IRQ_ENVELOPE)) { - unsigned char vcirq; - unsigned int done_mask = 0; - - /* IRQ bits are inverse (i.e. 0 = IRQ pending) */ - while ((vcirq = __gus_inregb(GF1R_IRQ_SOURCE) ^ - (GF1IRQ_WAVE | GF1IRQ_VOLUME)) & - (GF1IRQ_WAVE | GF1IRQ_VOLUME)) { - unsigned long voice = (vcirq & 0x1f); - unsigned char voice_ctl, volume_ctl; - unsigned int voice_mask = (1 << voice); - - /* Don't handle more than one IRQ from same voice */ - if (done_mask & voice_mask) - continue; - - done_mask |= voice_mask; - - /* Read voice/volume selection registers */ - __gus_select_voice(voice); - voice_ctl = __gus_inregb(GF1R_VOICE_CONTROL); - volume_ctl = __gus_inregb(GF1R_VOLUME_CONTROL); - - if ((vcirq & GF1IRQ_WAVE) && (gus.wt_callback) - && !(gus.eow_ignore & voice_mask)) { - DEBUG_OFS(5, 0xAF) - gus.wt_callback(voice, voice_ctl, volume_ctl); - } - - if ((vcirq & GF1IRQ_VOLUME) && (gus.vl_callback)) { - DEBUG_OFS(6, 0xAF) - gus.vl_callback(voice, voice_ctl, volume_ctl); - } - } - } - - /* Reset timers that sent this IRQ */ - if (irq_source & (GF1M_IRQ_TIMER1 | GF1M_IRQ_TIMER2)) { - unsigned char timer_ctl = gus.timer_ctl_reg; - - if (irq_source & GF1M_IRQ_TIMER1) - timer_ctl &= ~GF1M_TIMER1; - - if (irq_source & GF1M_IRQ_TIMER2) - timer_ctl &= ~GF1M_TIMER2; - - __gus_outregb_slow(GF1R_TIMER_CONTROL, timer_ctl); - __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); - } - - if (irq_source & GF1M_IRQ_TIMER1) - if (--gus.t1_countdown == 0) { - gus.t1_countdown = gus.t1_multiple; - gus.t1_ticks++; - - DEBUG_OFS(2, 0xCF) - - if (gus.t1_callback) { - timer_cb = 1; - gus.t1_callback(); - } - } - - if (irq_source & GF1M_IRQ_TIMER2) - if (--gus.t2_countdown == 0) { - gus.t2_countdown = gus.t2_multiple; - gus.t2_ticks++; - - DEBUG_OFS(3, 0xCF) - - if (gus.t2_callback) - gus.t2_callback(); - } -#if 0 - /* The following are not used and implemented yet */ - if (irq_source & (GF1M_IRQ_MIDI_TX | GF1M_IRQ_MIDI_RX)) { - } -#endif - } - - irq_ack(gus.gf1_irq); - - if (timer_cb && gus.timer_callback) - gus.timer_callback(); -} - -static void gf1_irq_end() -{ -} - -static boolean __gus_detect() -{ - /* A relatively relaxed autodetection; - We don't count on DRAM: GUS PnP could not have it - (although its anyway bad for us) - */ - __gus_select_voice(0); - __gus_stop_voice(); - __gus_outregw(GF1R_FREQUENCY, 0x1234); - __gus_outregw(GF1R_VOLUME, 0x5670); - return ((__gus_inregw(GF1R_FREQUENCY) & 0xfffe) == 0x1234) - && ((__gus_inregw(GF1R_VOLUME) & 0xfff0) == 0x5670); -} - -static void __gus_reset(boolean reset_io_dma) -{ - static unsigned char irqctl[16] = { 0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7 }; - static unsigned char dmactl[8] = { 0, 1, 0, 2, 0, 3, 4, 5 }; - unsigned char irqtmp, dmatmp; - - /* Disable interrupts while resetting to avoid spurious IRQs */ - int i, timer, old_ints = disable(); - - /* Stop the timer so that GUS IRQ won't clobber registers */ - timer = (gus.timer_ctl_reg & GF1M_TIMER1); - if (timer) - gus_timer_stop(); - - gus.dma_active = 0; - - __gus_outregb(GF1R_RESET, 0); - for (i = 0; i < 10; i++) - __gus_delay(); - __gus_outregb(GF1R_RESET, GF1M_MASTER_RESET); - for (i = 0; i < 10; i++) - __gus_delay(); - - outportb(GF1_MIDI_CTRL, GF1M_MIDI_RESET); - for (i = 0; i < 10; i++) - __gus_delay(); - outportb(GF1_MIDI_CTRL, 0); - - /* Reset all IRQ sources */ - __gus_outregb(GF1R_DMA_CONTROL, 0); - __gus_outregb(GF1R_TIMER_CONTROL, 0); - __gus_outregb(GF1R_SAMPLE_CONTROL, 0); - - /* Reset all voices */ - gus_reset(gus.voices, gus.dynmask); - - /* Flush any pending IRQs */ - inportb(GF1_IRQ_STATUS); - __gus_inregb(GF1R_DMA_CONTROL); - __gus_inregb(GF1R_SAMPLE_CONTROL); - __gus_inregb(GF1R_IRQ_SOURCE); - - if (reset_io_dma) { - /* Now set up the GUS card to required IRQs and DMAs */ - if (gus.irq[0] == gus.irq[1]) - irqtmp = irqctl[gus.irq[0]] | GF1M_IRQ_EQUAL; - else - irqtmp = irqctl[gus.irq[0]] | (irqctl[gus.irq[1]] << 3); - - if (gus.dma[0] == gus.dma[1]) - dmatmp = dmactl[gus.dma[0]] | GF1M_DMA_EQUAL; - else - dmatmp = dmactl[gus.dma[0]] | (dmactl[gus.dma[1]] << 3); - - /* Reset IRQs if possible */ - gus.mixer = - GF1M_MIXER_NO_LINE_IN | GF1M_MIXER_NO_OUTPUT | GF1M_MIXER_GF1_IRQ; - if (gus.version >= GUS_CARD_VERSION_CLASSIC1) { - outportb(GF1_REG_CTRL, 0x05); - outportb(GF1_MIX_CTRL, gus.mixer); - outportb(GF1_IRQ_CTRL, 0x00); /* Reset IRQs */ - outportb(GF1_REG_CTRL, 0x00); - } - - /* Set up DMA channels: NEVER disable MIXER_GF1_IRQ in the future */ - outportb(GF1_MIX_CTRL, gus.mixer); - outportb(GF1_IRQ_CTRL, dmatmp); - - /* Set up IRQ channels */ - outportb(GF1_MIX_CTRL, gus.mixer | GF1M_CONTROL_SELECT); - outportb(GF1_IRQ_CTRL, irqtmp); - } - - __gus_outregb(GF1R_RESET, GF1M_MASTER_RESET | GF1M_OUTPUT_ENABLE | GF1M_MASTER_IRQ); - __gus_delay(); - - /* Flush IRQs again */ - inportb(GF1_IRQ_STATUS); - __gus_inregb(GF1R_DMA_CONTROL); - __gus_inregb(GF1R_SAMPLE_CONTROL); - __gus_inregb(GF1R_IRQ_SOURCE); - - _irq_ack(gus.irq[0]); - _irq_ack(gus.irq[1]); - - if (timer) - gus_timer_continue(); - - if (old_ints) - enable(); - - /* Enable output */ - __gus_mixer_output(1); -} - -/* Transfer a block of data from GUS DRAM to main RAM through port I/O */ -static void __gus_transfer_io_in(unsigned long address, unsigned char *source, - unsigned long size) -{ - while (size) { - register unsigned int size64k; - - size64k = 0x10000 - (address & 0xffff); - if (size64k > size) - size64k = size; - size -= size64k; - - __gus_outregb(GF1R_DRAM_HIGH, address >> 16); - while (size64k--) { - __gus_outregw(GF1R_DRAM_LOW, address++); - *source++ = inportb(GF1_DRAM); - } - } -} - -/* Transfer a block of data into GUS DRAM through port I/O */ -static void __gus_transfer_io(unsigned long address, unsigned char *source, - unsigned long size, int flags) -{ - while (size) { - register unsigned int size64k; - - size64k = 0x10000 - (address & 0xffff); - if (size64k > size) - size64k = size; - size -= size64k; - - __gus_outregb(GF1R_DRAM_HIGH, address >> 16); - if (flags & GUS_WAVE_INVERT) - if (flags & GUS_WAVE_16BIT) - while (size64k-- && size64k--) { - __gus_outregw(GF1R_DRAM_LOW, address++); - outportb(GF1_DRAM, *source++); - __gus_outregw(GF1R_DRAM_LOW, address++); - outportb(GF1_DRAM, (*source++) ^ 0x80); - } else - while (size64k--) { - __gus_outregw(GF1R_DRAM_LOW, address++); - outportb(GF1_DRAM, (*source++) ^ 0x80); - } else - while (size64k--) { - __gus_outregw(GF1R_DRAM_LOW, address++); - outportb(GF1_DRAM, *source++); - } - } -} - -/* Wait for DMA transfer to finish between 8-9 1/18sec timer ticks */ -static int __gus_wait_dma() -{ - unsigned long timer; - _farsetsel(_dos_ds); - timer = _farnspeekl(0x46c); - while (gus.dma_active) - if (_farnspeekl(0x46c) - timer > 8) { - /* Force DMA abort since something went wrong */ - __gus_reset(0); - return -1; - } - - return 0; -} - -/* Transfer a block of data into GUS DRAM through DMA controller */ -static void __gus_transfer_dma(unsigned long address, unsigned char *source, - unsigned long size, int flags) -{ - unsigned char dma_control; - unsigned long bytes_left; - unsigned long cur_size; - unsigned long dest_addr; - - if ((gus.dma[0] > 3) || (flags & GUS_WAVE_16BIT)) - size = (size + 1) & ~1; - - bytes_left = size; - while (bytes_left) { - __gus_wait_dma(); - - cur_size = gus.dma_buff->size; - if (cur_size > bytes_left) - cur_size = bytes_left; - bytes_left -= cur_size; - dest_addr = address; - - if (gus.dma_buff->linear != source) - memmove(gus.dma_buff->linear, source, cur_size); - source += cur_size; - address += cur_size; - - /* Disable GUS -> DMA tie */ - __gus_outregb(GF1R_DMA_CONTROL, 0); - __gus_delay(); - - /* Set up the DMA */ - dma_start(gus.dma_buff, cur_size, DMA_MODE_WRITE); - gus.dma_active = 1; - - /* Reset the DMA IRQ pending bit if set */ - __gus_inregb(GF1R_DMA_CONTROL); - - /* The 16-bit DMA channels needs a slightly different approach */ - dma_control = GF1M_DMAR_ENABLE | GF1M_DMAR_IRQ_ENABLE | gus.dma_rate; - if (gus.dma[0] > 3) { - dest_addr = __gus_convert_addr16(dest_addr); - dma_control |= GF1M_DMAR_CHAN16; - } - - __gus_outregw(GF1R_DMA_ADDRESS, dest_addr >> 4); - - if (flags & GUS_WAVE_16BIT) - dma_control |= GF1M_DMAR_DATA16; - if (flags & GUS_WAVE_INVERT) - dma_control |= GF1M_DMAR_TOGGLE_SIGN; - - /* Tell GUS to start transfer */ - __gus_outregb(GF1R_DMA_CONTROL, dma_control); - } -} - -static void __gus_detect_version() -{ - unsigned char tmp; - - switch (gus.version = inportb(GF1_REVISION)) { - case 5: - gus.version = GUS_CARD_VERSION_CLASSIC_ICS; - gus.ics = 1; - gus.ics_flipped = 1; - break; - case 6: - case 7: - case 8: - case 9: - gus.version = GUS_CARD_VERSION_CLASSIC_ICS; - gus.ics = 1; - break; - case 10: - gus.version = GUS_CARD_VERSION_MAX; - gus.codec = 1; - break; - case 11: - gus.version = GUS_CARD_VERSION_MAX1; - gus.codec = 1; - break; - case 0x30: - gus.version = GUS_CARD_VERSION_ACE; - break; - case 0x50: - gus.version = GUS_CARD_VERSION_EXTREME; - break; - case 0xff: - /* Pre-3.7 board */ - outportb(GF1_REG_CTRL, 0x20); - tmp = inportb(GF1_REG_CTRL); - if ((tmp != 0xff) && (tmp & 0x06)) - gus.version = GUS_CARD_VERSION_CLASSIC1; - else - gus.version = GUS_CARD_VERSION_CLASSIC; - break; - default: - /* Hmm... unknown revision. Assume a safe Classic model */ -#ifdef MIKMOD_DEBUG - fprintf(stderr, "libgus: Unknown board revision (%02x)\n", - gus.version); -#endif - gus.version = GUS_CARD_VERSION_CLASSIC; - break; - } -} - -static void __gus_detect_transfer() -{ - unsigned char *outbuff, *inbuff; - unsigned int i, j, seed = 0x13243546; - __gus_transfer_func func; - -#define TRANSFER_SIZE 0x4000 - - outbuff = (unsigned char *) MikMod_malloc(TRANSFER_SIZE); - inbuff = (unsigned char *) MikMod_malloc(TRANSFER_SIZE); - - /* Suppose we have an malfunctioning GUS */ - gus.transfer = NULL; - - for (i = (gus.dma_buff ? 0 : 4); i <= 4; i++) { - switch (i) { - case 0: - gus.dma_rate = GF1M_DMAR_RATE0; - func = __gus_transfer_dma; - break; - case 1: - gus.dma_rate = GF1M_DMAR_RATE1; - func = __gus_transfer_dma; - break; - case 2: - gus.dma_rate = GF1M_DMAR_RATE2; - func = __gus_transfer_dma; - break; - case 3: - gus.dma_rate = GF1M_DMAR_RATE3; - func = __gus_transfer_dma; - break; - case 4: - func = __gus_transfer_io; - break; - } - - /* Fill data array each time with pseudo-random values */ - for (j = 0; j < TRANSFER_SIZE; j++) - outbuff[j] = seed, seed = - ((seed + 358979323) ^ (seed >> 16)) * 314159265; - - /* Transfer the random array to GUS */ - /* Poke a security fence around dest block */ - __gus_poke(0x100 - 1, 0xAA); - __gus_poke(0x100 - 2, 0x55); - __gus_poke(0x100 + TRANSFER_SIZE + 0, 0xAA); - __gus_poke(0x100 + TRANSFER_SIZE + 1, 0x55); - - func(0x100, outbuff, TRANSFER_SIZE, 0); - - if (__gus_wait_dma() == 0) { - /* Check if the security fence was not damaged */ - if ((__gus_peek(0x100 - 1) != 0xAA) - || (__gus_peek(0x100 - 2) != 0x55) - || (__gus_peek(0x100 + TRANSFER_SIZE + 0) != 0xAA) - || (__gus_peek(0x100 + TRANSFER_SIZE + 1) != 0x55)) - continue; - - /* Now check if GUS DRAM really data that we expects to be transferred */ - __gus_transfer_io_in(0x100, inbuff, TRANSFER_SIZE); - if (memcmp(outbuff, inbuff, TRANSFER_SIZE) == 0) { - gus.transfer = func; - break; - } - } - } - -#undef TRANSFER_SIZE - - MikMod_free(inbuff); - MikMod_free(outbuff); -} - -static void __gus_detect_memory() -{ - unsigned int size; - for (size = 0; size < 1024; size += 256) { - __gus_poke(size * 1024, 0xaa); - if (__gus_peek(size * 1024) != 0xaa) - break; - __gus_poke(size * 1024, 0x55); - if (__gus_peek(size * 1024) != 0x55) - break; - } - gus.ram = size; -} - -static void __gus_init() -{ - char *gusenv = getenv("ULTRASND"); - - memset((void *)&gus, 0, sizeof(gus)); - gus.cmd_voice = -1; - - if (!gusenv) - return; - - sscanf(gusenv, "%x,%d,%d,%d,%d", &gus.port, &gus.dma[0], &gus.dma[1], - &gus.irq[0], &gus.irq[1]); - - /* A relaxed sanity check */ - if ((gus.port < 0x100) || (gus.port > 0x1000) - || (gus.irq[0] < 2) || (gus.irq[0] > 15) - || (gus.irq[1] < 2) || (gus.irq[1] > 15) - || (gus.dma[0] < 0) || (gus.dma[0] > 7) - || (gus.dma[1] < 0) || (gus.dma[1] > 7)) - return; - - gus.voices = 32; - gus.timer_ctl = GF1M_MASK_TIMER1 | GF1M_MASK_TIMER2; - - /* Detect if the card is really there */ - if (__gus_detect() == 0) - return; - - /* Detect the version of Gravis Ultrasound */ - __gus_detect_version(); - - /* Reset the card */ - __gus_reset(1); - - /* Detect the amount of on-board memory */ - __gus_detect_memory(); - - gus.ok = 1; -} - -static void __gus_kick(gus_wave_t * wave, unsigned int wave_offset) -{ - unsigned char vc; - - vc = GF1VC_IRQ; - if (wave->format & GUS_WAVE_16BIT) - vc |= GF1VC_DATA16; - if (wave->format & GUS_WAVE_BACKWARD) - vc |= GF1VC_BACKWARD; - if (wave->format & GUS_WAVE_LOOP) { - vc |= GF1VC_LOOP_ENABLE; - if (wave->format & GUS_WAVE_BIDIR) - vc |= GF1VC_BI_LOOP; - } - __gus_set_loop_start(vc, (wave->begin.memory << 4) + wave->loop_start); - if (wave->format & GUS_WAVE_LOOP) - __gus_set_loop_end(vc, (wave->begin.memory << 4) + wave->loop_end); - else - __gus_set_loop_end(vc, (wave->begin.memory + wave->size) << 4); - __gus_set_current(vc, (wave->begin.memory << 4) + wave_offset + 100); - __gus_outregb_slow(GF1R_VOICE_CONTROL, vc); -} - -/* Timer 1 callback function (updates voices) */ -static void __gus_timer_update() -{ - gus_wave_t *wave; - unsigned long wave_offset; - unsigned char *src, *top; - unsigned int vmask = (1 << gus.cur_voice); - - if (!gus.cmd_pool_ready) - return; - - __gus_select_voice(gus.cur_voice); - wave_offset = 0; - src = gus.cmd_pool; - top = gus.cmd_pool + gus.cmd_pool_top; - -#define GET_B *src -#define GET_W *((unsigned short *)src) -#define GET_L *((unsigned long *)src) - - while (src < top) { - __gus_delay(); - switch (GET_B++) { - case PCMD_VOICE: - __gus_select_voice(gus.cur_voice = GET_B++); - vmask = (1 << gus.cur_voice); - break; - case PCMD_FREQ: - /* __gus_outregw(GF1R_FREQUENCY, GET_W++);*/ - __gus_outregw(GF1R_FREQUENCY, *(unsigned short *)src); - src += 2; - break; - case PCMD_PAN: - __gus_outregb(GF1R_BALANCE, GET_B++); - break; - case PCMD_VOLUME: - __gus_volume_ramp_to(gus.cur_vol[gus.cur_voice] = - /* GET_W++, GUS_VOLCHANGE_RAMP, GF1VL_IRQ);*/ - *(unsigned short *)src, GUS_VOLCHANGE_RAMP, GF1VL_IRQ); - src += 2; - break; - case PCMD_VOLUME_PREPARE: - /* gus.cur_vol[gus.cur_voice] = GET_W++;*/ - gus.cur_vol[gus.cur_voice] = *(unsigned short *)src; - src += 2; - break; - case PCMD_OFFSET: - /* wave_offset = GET_L++;*/ - wave_offset = *(unsigned long *)src; - src += 4; - break; - case PCMD_START: - /* wave = (gus_wave_t *) GET_L++;*/ - wave = (gus_wave_t *) *(unsigned long *)src; - src += 4; - gus.cur_wave[gus.cur_voice] = wave; - gus.kick_offs[gus.cur_voice] = wave_offset; - if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) { - __gus_kick(wave, wave_offset); - __gus_volume_ramp_to(gus.cur_vol[gus.cur_voice], - GUS_VOLCHANGE_RAMP, GF1VL_IRQ); - } else - gus.voice_kick[gus.cur_voice] = 1; - wave_offset = 0; - gus.eow_ignore |= vmask; - break; - case PCMD_STOP: - /* If volume is close to nothing, abort immediately instead of - ramping */ - gus.cur_vol[gus.cur_voice] = 0; - gus.cur_wave[gus.cur_voice] = NULL; - if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) - __gus_stop_voice(); - break; - case PCMD_STOP_LOOP: - __gus_outregb_slow(GF1R_VOICE_CONTROL, - (__gus_inregb(GF1R_VOICE_CONTROL) | GF1VC_IRQ) - & ~GF1VC_LOOP_ENABLE); - __gus_outregb_slow(GF1R_VOLUME_CONTROL, - __gus_inregb(GF1R_VOLUME_CONTROL) & - ~GF1VL_ROLLOVER); - break; - default: - /* Alarm! Break out immediately */ - src = top; - break; - } - } - -#undef GET_B -#undef GET_W -#undef GET_L - - gus.cmd_pool_ready = 0; - gus.cmd_pool_top = 0; -} - -static void __gus_wavetable_update(unsigned int voice, unsigned int voice_ctl, - unsigned int volume_ctl) -{ - gus_wave_t *wave = gus.cur_wave[voice]; - - if (!wave || !(wave->format & GUS_WAVE_LOOP)) { - __gus_stop_voice(); - gus.cur_wave[voice] = NULL; - gus.cur_vol[voice] = 0; - if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) - __gus_stop_voice(); - } -} - -static void __gus_volume_update(unsigned int voice, unsigned int voice_ctl, - unsigned int volume_ctl) -{ - __gus_volume_ramp_to(gus.cur_vol[voice], GUS_VOLCHANGE_RAMP, GF1VL_IRQ); - if (!gus.cur_wave[voice]) - __gus_stop_voice(); - else if (gus.voice_kick[voice]) - __gus_kick(gus.cur_wave[voice], gus.kick_offs[voice]); - gus.voice_kick[voice] = 0; -} - -/***************************************************** GUS memory manager *****/ - -/* Mark all GUS memory as available */ -static void __gus_mem_clear() -{ - __gus_mcb *cur = gus.mcb; - - while (cur) { - __gus_mcb *next = cur->next; - if (cur != gus.mcb) - MikMod_free(cur); - cur = next; - } - - if (!gus.mcb) - gus.mcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); - - gus.mcb->next = gus.mcb->prev = NULL; - gus.mcb->addr = 0; - gus.mcb->size = gus.ram * 1024; - gus.mcb->free = 1; -} - -/* Return amount of free memory */ -static unsigned int __gus_mem_get_free() -{ - __gus_mcb *cur = gus.mcb; - unsigned int size = 0; - - if (!gus.open) - return gus.ram * 1024; - - while (cur) { - if (cur->free) - size += cur->size; - cur = cur->next; - } - - return size; -} - -/* Return largest size for a 8-bit sample */ -static unsigned int __gus_mem_get_free_8() -{ - __gus_mcb *cur = gus.mcb; - unsigned int size = 0; - - if (!gus.open) - return 0; - - while (cur) { - if (cur->free && (cur->size > size)) - size = cur->size; - cur = cur->next; - } - - return size; -} - -/* Return largest size for a 16-bit sample */ -static unsigned int __gus_mem_get_free_16() -{ - __gus_mcb *cur = gus.mcb; - unsigned int size = 0; - - if (!gus.open) - return 0; - - while (cur) { - if (cur->free) { - unsigned int size16 = cur->size; - unsigned int tmp; - /* 16-bit samples cannot cross 256K boundaries */ - tmp = 0x40000 - (cur->addr & 0x3ffff); - if (size16 > tmp) - size16 = tmp; - /* 16-bit samples should be aligned on a 32-byte boundary */ - size16 -= (32 - cur->addr) & 0x1f; - - if (size16 > size) - size = size16; - - /* Now try vice versa: skip a portion of aligned memory */ - size16 = - (cur->addr + cur->size) - ((cur->addr + 0x3ffff) & ~0x3ffff); - if ((size16 < 0x7fffffff) && (size16 > size)) - size = size16; - } - cur = cur->next; - } - - return size; -} - -/* Allocate a segment of GUS DRAM for a sample with given bits per sample. - * The algorithm tries to find the smallest free block that fits requested - * size; but if found free block is larger by some (large) delta than - * requested block size, the largest possible block is preffered. - */ -static unsigned int __gus_mem_alloc(unsigned int size, int bits16) -{ - __gus_mcb *cur = gus.mcb; - __gus_mcb *best_max = NULL, *best_min = NULL; - unsigned int best_max_delta = 0, best_min_delta = 0xffffffff; - unsigned int best_max_prefix = 0, best_min_prefix = 0; - unsigned int memaddr, memsize; - - if (!gus.open || !size || (bits16 && size > 0x40000)) - return -1; - - /* Round block size up to nearest acceptable DMA bound */ - if (bits16) - size = (size + 0x1f) & ~0x1f; - else - size = (size + 0x0f) & ~0x0f; - - while (cur) { - if (cur->free) { - unsigned char fits = 0; - - memsize = cur->size; - memaddr = cur->addr; - - if (bits16) { - /* 16-bit samples cannot cross 256K boundaries */ - unsigned int tmp = 256 * 1024 - (memaddr & 0x3ffff); - if (memsize > tmp) - memsize = tmp; - /* 16-bit samples should be aligned on a 32-byte boundary */ - memsize -= (32 - memaddr) & 0x1f; - memaddr = (memaddr + 0x1f) & ~0x1f; - } - - /* If block fits, analyze it */ - if (size <= memsize) - fits = 1; - /* Look if we still can complete the request by creating a free - block */ - else if (size <= cur->size) { - /* Align start address to next 256k boundary */ - unsigned int endaddr = cur->addr + cur->size; - memaddr = (cur->addr + 0x3ffff) & ~0x3ffff; - /* Can we split current block by inserting a free block at the - beginning? */ - if ((memaddr < endaddr) && (memaddr + size <= endaddr)) - fits = 1; - } - - if (fits) { - unsigned int size_delta = cur->size - size; - unsigned int size_prefix = memaddr - cur->addr; - if (size_delta < best_min_delta) - best_min = cur, best_min_delta = - size_delta, best_min_prefix = size_prefix; - if (size_delta > best_max_delta) - best_max = cur, best_max_delta = - size_delta, best_max_prefix = size_prefix; - } - } - - cur = cur->next; - } - - if (!best_min) - return -1; - - /* If minimal block that fits is too large, use largest block that fits */ - /* But if using the maximal block is going to create a small hole, forget - it */ - if ((best_max_prefix == 0) - || (best_max_prefix >= DRAM_HOLE_THRESHOLD) - || (best_min_prefix != 0)) - if ( - ((best_min_delta < DRAM_HOLE_THRESHOLD) && - (best_max_delta >= DRAM_HOLE_THRESHOLD)) || - ((best_min_prefix > 0) && (best_min_prefix < DRAM_HOLE_THRESHOLD) - && ((best_max_prefix == 0) || - (best_max_prefix > best_min_prefix))) || - ((best_min_prefix != 0) && (best_max_prefix == 0))) { - best_min = best_max; - best_min_delta = best_max_delta; - best_min_prefix = best_max_prefix; - } - - /* Compute the DRAM address to return */ - memaddr = best_min->addr + best_min_prefix; - if (bits16) - memaddr = (memaddr + 0x1f) & ~0x1f; - else - memaddr = (memaddr + 0x0f) & ~0x0f; - - /* If we have a considerable hole at the beginning of sample, - create a free node describing the hole */ - if (memaddr - best_min->addr >= DRAM_SPLIT_THRESHOLD) { - __gus_mcb *newmcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); - newmcb->prev = best_min->prev; - newmcb->next = best_min; - newmcb->addr = best_min->addr; - newmcb->size = memaddr - best_min->addr; - newmcb->free = 1; - best_min->addr = memaddr; - best_min->size -= newmcb->size; - best_min->prev = newmcb; - if (newmcb->prev) - newmcb->prev->next = newmcb; - } - - /* Compute the size of hole at the end of block */ - memsize = (best_min->addr + best_min->size) - (memaddr + size); - - /* Split the block if the block is larger than requested amount */ - if (memsize > DRAM_SPLIT_THRESHOLD) { - /* The next node cannot be free since free blocks are always glued - together */ - __gus_mcb *newmcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); - best_min->size -= memsize; - newmcb->prev = best_min; - newmcb->next = best_min->next; - newmcb->addr = best_min->addr + best_min->size; - newmcb->size = memsize; - newmcb->free = 1; - if (best_min->next) - best_min->next->prev = newmcb; - best_min->next = newmcb; - } - best_min->free = 0; - - return memaddr; -} - -static void __gus_mem_free(unsigned int addr) -{ - __gus_mcb *cur = gus.mcb; - while (cur) { - if (!cur->free && (cur->addr <= addr) && - (cur->addr + cur->size > addr)) { - cur->free = 1; - - /* If next block is free as well, link them together */ - if (cur->next && cur->next->free) { - __gus_mcb *next = cur->next; - cur->size += next->size; - cur->next = next->next; - if (next->next) - next->next->prev = cur; - MikMod_free(next); - } - - /* If previous block is free, link current block with it */ - if (cur->prev && cur->prev->free) { - cur->prev->size += cur->size; - cur->prev->next = cur->next; - if (cur->next) - cur->next->prev = cur->prev; - MikMod_free(cur); - } - return; - } - cur = cur->next; - } -} - -static void __gus_mem_pack() -{ -} - -#ifdef MIKMOD_DEBUG - -/* Debug dump of GUS DRAM heap */ -void __gus_mem_dump() -{ - __gus_mcb *cur = gus.mcb; - fprintf(stderr, "/-- Offset --+-- Prev --+-- Size --+-- Free --\\\n"); - while (cur) { - fprintf(stderr, "| %08X | %08X | %6d | %s |\n", - cur->addr, cur->prev ? cur->prev->addr : -1, cur->size, - cur->free ? "yes" : " no"); - cur = cur->next; - } - fprintf(stderr, "\\------------+----------+----------+----------/\n"); -} - -#endif - -/************************************************** Middle-level routines *****/ - -static int __gus_instrument_free(gus_instrument_t * instrument) -{ - gus_instrument_t **cur_instr; - gus_layer_t *cur_layer; - gus_wave_t *cur_wave, *wave_head; - - /* Remove the instrument from the list of registered instruments */ - cur_instr = (gus_instrument_t **) & gus.instr; - while (*cur_instr) { - if (*cur_instr == instrument) { - *cur_instr = instrument->next; - goto instr_loaded; - } - cur_instr = &(*cur_instr)->next; - } - return -1; - -instr_loaded: - wave_head = NULL; - for (cur_layer = instrument->info.layer; cur_layer; - cur_layer = cur_layer->next) - /* Free all waves */ - for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) { - if (!wave_head) - wave_head = cur_wave; - if (cur_wave->begin.memory != (unsigned int)-1) - __gus_mem_free(cur_wave->begin.memory); - } - if (wave_head) - MikMod_free(wave_head); - - MikMod_free(instrument->info.layer); - if (instrument->name) - MikMod_free(instrument->name); - MikMod_free(instrument); - return 0; -} - -static gus_instrument_t *__gus_instrument_get(int program) -{ - gus_instrument_t *cur_instr = (gus_instrument_t *) gus.instr; - while (cur_instr) { - if (cur_instr->number.instrument == program) - return cur_instr; - cur_instr = cur_instr->next; - } - return NULL; -} - -static gus_instrument_t *__gus_instrument_copy(gus_instrument_t * instrument) -{ - gus_instrument_t **cur_instr, *instr; - gus_layer_t *cur_layer, *dest_layer; - gus_wave_t *cur_wave, *dest_wave; - unsigned int waves, layers; - - if (!instrument || !instrument->info.layer || !gus.open) - return NULL; - - if (__gus_instrument_get(instrument->number.instrument)) - return NULL; - - instr = (gus_instrument_t *) MikMod_malloc(sizeof(gus_instrument_t)); - *instr = *instrument; - - if (instrument->name) - instr->name = MikMod_strdup(instrument->name); - - /* Make a copy of all layers at once */ - for (layers = 0, cur_layer = instrument->info.layer; cur_layer; layers++) - cur_layer = cur_layer->next; - - if (!(dest_layer = instr->info.layer = (gus_layer_t *) MikMod_malloc(sizeof(gus_layer_t) * layers))) { - if (instr->name) - MikMod_free(instr->name); - MikMod_free(instr); - return NULL; - } - for (waves = 0, cur_layer = instrument->info.layer; cur_layer; - cur_layer = cur_layer->next) { - *dest_layer = *cur_layer; - dest_layer->wave = NULL; - /* Count the total number of waves */ - for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) - waves++; - if (cur_layer->next) - dest_layer->next = dest_layer + 1; - else - dest_layer->next = NULL; - dest_layer++; - } - - /* Allocate memory for waves */ - if (!(dest_wave = (gus_wave_t *) MikMod_malloc(sizeof(gus_wave_t) * waves))) { - MikMod_free(instr->info.layer); - if (instr->name) - MikMod_free(instr->name); - MikMod_free(instr); - return NULL; - } - for (cur_layer = instrument->info.layer, dest_layer = instr->info.layer; - cur_layer; cur_layer = cur_layer->next, dest_layer = dest_layer->next) - /* Copy all waves */ - for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) { - if (!dest_layer->wave) - dest_layer->wave = dest_wave; - - *dest_wave = *cur_wave; - /* Mark DRAM address as unallocated */ - dest_wave->begin.memory = -1; - - if (cur_wave->next) - dest_wave->next = (dest_wave + 1); - else - dest_wave->next = NULL; - dest_wave++; - } - - /* Insert the instrument into list of registered instruments */ - cur_instr = (gus_instrument_t **) & gus.instr; - while (*cur_instr) - cur_instr = &(*cur_instr)->next; - *cur_instr = instr; - - return instr; -} - -static void __gus_instruments_clear() -{ - gus_instrument_t *next_instr, *cur_instr = (gus_instrument_t *) gus.instr; - while (cur_instr) { - next_instr = cur_instr->next; - __gus_instrument_free(cur_instr); - cur_instr = next_instr; - } -} - -/******************************************************* libGUS interface *****/ - -/* return value: number of GUS cards installed in system */ -int gus_cards() -{ - if (!gus.ok) - __gus_init(); - return gus.ok ? 1 : 0; -} - -int gus_info(gus_info_t * info, int reread) -{ - if (!gus.ok) - __gus_init(); - if (!gus.ok) - return -1; - - strcpy((char *)info->id, "gus0"); - info->flags = (gus.ram ? GUS_STRU_INFO_F_PCM : 0); - info->version = gus.version; - info->port = gus.port; - info->irq = gus.irq[0]; - info->dma1 = gus.dma[0]; - info->dma2 = gus.dma[1]; - - info->mixing_freq = gus.freq; - - info->memory_size = gus.ram * 1024; - info->memory_free = __gus_mem_get_free(); - info->memory_block_8 = __gus_mem_get_free_8(); - info->memory_block_16 = __gus_mem_get_free_16(); - return 0; -} - -int gus_open(int card, size_t queue_buffer_size, int non_block) -{ - __dpmi_meminfo struct_info, pool_info; - - if (!gus.ok) - __gus_init(); - - if (!gus.ok || gus.open || card != 0) - return -1; - - /* Now lock the gus structure in memory */ - struct_info.address = __djgpp_base_address + (unsigned long)&gus; - struct_info.size = sizeof(gus); - if (__dpmi_lock_linear_region(&struct_info)) - return -1; - - /* And hook the GF1 interrupt */ - __irq_stack_count = 4; - gus.gf1_irq = - irq_hook(gus.irq[0], gf1_irq, (long)gf1_irq_end - (long)gf1_irq); - __irq_stack_count = 1; - if (!gus.gf1_irq) { - __dpmi_unlock_linear_region(&struct_info); - return -1; - } - - /* Enable the interrupt */ - irq_enable(gus.gf1_irq); - if (gus.irq[0] > 7) - _irq_enable(2); - - /* Allocate a DMA buffer: if we fail, we just use I/O so don't fail */ - if ((gus.transfer == NULL) || (gus.transfer == __gus_transfer_dma)) - gus.dma_buff = dma_allocate(gus.dma[0], GF1_DMA_BUFFER_SIZE); - else - gus.dma_buff = NULL; - - /* Detect the best available RAM -> DRAM transfer function */ - if (!gus.transfer) { - __gus_detect_transfer(); - if (gus.transfer != __gus_transfer_dma || !gus.transfer) - dma_free(gus.dma_buff), gus.dma_buff = NULL; - - /* If no transfer function worked, fail */ - if (!gus.transfer) { - if (gus.dma_buff) { - dma_free(gus.dma_buff); - gus.dma_buff = NULL; - } - __dpmi_unlock_linear_region(&struct_info); - irq_unhook(gus.gf1_irq); - gus.gf1_irq = NULL; - return -1; - } - } - - /* Allocate and lock command pool buffer */ - if (queue_buffer_size < 64) - queue_buffer_size = 64; - if (queue_buffer_size > 16384) - queue_buffer_size = 16384; - gus.cmd_pool = (unsigned char *) MikMod_malloc(queue_buffer_size); - pool_info.address = __djgpp_base_address + (unsigned long)&gus.cmd_pool; - pool_info.size = sizeof(queue_buffer_size); - if (__dpmi_lock_linear_region(&pool_info)) { - if (gus.dma_buff) { - dma_free(gus.dma_buff); - gus.dma_buff = NULL; - } - __dpmi_unlock_linear_region(&struct_info); - irq_unhook(gus.gf1_irq); - gus.gf1_irq = NULL; - return -1; - } - - gus.open++; - - __gus_mem_clear(); - gus.t1_callback = __gus_timer_update; - gus.wt_callback = __gus_wavetable_update; - gus.vl_callback = __gus_volume_update; - gus_do_tempo(60); /* Default is 60 Hz */ - - return 0; -} - -int gus_close(int card) -{ - __dpmi_meminfo struct_info; - - if (!gus.open || card != 0) - return -1; - - /* First reset the card: disable any operation it can currently perform */ - __gus_reset(0); - - gus.open--; - - /* Stop the timer */ - gus_timer_stop(); - - /* Free DMA buffer if used */ - if (gus.dma_buff) { - dma_free(gus.dma_buff); - gus.dma_buff = NULL; - } - - /* And unhook the GF1 interrupt */ - irq_unhook(gus.gf1_irq); - gus.gf1_irq = NULL; - - /* Unlock the gus structure */ - struct_info.address = __djgpp_base_address + (unsigned long)&gus; - struct_info.size = sizeof(gus); - __dpmi_unlock_linear_region(&struct_info); - - __gus_mem_clear(); - __gus_instruments_clear(); - - return 0; -} - -int gus_select(int card) -{ - if (!gus.open || (card != 0)) - return -1; - - return 0; -} - -/* return value: same as gus_reset function - note: this command doesn't change number of active voices and doesn't do - hardware reset */ -int gus_reset_engine_only() -{ - gus.timer_base = 100; - return 0; -} - -int gus_reset(int voices, unsigned int channel_voices) -{ - static unsigned short freq_table[32 - 14 + 1] = { - 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, - 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 - }; - int voice; - int timer; - - /* No support for dynamically allocated voices for now */ - gus.dynmask = channel_voices; - - if (voices < 14) - voices = 14; - if (voices > 32) - voices = 32; - - /* Stop the timer so that GUS IRQ won't clobber registers */ - timer = (gus.timer_ctl_reg & GF1M_TIMER1); - if (timer) - gus_timer_stop(); - - /* Stop all voices */ - for (voice = 0; voice < 32; voice++) { - __gus_select_voice(voice); - __gus_stop_voice(); - gus.cur_wave[voice] = NULL; - gus.cur_vol[voice] = 0; - - __gus_delay(); - - /* Reset voice parameters to reasonable values */ - __gus_set_current(0, 0); - __gus_set_loop_start(0, 0); - __gus_set_loop_end(0, 0); - __gus_outregw(GF1R_VOLUME, 0); - __gus_outregb(GF1R_VOLUME_RATE, 0); - __gus_outregb(GF1R_VOLUME_START, 0); - __gus_outregb(GF1R_VOLUME_END, 0); - __gus_outregb(GF1R_BALANCE, 0x7); - } - - voice = (__gus_inregb(GF1R_VOICES) & 0x1f) + 1; - - if (voice != voices) { - int reset = __gus_inregb(GF1R_RESET); - __gus_outregb(GF1R_RESET, reset & ~GF1M_OUTPUT_ENABLE); - __gus_delay(); - __gus_outregb(GF1R_VOICES, 0xc0 | (voices - 1)); - __gus_delay(); - __gus_outregb(GF1R_RESET, reset); - } - - /* Compute the discretization frequence */ - gus.voices = voices; - if (gus.interwave) - gus.freq = 44100; - else - gus.freq = freq_table[voices - 14]; - - gus_reset_engine_only(); - - if (timer) - gus_timer_continue(); - - return gus.voices; -} - -int gus_do_flush() -{ - DEBUG_PRINT(("gus_do_flush: top = %d\n", gus.cmd_pool_top)) - gus.cmd_pool_ready = 1; - return 0; -} - -/* set new tempo */ -void gus_do_tempo(unsigned int tempo) -{ - DEBUG_PRINT(("gus_do_tempo (%d)\n", tempo)) - gus_timer_tempo(tempo); - gus_timer_start(); -} - -/* set voice frequency in Hz */ -void gus_do_voice_frequency(unsigned char voice, unsigned int freq) -{ - DEBUG_PRINT(("gus_do_voice_frequency (%d, %d)\n", voice, freq)) - __pool_select_voice(voice); - __pool_command_w(PCMD_FREQ, - (((freq << 9) + (gus.freq >> 1)) / gus.freq) << 1); -} - -/* set voice pan (0-16384) (full left - full right) */ -void gus_do_voice_pan(unsigned char voice, unsigned short pan) -{ - DEBUG_PRINT(("gus_do_voice_pan (%d, %d)\n", voice, pan)) - pan >>= 10; - if (pan > 15) - pan = 15; - __pool_select_voice(voice); - __pool_command_b(PCMD_PAN, pan); -} - -/* set voice volume level 0-16384 (linear) */ -void gus_do_voice_volume(unsigned char voice, unsigned short vol) -{ - DEBUG_PRINT(("gus_do_voice_volume (%d, %d)\n", voice, vol)) - if (vol > 0x3fff) - vol = 0x3fff; - __pool_select_voice(voice); - __pool_command_w(PCMD_VOLUME, __gus_volume_table[vol >> 5]); -} - -/* start voice - * voice : voice # - * program : program # or ~0 = current - * freq : frequency in Hz - * volume : volume level (0-16384) or ~0 = current - * pan : pan level (0-16384) or ~0 = current - */ -void gus_do_voice_start(unsigned char voice, unsigned int program, - unsigned int freq, unsigned short volume, - unsigned short pan) -{ - gus_do_voice_start_position(voice, program, freq, volume, pan, 0); -} - -/* start voice - * voice : voice # - * program : program # or ~0 = current - * freq : frequency in Hz - * volume : volume level (0-16384) or ~0 = current - * pan : pan level (0-16384) or ~0 = current - * position : offset to wave in bytes * 16 (lowest 4 bits - fraction) - */ -void gus_do_voice_start_position(unsigned char voice, unsigned int program, - unsigned int freq, unsigned short volume, - unsigned short pan, unsigned int position) -{ - gus_instrument_t *instrument; - gus_wave_t *wave; - - DEBUG_PRINT( - ("gus_do_voice_start_position (%d, %d, pos: %d)\n", voice, - program, position)) - - instrument = __gus_instrument_get(program); - - if (!instrument - || !instrument->info.layer - || !instrument->info.layer->wave - || instrument->flags == GUS_INSTR_F_NOT_FOUND - || instrument->flags == GUS_INSTR_F_NOT_LOADED) return; - - gus_do_voice_frequency(voice, freq); - gus_do_voice_pan(voice, pan); - - /* We have to set volume different way, to avoid unneeded work in handler */ - if (volume > 0x3fff) - volume = 0x3fff; - __pool_command_w(PCMD_VOLUME_PREPARE, __gus_volume_table[volume >> 5]); - - switch (instrument->mode) { - case GUS_INSTR_SIMPLE: - wave = instrument->info.layer->wave; - if (position) - __pool_command_l(PCMD_OFFSET, position); - __pool_command_l(PCMD_START, (unsigned long)wave); - break; - } -} - -/* stop voice - * mode = 0 : stop voice now - * mode = 1 : disable wave loop and finish it - */ -void gus_do_voice_stop(unsigned char voice, unsigned char mode) -{ - __pool_select_voice(voice); - if (mode) - __pool_command(PCMD_STOP_LOOP); - else - __pool_command(PCMD_STOP); -} - -/* wait x ticks - this command is block separator - all commands between blocks are interpreted in the begining of one tick */ -void gus_do_wait(unsigned int ticks) -{ - DEBUG_PRINT(("gus_do_wait (%d)\n", ticks)) - - ticks += gus.t1_ticks; - while ((int)(ticks - gus.t1_ticks) > 0); -} - -int gus_get_voice_status(int voice) -{ - __gus_select_voice(voice); - return __gus_inregb(GF1R_VOICE_CONTROL) & GF1VC_STOPPED ? 0 : 1; -} - -/* return value: file handle (descriptor) for /dev/gus */ -int gus_get_handle() -{ - /* Return stdout handle so that select() will "work" with it */ - return 0; -} - -/* return value: zero if instrument was successfully allocated */ -int gus_memory_alloc(gus_instrument_t * instrument) -{ - gus_instrument_t *instr = __gus_instrument_copy(instrument); - gus_layer_t *cur_layer; - gus_wave_t *cur_wave; - - DEBUG_PRINT(("gus_memory_alloc (%d)\n", instrument->number.instrument)) - - if (!instr) - return -1; - - for (cur_layer = instr->info.layer; cur_layer; - cur_layer = cur_layer->next) for (cur_wave = cur_layer->wave; - cur_wave; - cur_wave = cur_wave->next) { - if (cur_layer->mode == GUS_INSTR_SIMPLE) { - cur_wave->begin.memory = __gus_mem_alloc(cur_wave->size, - cur_wave->format & - GUS_WAVE_16BIT); - if (cur_wave->begin.memory == (unsigned int)-1) { - __gus_instrument_free(instr); - return -1; - } - gus.transfer(cur_wave->begin.memory, cur_wave->begin.ptr, - cur_wave->size, cur_wave->format); - } else if (cur_layer->mode == GUS_INSTR_PATCH) - /* not supported yet */ ; - } - - return 0; -} - -/* return value: zero if instrument was successfully removed */ -int gus_memory_free(gus_instrument_t * instrument) -{ - gus_instrument_t *cur_instr = gus.instr; - - DEBUG_PRINT(("gus_memory_free (%d)\n", instrument->number.instrument)) - - for (; cur_instr; cur_instr = cur_instr->next) - if (cur_instr->number.instrument == instrument->number.instrument) - return __gus_instrument_free(cur_instr); - - return -1; -} - -/* return value: unused gus memory in bytes */ -int gus_memory_free_size() -{ - return __gus_mem_get_free(); -} - -/* return value: zero if success */ -int gus_memory_pack() -{ - __gus_mem_pack(); - return 0; -} - -/* return value: gus memory size in bytes */ -int gus_memory_size() -{ - return gus.ram * 1024; -} - -/* return value: current largest free block for 8-bit or 16-bit wave */ -int gus_memory_free_block(int w_16bit) -{ - return w_16bit ? __gus_mem_get_free_16() : __gus_mem_get_free_8(); -} - -/* input value: see to GUS_DOWNLOAD_MODE_XXXX constants (gus.h) - return value: zero if samples & instruments was successfully removed from - GF1 memory manager */ -int gus_memory_reset(int mode) -{ - __gus_mem_clear(); - __gus_instruments_clear(); - return 0; -} - -/* return value: zero if command queue was successfully flushed */ -int gus_queue_flush() -{ - return 0; -} - -/* input value: echo buffer size in items (if 0 - erase echo buffer) */ -int gus_queue_read_set_size(int items) -{ - return 0; -} - -/* input value: write queue size in items (each item have 8 bytes) */ -int gus_queue_write_set_size(int items) -{ - return 0; -} - -/* return value: zero if successfull */ -int gus_timer_start() -{ - gus.timer_ctl_reg |= GF1M_TIMER1; - __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); - - gus.timer_ctl = gus.timer_ctl & ~GF1M_MASK_TIMER1; - outportb(GF1_TIMER_CTRL, 0x04); - outportb(GF1_TIMER_DATA, gus.timer_ctl | GF1M_START_TIMER1); - return 0; -} - -/* return value: zero if timer was stoped */ -int gus_timer_stop() -{ - gus.timer_ctl_reg &= ~GF1M_TIMER1; - __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); - - gus.timer_ctl = gus.timer_ctl | GF1M_MASK_TIMER1; - outportb(GF1_TIMER_CTRL, 0x04); - outportb(GF1_TIMER_DATA, gus.timer_ctl); - return 0; -} - -/* return value: zero if setup was success */ -int gus_timer_tempo(int ticks) -{ - unsigned int counter; - - /* Limit ticks per second to 1..1000 range */ - if (ticks < 1) - ticks = 1; - if (ticks > 1000) - ticks = 1000; - - /* GF1 timer1 period is 80 usecs, 12500 times per second */ - counter = 1250000 / (ticks * gus.timer_base); - gus.t1_multiple = 1; - while (counter > 255) { - counter >>= 1; - gus.t1_multiple <<= 1; - } - gus.t1_countdown = gus.t1_multiple; - __gus_outregb(GF1R_TIMER1, 256 - counter); - return 0; -} - -/* return value: zero if timer will be continue */ -int gus_timer_continue() -{ - return gus_timer_start(); -} - -/* return value: zero if setup was success (default timebase = 100) */ -int gus_timer_base(int base) -{ - gus.timer_base = base; - return 0; -} - -void gus_timer_callback(void (*timer_callback) ()) -{ - gus.timer_callback = timer_callback; -} - -void gus_convert_delta(unsigned int type, unsigned char *dest, - unsigned char *src, size_t size) -{ - if (!(type & GUS_WAVE_DELTA)) - return; - - /* This doesn't depend much on wave signedness, since addition/subtraction - do not depend on operand signedness */ - if (type & GUS_WAVE_16BIT) { - unsigned short delta = type & GUS_WAVE_UNSIGNED ? 0x8000 : 0; - while (size--) { - delta = *(unsigned short *)dest = *(unsigned short *)src + delta; - src += sizeof(unsigned short); - dest += sizeof(unsigned short); - } - } else { - unsigned char delta = type & GUS_WAVE_UNSIGNED ? 0x80 : 0; - while (size--) { - delta = *(unsigned char *)dest = *(unsigned char *)src + delta; - src++; - dest++; - } - } -} - -int gus_dma_usage (int use) -{ - if (gus.dma_buff) - return -1; - gus.transfer = __gus_transfer_io; - return 0; -} - -#endif /* DRV_ULTRA */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosgus.h b/libs/mikmod/drivers/dos/dosgus.h deleted file mode 100644 index ae3488a..0000000 --- a/libs/mikmod/drivers/dos/dosgus.h +++ /dev/null @@ -1,514 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - $Id$ - - libGUS-alike definitions for DOS - -==============================================================================*/ - -#ifndef __DOSGUS_H__ -#define __DOSGUS_H__ - -#include -#include "dosdma.h" -#include "dosirq.h" -#include "libgus.h" - -/* Private header file for a libGUS-alike library for DOS */ - -#define JOYSTICK_TIMER (gus.port+0x201) /* 201 */ -#define JOYSTICK_DATA (gus.port+0x201) /* 201 */ - -#define GF1_MIDI_CTRL (gus.port+0x100) /* 3X0 */ -#define GF1_MIDI_DATA (gus.port+0x101) /* 3X1 */ - -#define GF1_VOICESEL (gus.port+0x102) /* 3X2 */ -#define GF1_REGSEL (gus.port+0x103) /* 3X3 */ -#define GF1_DATA (gus.port+0x104) /* 3X4 */ -#define GF1_DATA_LOW (gus.port+0x104) /* 3X4 */ -#define GF1_DATA_HIGH (gus.port+0x105) /* 3X5 */ -#define GF1_IRQ_STATUS (gus.port+0x006) /* 2X6 */ -#define GF1_DRAM (gus.port+0x107) /* 3X7 */ - -#define GF1_MIX_CTRL (gus.port+0x000) /* 2X0 */ -#define GF1_TIMER_CTRL (gus.port+0x008) /* 2X8 */ -#define GF1_TIMER_DATA (gus.port+0x009) /* 2X9 */ -#define GF1_IRQ_CTRL (gus.port+0x00B) /* 2XB */ -#define GF1_REG_CTRL (gus.port+0x00F) /* 2XF */ - -#define GF1_REVISION (gus.port+0x506) /* 7X6 */ - -/* The GF1 hardware clock rate */ -#define CLOCK_RATE 9878400L - -/* GF1 voice-independent registers */ -#define GF1R_DMA_CONTROL 0x41 -#define GF1R_DMA_ADDRESS 0x42 -#define GF1R_DRAM_LOW 0x43 -#define GF1R_DRAM_HIGH 0x44 - -#define GF1R_TIMER_CONTROL 0x45 -#define GF1R_TIMER1 0x46 -#define GF1R_TIMER2 0x47 - -#define GF1R_SAMPLE_RATE 0x48 -#define GF1R_SAMPLE_CONTROL 0x49 - -#define GF1R_JOYSTICK 0x4B -#define GF1R_RESET 0x4C - -/* GF1 voice-specific registers */ -#define GF1R_VOICE_CONTROL 0x00 -#define GF1R_FREQUENCY 0x01 -#define GF1R_START_HIGH 0x02 -#define GF1R_START_LOW 0x03 -#define GF1R_END_HIGH 0x04 -#define GF1R_END_LOW 0x05 -#define GF1R_VOLUME_RATE 0x06 -#define GF1R_VOLUME_START 0x07 -#define GF1R_VOLUME_END 0x08 -#define GF1R_VOLUME 0x09 -#define GF1R_ACC_HIGH 0x0a -#define GF1R_ACC_LOW 0x0b -#define GF1R_BALANCE 0x0c -#define GF1R_VOLUME_CONTROL 0x0d -#define GF1R_VOICES 0x0e -#define GF1R_IRQ_SOURCE 0x0f - -/* Add this to above registers for reading */ -#define GF1R_READ_MASK 0x80 - -/* MIDI */ -#define GF1M_MIDI_RESET 0x03 -#define GF1M_MIDI_ENABLE_XMIT 0x20 -#define GF1M_MIDI_ENABLE_RCV 0x80 - -#define GF1M_MIDI_RCV_FULL 0x01 -#define GF1M_MIDI_XMIT_EMPTY 0x02 -#define GF1M_MIDI_FRAME_ERR 0x10 -#define GF1M_MIDI_OVERRUN 0x20 -#define GF1M_MIDI_IRQ_PEND 0x80 - -/* Joystick */ -#define GF1M_JOY_POSITION 0x0f -#define GF1M_JOY_BUTTONS 0xf0 - -/* GF1_IRQ_STATUS (port 2X6) */ -#define GF1M_IRQ_MIDI_TX 0x01 /* pending MIDI xmit IRQ */ -#define GF1M_IRQ_MIDI_RX 0x02 /* pending MIDI recv IRQ */ -#define GF1M_IRQ_TIMER1 0x04 /* general purpose timer */ -#define GF1M_IRQ_TIMER2 0x08 /* general purpose timer */ -#define GF1M_IRQ_WAVETABLE 0x20 /* pending wavetable IRQ */ -#define GF1M_IRQ_ENVELOPE 0x40 /* pending volume envelope IRQ */ -#define GF1M_IRQ_DMA_COMPLETE 0x80 /* pending dma transfer complete IRQ */ - -/* GF1_MIX_CTRL (port 2X0) */ -#define GF1M_MIXER_NO_LINE_IN 0x01 /* 0: enable */ -#define GF1M_MIXER_NO_OUTPUT 0x02 /* 0: enable */ -#define GF1M_MIXER_MIC_IN 0x04 /* 1: enable */ -#define GF1M_MIXER_GF1_IRQ 0x08 /* 1: enable */ -#define GF1M_GF1_COMBINED_IRQ 0x10 /* 1: IRQ1 == IRQ2 */ -#define GF1M_MIDI_LOOPBACK 0x20 /* 1: enable loop back */ -#define GF1M_CONTROL_SELECT 0x40 /* 0: DMA latches; 1: IRQ latches */ - -/* Timer data register (2X9) */ -#define GF1M_START_TIMER1 0x01 -#define GF1M_START_TIMER2 0x02 -#define GF1M_MASK_TIMER1 0x20 -#define GF1M_MASK_TIMER2 0x40 -#define GF1M_TIMER_CLRIRQ 0x80 - -/* IRQ/DMA control register (2XB) */ -#define GF1M_IRQ_EQUAL 0x40 -#define GF1M_DMA_EQUAL 0x40 - -/* (0x41) DMA control register bits */ -#define GF1M_DMAR_ENABLE 0x01 /* 1: go */ -#define GF1M_DMAR_READ 0x02 /* 1: read (->RAM), 0: write (->DRAM) */ -#define GF1M_DMAR_CHAN16 0x04 /* 1: 16 bit, 0: 8 bit DMA channel */ -#define GF1M_DMAR_RATE 0x18 /* 00: fast, 11: slow */ -#define GF1M_DMAR_IRQ_ENABLE 0x20 /* 1: enable */ -#define GF1M_DMAR_IRQ_PENDING 0x40 /* R: DMA irq pending */ -#define GF1M_DMAR_DATA16 0x40 /* W: 0: 8 bits; 1: 16 bits per sample */ -#define GF1M_DMAR_TOGGLE_SIGN 0x80 /* W: 1: invert high bit */ - -/* DMA transfer rate divisors */ -#define GF1M_DMAR_RATE0 0x00 /* Fastest DMA xfer (~650khz) */ -#define GF1M_DMAR_RATE1 0x08 /* fastest / 2 */ -#define GF1M_DMAR_RATE2 0x10 /* fastest / 4 */ -#define GF1M_DMAR_RATE3 0x18 /* Slowest DMA xfer (fastest / 8) */ - -/* (0x45) Timer Control */ -#define GF1M_TIMER1 0x04 /* Enable timer 1 IRQ */ -#define GF1M_TIMER2 0x08 /* Enable timer 2 IRQ */ - -/* (0x49) Sampling (ADC) control register */ -#define GF1M_DMAW_ENABLE 0x01 /* 1: Start sampling */ -#define GF1M_DMAW_MODE 0x02 /* 0: mono, 1: stereo */ -#define GF1M_DMAW_CHAN16 0x04 /* 0: 8 bit, 1: 16 bit */ -#define GF1M_DMAW_IRQ_ENABLE 0x20 /* 1: enable IRQ */ -#define GF1M_DMAW_IRQ_PENDING 0x40 /* 1: irq pending */ -#define GF1M_DMAW_TOGGLE_SIGN 0x80 /* 1: invert sign bit */ - -/* (0x4C) GF1 reset register */ -#define GF1M_MASTER_RESET 0x01 /* 0: hold in reset */ -#define GF1M_OUTPUT_ENABLE 0x02 /* 1: enable output */ -#define GF1M_MASTER_IRQ 0x04 /* 1: master IRQ enable */ - -/* (0x0,0x80) Voice control register - GF1R_VOICE_CONTROL */ -#define GF1VC_STOPPED 0x01 /* 1: voice has stopped */ -#define GF1VC_STOP 0x02 /* 1: stop voice */ -#define GF1VC_DATA16 0x04 /* 0: 8 bit, 1: 16 bit */ -#define GF1VC_LOOP_ENABLE 0x08 /* 1: enable */ -#define GF1VC_BI_LOOP 0x10 /* 1: bi directional looping */ -#define GF1VC_IRQ 0x20 /* 1: enable voice's wave irq */ -#define GF1VC_BACKWARD 0x40 /* 0: increasing, 1: decreasing */ -#define GF1VC_IRQ_PENDING 0x80 /* 1: wavetable irq pending */ - -/* (0x01,0x81) Frequency control */ -/* Bit 0 - Unused */ -/* Bits 1-9 - Fractional portion */ -/* Bits 10-15 - Integer portion */ - -/* (0x02,0x82) Accumulator start address - GF1R_START_HIGH */ -/* Bits 0-11 - HIGH 12 bits of address */ -/* Bits 12-15 - Unused */ - -/* (0x03,0x83) Accumulator start address - GF1R_START_LOW */ -/* Bits 0-4 - Unused */ -/* Bits 5-8 - Fractional portion */ -/* Bits 9-15 - Low 7 bits of integer portion */ - -/* (0x04,0x84) Accumulator end address - GF1R_END_HIGH */ -/* Bits 0-11 - HIGH 12 bits of address */ -/* Bits 12-15 - Unused */ - -/* (0x05,0x85) Accumulator end address - GF1R_END_LOW */ -/* Bits 0-4 - Unused */ -/* Bits 5-8 - Fractional portion */ -/* Bits 9-15 - Low 7 bits of integer portion */ - -/* (0x06,0x86) Volume Envelope control register - GF1R_VOLUME_RATE */ -#define GF1VL_RATE_MANTISSA 0x3f -#define GF1VL_RATE_RANGE 0xC0 - -/* (0x07,0x87) Volume envelope start - GF1R_VOLUME_START */ -#define GF1VL_START_MANT 0x0F -#define GF1VL_START_EXP 0xF0 - -/* (0x08,0x88) Volume envelope end - GF1R_VOLUME_END */ -#define GF1VL_END_MANT 0x0F -#define GF1VL_END_EXP 0xF0 - -/* (0x09,0x89) Current volume register - GF1R_VOLUME */ -/* Bits 0-3 - Unused */ -/* Bits 4-11 - Mantissa of current volume */ -/* Bits 10-15 - Exponent of current volume */ - -/* (0x0A,0x8A) Accumulator value (high) */ -/* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */ - -/* (0x0B,0x8B) Accumulator value (low) */ -/* Bits 0-8 - Fractional portion */ -/* Bits 9-15 - Integer portion of low adress (a6-a0) */ - -/* (0x0C,0x8C) Pan (balance) position */ -/* Bits 0-3 - Balance position 0=full left, 0x0f=full right */ - -/* (0x0D,0x8D) Volume control register - GF1R_VOLUME_CONTROL */ -#define GF1VL_STOPPED 0x01 /* volume has stopped */ -#define GF1VL_STOP 0x02 /* stop volume */ -#define GF1VL_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */ -#define GF1VL_LOOP_ENABLE 0x08 /* 1: enable */ -#define GF1VL_BI_LOOP 0x10 /* 1: bi directional looping */ -#define GF1VL_IRQ 0x20 /* 1: enable voice's volume irq */ -#define GF1VL_BACKWARD 0x40 /* 0: increasing, 1: decreasing */ -#define GF1VL_IRQ_PENDING 0x80 /* 1: wavetable irq pending */ - -/* (0x0E,0x8E) Number of active voices */ -/* Bits 0-5 - Number of active voices - 1 */ - -/* (0x0F,0x8F) Sources of IRQs */ -/* Bits 0-4 - interrupting voice number */ -/* Bit 5 - Always a 1 */ -#define GF1IRQ_VOLUME 0x40 /* individual voice irq bit */ -#define GF1IRQ_WAVE 0x80 /* individual waveform irq bit */ - -/* Commands are pooled and executed ON TIMER (1st timer) interrupt. - * Currently there is a limit on the number of commands that you can - * issue between gus_do_flush (...); this should not be an issue however - * because each voice has a limited (little) set of parameters that - * you can change (freq, vol, pan... what else?) - * - * The pool is a pseudo-CPU code that gets executed once per timer interrupt. - */ - -/* Below are definitions for commands placed in GUS command pool */ -#define PCMD_NOP 0x00 /* Traditionally ... */ -#define PCMD_VOICE 0x01 /* +B: select voice */ -#define PCMD_START 0x02 /* +L: start voice */ -#define PCMD_STOP 0x03 /* stop voice */ -#define PCMD_FREQ 0x04 /* +W: set frequence */ -#define PCMD_VOLUME 0x05 /* +W: set volume */ -#define PCMD_VOLUME_PREPARE 0x06 /* +W: prepare to set volume on (soon to follow) kick */ -#define PCMD_PAN 0x07 /* +B: set panning */ -#define PCMD_OFFSET 0x08 /* +L: set DRAM offset */ -#define PCMD_STOP_LOOP 0x09 /* stop looping */ - -#define GUS_VOLCHANGE_RAMP 0x20 /* Volume change ramp speed */ - -/* Definition for the boolean type */ -typedef unsigned char boolean; -/* Prototype for functions that do block transfers to GUS DRAM: - flags can contain any of the following bits: - GUS_WAVE_16BIT - sample is 16-bit - GUS_WAVE_UNSIGNED - do not invert sign bit while downloading - */ -typedef void (*__gus_transfer_func) (unsigned long address, - unsigned char *source, - unsigned long size, int flags); -typedef void (*__gus_callback) (); -typedef void (*__gus_callback_3) (unsigned int, unsigned int, unsigned int); - -/* Structure used to keep track of all on-board GUS memory */ -typedef struct __struct_gus_mcb { - struct __struct_gus_mcb *next; /* Next MCB in chain */ - struct __struct_gus_mcb *prev; /* Previous MCB in chain */ - unsigned int addr; /* GUS DRAM address */ - unsigned int size; /* Memory block size */ - int free; /* 1: block is free */ -} __gus_mcb; - -/* Structure defining overall GUS state/information */ -typedef struct __gus_state_s { - unsigned int port; /* Base I/O port (0x220, 0x240, ...) */ - unsigned int irq[2]; /* GF1 IRQ and MIDI IRQ */ - unsigned int dma[2]; /* Play / record DMA */ - unsigned int ram; /* Memory size (K), i.e. 256, 1024 etc */ - unsigned int version; /* GUS version (see GUS_CARD_VERSION_XXX in libgus.h */ - unsigned int freq; /* Current mixing frequency */ - unsigned int voices; /* Active voices (14-32) */ - unsigned int dynmask; /* Dynamically allocated voices mask */ - unsigned int timer_base; /* The relative timer speed in percents (def: 100) */ - volatile unsigned int t1_ticks; /* Incremented per each timer1 tick */ - volatile unsigned int t2_ticks; /* Incremented per each timer2 tick */ - volatile unsigned int t1_countdown; /* t1_callback is called when this reaches zero */ - volatile unsigned int t2_countdown; /* t2_callback is called when this reaches zero */ - unsigned int t1_multiple; /* Timer1 handler is called once per such many ticks */ - unsigned int t2_multiple; /* Timer2 handler is called once per such many ticks */ - struct irq_handle *gf1_irq; /* The interrupt handler for GF1 events */ - dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ - __gus_callback dma_callback; /* Routine called at end of DMA transfers */ - __gus_callback t1_callback; /* Routine called on Timer1 events */ - __gus_callback t2_callback; /* Routine called on Timer1 events */ - __gus_callback timer_callback; /* Called once per TEMPO ticks */ - __gus_callback_3 wt_callback; /* Routine called on WaveTable events */ - __gus_callback_3 vl_callback; /* Routine called on Volume ramp events */ - __gus_mcb *mcb; /* Chained list of memory control blocks */ - __gus_transfer_func transfer; /* Best working function for DRAM transfer */ - gus_instrument_t *instr; /* The list of registered instruments */ - unsigned short mixer; /* Current mixer register state */ - unsigned char dma_rate; /* One of GF1M_DMAR_RATEX constants defined above */ - unsigned char timer_ctl; /* Timer control register value (2x8/2x9) */ - unsigned char timer_ctl_reg; /* Timer control register value (GF1/0x45) */ - boolean ok; /* Is the information below okay? */ - boolean open; /* 1 if between gus_open() and gus_close() */ - boolean ics; /* Is it equipped with an ICS mixer? */ - boolean ics_flipped; /* rev 5 (3.7) has flipped R/L mixer */ - boolean codec; /* Is it equipped with a GUS MAX codec? */ - boolean interwave; /* GUS InterWave card */ - volatile boolean dma_active; /* DMA is transferring data */ - volatile boolean cmd_pool_ready; /* Flush cmd_pool during timer interrupt */ - unsigned char cmd_voice; /* Pool selection index cache */ - unsigned int cmd_pool_top; /* Command pool top */ - unsigned char *cmd_pool; /* Async commands pool */ - /* The following data is for private use only by interrupt routines! */ - gus_wave_t *cur_wave[32]; /* Currently played waves */ - boolean voice_kick[32]; /* Kick wave on next volume ramp IRQ */ - unsigned int kick_offs[32]; /* Sample start position on kick */ - unsigned short cur_vol[32]; /* Current voice volumes */ - unsigned int cur_voice; /* Current voice */ - unsigned int eow_ignore; /* Temp ignore end-of-wave IRQ for these voices */ -} __gus_state; - -extern __gus_state gus; -extern void __gus_delay(); - -static unsigned long __gus_convert_addr16(unsigned long address) -{ - return ((address & 0x0003ffff) >> 1) | (address & ~0x0003ffff); -} - -/* The XXX_slow routines cannot be used outside IRQ handler! */ -static inline void __gus_outregb_slow(unsigned char reg, unsigned char value) -{ - outportb(GF1_REGSEL, reg); - outportb(GF1_DATA_HIGH, value); - __gus_delay(); - outportb(GF1_DATA_HIGH, value); -} - -static inline void __gus_outregw_slow(unsigned char reg, unsigned short value) -{ - outportb(GF1_REGSEL, reg); - outportw(GF1_DATA, value); - __gus_delay(); - outportw(GF1_DATA, value); -} - -static inline void __gus_outregb(unsigned char reg, unsigned char value) -{ - outportb(GF1_REGSEL, reg); - outportb(GF1_DATA_HIGH, value); -} - -static inline void __gus_outregw(unsigned char reg, unsigned short value) -{ - outportb(GF1_REGSEL, reg); - outportw(GF1_DATA, value); -} - -static inline unsigned char __gus_inregb(unsigned char reg) -{ - if (reg < 0x10) - reg |= GF1R_READ_MASK; - outportb(GF1_REGSEL, reg); - return inportb(GF1_DATA_HIGH); -} - -static inline unsigned short __gus_inregw(unsigned char reg) -{ - if (reg < 0x10) - reg |= GF1R_READ_MASK; - outportb(GF1_REGSEL, reg); - return inportw(GF1_DATA); -} - -static inline void __gus_set_dram_address(unsigned int address) -{ - __gus_outregb(GF1R_DRAM_HIGH, address >> 16); - __gus_outregw(GF1R_DRAM_LOW, address); -} - -static inline unsigned char __gus_peek(unsigned int address) -{ - __gus_set_dram_address(address); - return inportb(GF1_DRAM); -} - -static inline void __gus_poke(unsigned int address, unsigned char value) -{ - __gus_set_dram_address(address); - outportb(GF1_DRAM, value); -} - -static inline void __gus_select_voice(unsigned char voice) -{ - outportb(GF1_VOICESEL, voice); -} - -static inline void __gus_set_current(unsigned char mode, - unsigned long address) -{ - if (mode & GF1VC_DATA16) - address = __gus_convert_addr16(address); - __gus_outregw_slow(GF1R_ACC_HIGH, address >> 11); - __gus_outregw_slow(GF1R_ACC_LOW, address << 5); -} - -static inline void __gus_set_loop_start(unsigned char mode, - unsigned long address) -{ - if (mode & GF1VC_DATA16) - address = __gus_convert_addr16(address); - __gus_outregw_slow(GF1R_START_HIGH, address >> 11); - __gus_outregw_slow(GF1R_START_LOW, address << 5); -} - -static inline void __gus_set_loop_end(unsigned char mode, - unsigned long address) -{ - address--; - if (mode & GF1VC_DATA16) - address = __gus_convert_addr16(address); - __gus_outregw_slow(GF1R_END_HIGH, address >> 11); - __gus_outregw_slow(GF1R_END_LOW, address << 5); -} - -static inline void __gus_mixer_output(boolean state) -{ - if (state) - gus.mixer &= ~GF1M_MIXER_NO_OUTPUT; - else - gus.mixer |= GF1M_MIXER_NO_OUTPUT; - outportb(GF1_MIX_CTRL, gus.mixer); - /* Dummy read to avoid touching DMA latches */ - __gus_inregb(GF1R_BALANCE); -} - -/* Inline routines for working with command pools */ - -/* WARNING: no bounds checking due to performance reasons */ -#define __POOL_VALUE(type,value) \ - *((unsigned type *)&gus.cmd_pool [gus.cmd_pool_top]) = value; \ - gus.cmd_pool_top += sizeof (type); - -static inline void __pool_command(unsigned char command) -{ - __POOL_VALUE(char, command); -} - -static inline void __pool_command_b(unsigned char command, unsigned char arg) -{ - __POOL_VALUE(char, command); - __POOL_VALUE(char, arg); -} - -static inline void __pool_command_w(unsigned char command, unsigned short arg) -{ - __POOL_VALUE(char, command); - __POOL_VALUE(short, arg); -} - -static inline void __pool_command_l(unsigned char command, unsigned long arg) -{ - __POOL_VALUE(char, command); - __POOL_VALUE(long, arg); -} - -static inline void __pool_select_voice(unsigned char voice) -{ - if (gus.cmd_voice != voice) - __pool_command_b(PCMD_VOICE, gus.cmd_voice = voice); -} - -#undef __POOL_VALUE - -#ifdef DEBUG -/* Debug dump of GUS DRAM heap */ -extern void __gus_mem_dump(); -#endif - -#endif /* __DOSGUS_H__ */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosirq.c b/libs/mikmod/drivers/dos/dosirq.c deleted file mode 100644 index 9b0e21f..0000000 --- a/libs/mikmod/drivers/dos/dosirq.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - Implementation of IRQ routines on DOS - Copyright (C) 1999 by Andrew Zabolotny, - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "dosirq.h" - -#include -#include -#include -#include -#include -#include -#include "mikmod.h" /* for MikMod_malloc() & co */ - -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 - -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 *) MikMod_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)) { - MikMod_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); - - MikMod_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 *) MikMod_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)) { - MikMod_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); - - MikMod_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(); -} - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosirq.h b/libs/mikmod/drivers/dos/dosirq.h deleted file mode 100644 index eaf60a1..0000000 --- a/libs/mikmod/drivers/dos/dosirq.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - Interface for IRQ routines on DOS - Copyright (C) 1999 by Andrew Zabolotny, - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __DOSIRQ_H__ -#define __DOSIRQ_H__ - -#include - -#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__ */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dossb.c b/libs/mikmod/drivers/dos/dossb.c deleted file mode 100644 index 344ac85..0000000 --- a/libs/mikmod/drivers/dos/dossb.c +++ /dev/null @@ -1,575 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - Sound Blaster I/O routines, common for SB8, SBPro and SB16 - Written by Andrew Zabolotny - -==============================================================================*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef DRV_SB - -#include -#include -#include -#include -#include -#include -#include - -#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; - unsigned int timer, oldcount; - - 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; -} - -#endif /* DRV_SB */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dossb.h b/libs/mikmod/drivers/dos/dossb.h deleted file mode 100644 index cb8dc70..0000000 --- a/libs/mikmod/drivers/dos/dossb.h +++ /dev/null @@ -1,341 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - $Id$ - - SoundBlaster and compatible soundcards definitions - -==============================================================================*/ - -#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__ */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/doswss.c b/libs/mikmod/drivers/dos/doswss.c deleted file mode 100644 index 41cdf38..0000000 --- a/libs/mikmod/drivers/dos/doswss.c +++ /dev/null @@ -1,577 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - Windows Sound System I/O routines (CS42XX, ESS18XX, GUS+DaughterBoard etc) - Written by Andrew Zabolotny - -==============================================================================*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef DRV_WSS - -#include -#include -#include -#include -#include -#include -#include - -#include "doswss.h" - -/********************************************* Private variables/routines *****/ - -__wss_state wss; - -/* WSS frequency rates... lower bit selects one of two frequency generators */ -static unsigned int wss_rates[14][2] = { - {5510, 0x00 | WSSM_XTAL2}, - {6620, 0x0E | WSSM_XTAL2}, - {8000, 0x00 | WSSM_XTAL1}, - {9600, 0x0E | WSSM_XTAL1}, - {11025, 0x02 | WSSM_XTAL2}, - {16000, 0x02 | WSSM_XTAL1}, - {18900, 0x04 | WSSM_XTAL2}, - {22050, 0x06 | WSSM_XTAL2}, - {27420, 0x04 | WSSM_XTAL1}, - {32000, 0x06 | WSSM_XTAL1}, - {33075, 0x0C | WSSM_XTAL2}, - {37800, 0x08 | WSSM_XTAL2}, - {44100, 0x0A | WSSM_XTAL2}, - {48000, 0x0C | WSSM_XTAL1} -}; - -static void wss_irq() -{ - /* Make sure its not a spurious IRQ */ - if (!irq_check(wss.irq_handle)) - return; - - wss.irqcount++; - - /* Clear IRQ status */ - outportb(WSS_STATUS, 0); - - /* Write transfer count again */ - __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff); - __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8); - irq_ack(wss.irq_handle); - - enable(); - if (wss.timer_callback) - wss.timer_callback(); -} - -static void wss_irq_end() -{ -} - -/* WSS accepts some conventional values instead of frequency in Hz... */ -static unsigned char __wss_getrate(unsigned int *freq) -{ - int i, best = -1, delta = 0xffff; - - for (i = 0; i < 14; i++) { - int newdelta = abs(wss_rates[i][0] - *freq); - if (newdelta < delta) - best = i, delta = newdelta; - } - - *freq = wss_rates[best][0]; - return wss_rates[best][1]; -} - -/* Check if we really have a WSS compatible card on given address */ -static boolean __wss_ping() -{ - /* Disable CODEC operations first */ - __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - /* Now put some harmless values in registers and check them */ - __wss_outreg(WSSR_COUNT_LOW, 0xaa); - __wss_outreg(WSSR_COUNT_HIGH, 0x55); - return (__wss_inreg(WSSR_COUNT_LOW) == 0xaa) - && (__wss_inreg(WSSR_COUNT_HIGH) == 0x55); -} - -static boolean __wss_reset() -{ - int count; - - /* Disable output */ - wss_output(FALSE); - - /* Now select the test/initialization register */ - count = 10000; - while (inportb(WSS_ADDR) != WSSR_TEST_INIT) { - outportb(WSS_ADDR, WSSR_TEST_INIT); - if (!--count) - return FALSE; - } - - count = 10000; - while (inportb(WSS_DATA) & WSSM_CALIB_IN_PROGRESS) { - outportb(WSS_ADDR, WSSR_TEST_INIT); - if (!--count) - return FALSE; - } - - /* Enable playback IRQ */ - __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); - __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); - - /* Clear IRQ status */ - outportb(WSS_STATUS, 0); - - return TRUE; -} - -static boolean __wss_setformat(unsigned char format) -{ - int count; - - outportb(WSS_ADDR, WSSM_MCE | WSSR_PLAY_FORMAT); - outportb(WSS_DATA, format); - inportb(WSS_DATA); /* ERRATA SHEETS ... */ - inportb(WSS_DATA); /* ERRATA SHEETS ... */ - - /* Wait end of syncronization ... */ - if (!__wss_wait()) - return FALSE; - - /* Turn off the ModeChangeEnable bit: do it until it works */ - count = 10000; - while (inportb(WSS_ADDR) != WSSR_PLAY_FORMAT) { - outportb(WSS_ADDR, WSSR_PLAY_FORMAT); - if (!--count) - return FALSE; - } - - return __wss_reset(); -} - -/**************************************************** WSS detection stuff *****/ - -static int __wss_irq_irqdetect(int irqno) -{ - unsigned char status = inportb(WSS_STATUS); - /* Clear IRQ status */ - outportb(WSS_STATUS, 0); - /* Reset transfer counter */ - __wss_outreg(WSSR_COUNT_LOW, 0); - __wss_outreg(WSSR_COUNT_HIGH, 0); - return (status & WSSM_INT); -} - -static boolean __wss_detect() -{ - /* First find the port number */ - if (!wss.port) { - static unsigned int wss_ports[] = - { 0x32c, 0x530, 0x604, 0xE80, 0xF40 }; - int i; - for (i = 0; i < 5; i++) { - wss.port = wss_ports[i]; - if (__wss_ping()) - break; - } - if (i < 0) { - wss.port = 0; - return FALSE; - } - } - - /* Now disable output */ - wss_output(FALSE); - - /* Detect the DMA channel */ - if (!wss.dma) { - static int __dma[] = { 0, 1, 3 }; - int i; - - /* Enable playback IRQ */ - __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); - __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); - - /* Start a short DMA transfer and check if DMA count is zero */ - for (i = 0; i < 3; i++) { - unsigned int timer, status, freq = 44100; - - wss.dma = __dma[i]; - - dma_disable(wss.dma); - dma_set_mode(wss.dma, DMA_MODE_WRITE); - dma_clear_ff(wss.dma); - dma_set_count(wss.dma, 10); - dma_enable(wss.dma); - - /* Clear IRQ status */ - outportb(WSS_STATUS, 0); - - __wss_setformat(__wss_getrate(&freq)); - __wss_outreg(WSSR_COUNT_LOW, 1); - __wss_outreg(WSSR_COUNT_HIGH, 0); - /* Tell codec to start transfer */ - __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - - _farsetsel(_dos_ds); - timer = _farnspeekl(0x46c); - - while (_farnspeekl(0x46c) - timer <= 2) - if (dma_get_count(wss.dma) == 0) - break; - __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - dma_disable(wss.dma); - - /* Now check if DMA transfer count is zero and an IRQ is pending */ - status = inportb(WSS_STATUS); - outportb(WSS_STATUS, 0); - if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT)) - break; - - wss.dma = 0; - } - - if (!wss.dma) - return FALSE; - } - - /* Now detect the IRQ number */ - if (!wss.irq) { - unsigned int i, irqmask, freq = 5510; - unsigned long timer, delta = 0x7fffffff; - - /* IRQ can be one of 2,3,5,7,10 */ - irq_detect_start(0x04ac, __wss_irq_irqdetect); - - dma_disable(wss.dma); - dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - dma_clear_ff(wss.dma); - dma_set_count(wss.dma, 1); - dma_enable(wss.dma); - - __wss_setformat(__wss_getrate(&freq)); - - /* Clear IRQ status */ - outportb(WSS_STATUS, 0); - - __wss_outreg(WSSR_COUNT_LOW, 0); - __wss_outreg(WSSR_COUNT_HIGH, 0); - - /* Prepare timeout counter */ - _farsetsel(_dos_ds); - timer = _farnspeekl(0x46c); - while (timer == _farnspeekl(0x46c)); - timer = _farnspeekl(0x46c); - - /* Reset all IRQ counters */ - irq_detect_clear(); - - /* Tell codec to start transfer */ - __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - - /* Now wait 1/18 seconds */ - while (timer == _farnspeekl(0x46c)); - __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - dma_disable(wss.dma); - - /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval - of 1/18.2 second, we should have received about 302 interrupts */ - for (i = 2; i <= 10; i++) { - int count = abs(302 - irq_detect_get(i, &irqmask)); - if (count < delta) - wss.irq = i, delta = count; - } - if (delta > 150) - wss.irq = 0; - - irq_detect_end(); - if (!wss.irq) - return FALSE; - } - - return TRUE; -} - -/*************************************************** High-level interface *****/ - -/* Detect whenever WSS is present and fill "wss" structure */ -boolean wss_detect() -{ - char *env; - - /* Try to find the port and DMA from environment */ - env = getenv("WSS"); - - while (env && *env) { - /* Skip whitespace */ - while ((*env == ' ') || (*env == '\t')) - env++; - if (!*env) - break; - - switch (*env++) { - case 'A': - case 'a': - if (!wss.port) - wss.port = strtol(env, &env, 16); - break; - case 'I': - case 'i': - if (!wss.irq) - wss.irq = strtol(env, &env, 10); - break; - case 'D': - case 'd': - if (!wss.dma) - wss.dma = strtol(env, &env, 10); - break; - default: - /* Skip other values */ - while (*env && (*env != ' ') && (*env != '\t')) - env++; - break; - } - } - - /* Try to fill the gaps in wss hardware parameters */ - __wss_detect(); - - if (!wss.port || !wss.irq || !wss.dma) - return FALSE; - - if (!__wss_ping()) - return FALSE; - - if (!__wss_reset()) - return FALSE; - - wss.ok = 1; - return TRUE; -} - -/* Reset WSS */ -void wss_reset() -{ - wss_stop_dma(); - __wss_reset(); -} - -/* Open WSS for usage */ -boolean wss_open() -{ - __dpmi_meminfo struct_info; - - if (!wss.ok) - if (!wss_detect()) - return FALSE; - - if (wss.open) - return FALSE; - - /* Now lock the wss structure in memory */ - struct_info.address = __djgpp_base_address + (unsigned long)&wss; - struct_info.size = sizeof(wss); - if (__dpmi_lock_linear_region(&struct_info)) - return FALSE; - - /* Hook the WSS IRQ */ - wss.irq_handle = - irq_hook(wss.irq, wss_irq, (long)wss_irq_end - (long)wss_irq); - if (!wss.irq_handle) { - __dpmi_unlock_linear_region(&struct_info); - return FALSE; - } - - /* Enable the interrupt */ - irq_enable(wss.irq_handle); - if (wss.irq > 7) - _irq_enable(2); - - wss.open++; - - return TRUE; -} - -/* Finish working with WSS */ -boolean wss_close() -{ - __dpmi_meminfo struct_info; - if (!wss.open) - return FALSE; - - wss.open--; - - /* Stop/free DMA buffer */ - wss_stop_dma(); - - /* Unhook IRQ */ - irq_unhook(wss.irq_handle); - wss.irq_handle = NULL; - - /* Unlock the wss structure */ - struct_info.address = __djgpp_base_address + (unsigned long)&wss; - struct_info.size = sizeof(wss); - __dpmi_unlock_linear_region(&struct_info); - - return TRUE; -} - -/* Adjust frequency rate to nearest WSS available */ -unsigned int wss_adjust_freq(unsigned int freq) -{ - __wss_getrate(&freq); - return freq; -} - -/* Enable/disable speaker output */ -/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ -boolean wss_start_dma(unsigned char mode, unsigned int freq) -{ - int dmabuffsize; - unsigned char format; - - /* Stop DMA transfer if it is enabled */ - wss_stop_dma(); - - /* Sanity check: we support only 8-bit unsigned and 16-bit signed formats */ - if (((mode & WSSMODE_16BITS) && !(mode & WSSMODE_SIGNED)) - || (!(mode & WSSMODE_16BITS) && (mode & WSSMODE_SIGNED))) - return FALSE; - - /* Find the nearest frequency divisor (rate) */ - format = __wss_getrate(&freq); - wss.mode = mode; - - /* Get a DMA buffer enough for a 1sec interval... 4K <= dmasize <= 32K */ - dmabuffsize = freq; - if (mode & WSSMODE_STEREO) - dmabuffsize *= 2; - if (mode & WSSMODE_16BITS) - dmabuffsize *= 2; - dmabuffsize >>= 2; - if (dmabuffsize < 4096) - dmabuffsize = 4096; - if (dmabuffsize > 32768) - dmabuffsize = 32768; - dmabuffsize = (dmabuffsize + 255) & 0xffffff00; - - wss.dma_buff = dma_allocate(wss.dma, dmabuffsize); - if (!wss.dma_buff) - return FALSE; - - /* Fill DMA buffer with silence */ - dmabuffsize = wss.dma_buff->size; - if (mode & WSSMODE_SIGNED) - memset(wss.dma_buff->linear, 0, dmabuffsize); - else - memset(wss.dma_buff->linear, 0x80, dmabuffsize); - - /* Check data size and build a WSSR_PLAY_FORMAT value accordingly */ - wss.samples = dmabuffsize; - if (mode & WSSMODE_16BITS) { - wss.samples >>= 1; - format |= WSSM_16BITS; - } - - if (mode & WSSMODE_STEREO) { - wss.samples >>= 1; - format |= WSSM_STEREO; - } - - if (!__wss_setformat(format)) { - wss_stop_dma(); - return FALSE; - } - - /* Prime DMA for transfer */ - dma_start(wss.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - - /* Tell codec how many samples to transfer */ - wss.samples = (wss.samples >> 1) - 1; - __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff); - __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8); - - /* Tell codec to start transfer */ - __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - - return TRUE; -} - -/* Stop playing from DMA buffer */ -void wss_stop_dma() -{ - if (!wss.dma_buff) - return; - - __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); - dma_disable(wss.dma); - dma_free(wss.dma_buff); - wss.dma_buff = NULL; -} - -/* Query current position/total size of the DMA buffer */ -void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos) -{ - unsigned int dma_left; - *dma_size = wss.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(wss.dma); - dma_left_test = dma_get_count(wss.dma); - dma_left = dma_get_count(wss.dma); - if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10)) - break; - } - *dma_pos = *dma_size - dma_left; -} - -void wss_output(boolean enable) -{ - if (enable) - wss.curlevel = wss.level; - else - wss.curlevel = 0x3f; - - __wss_outreg(WSSR_MASTER_L, wss.curlevel); - __wss_outreg(WSSR_MASTER_R, wss.curlevel); -} - -void wss_level(int level) -{ - if (level < 0) - level = 0; - if (level > 63) - level = 63; - wss.curlevel = wss.level = level ^ 63; - - __wss_outreg(WSSR_MASTER_L, wss.curlevel); - __wss_outreg(WSSR_MASTER_R, wss.curlevel); -} - -#endif /* DRV_WSS */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/doswss.h b/libs/mikmod/drivers/dos/doswss.h deleted file mode 100644 index ae77bb5..0000000 --- a/libs/mikmod/drivers/dos/doswss.h +++ /dev/null @@ -1,217 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - $Id$ - - Windows Sound System and compatible soundcards definitions - -==============================================================================*/ - -#ifndef __DOSWSS_H__ -#define __DOSWSS_H__ - -#include "dosdma.h" -#include "dosirq.h" - -#define WSS_ADDR (wss.port + 0x04) -#define WSS_DATA (wss.port + 0x05) -#define WSS_STATUS (wss.port + 0x06) -#define WSS_PIO (wss.port + 0x07) - -/* WSS_ADDR: Bits 0-4 select an internal register to read/write */ -#define WSSR_INPUT_L 0x00 /* Left input control register */ -#define WSSR_INPUT_R 0x01 /* RIght input control register */ -#define WSSR_AUX1_L 0x02 /* Left Aux #1 input control */ -#define WSSR_AUX1_R 0x03 /* Right Aux #1 input control */ -#define WSSR_CD_L 0x04 /* Left Aux #2 input control */ -#define WSSR_CD_R 0x05 /* Right Aux #2 input control */ -#define WSSR_MASTER_L 0x06 /* Left output control */ -#define WSSR_MASTER_R 0x07 /* Right output control */ -#define WSSR_PLAY_FORMAT 0x08 /* Clock and data format */ -#define WSSR_IFACE_CTRL 0x09 /* Interface control */ -#define WSSR_PIN_CTRL 0x0a /* Pin control */ -#define WSSR_TEST_INIT 0x0b /* Test and initialization */ -#define WSSR_MISC_INFO 0x0c /* Miscellaneaous information */ -#define WSSR_LOOPBACK 0x0d /* Digital Mix */ -#define WSSR_COUNT_HIGH 0x0e /* Playback Upper Base Count */ -#define WSSR_COUNT_LOW 0x0f /* Playback Lower Base Count */ -#define WSSR_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ -#define WSSR_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ -#define WSSR_LINE_IN_L 0x12 /* left line input control */ -#define WSSR_LINE_IN_R 0x13 /* right line input control */ -#define WSSR_TIMER_LOW 0x14 /* timer low byte */ -#define WSSR_TIMER_HIGH 0x15 /* timer high byte */ -#define WSSR_IRQ_STATUS 0x18 /* irq status register */ -#define WSSR_MONO_IO_CTRL 0x1a /* mono input/output control */ -#define WSSR_REC_FORMAT 0x1c /* record format */ -#define WSSR_REC_COUNT_HIGH 0x1e /* record upper count */ -#define WSSR_REC_COUNT_LOW 0x1f /* record lower count */ - -/* WSS_ADDR bits 7-5 definition */ -#define WSSM_INIT 0x80 /* Codec is initializing */ -#define WSSM_MCE 0x40 /* Mode change enable */ -#define WSSM_TRD 0x20 /* Transfer Request Disable */ -/* bits 4-0 are indirect register address (0-15) */ - -/* WSS_STATUS bit masks */ -#define WSSM_CUL 0x80 /* Capture data upper/lower byte */ -#define WSSM_CLR 0x40 /* Capture left/right sample */ -#define WSSM_CRDY 0x20 /* Capture data read */ -#define WSSM_SOUR 0x10 /* Playback over/under run error */ -#define WSSM_PUL 0x08 /* Playback upper/lower byte */ -#define WSSM_PLR 0x04 /* Playback left/right sample */ -#define WSSM_PRDY 0x02 /* Playback data register read */ -#define WSSM_INT 0x01 /* interrupt status */ - -/* Definitions for output level registers */ -#define WSSM_MUTE 0x80 /* Mute this output source */ -/* bits 5-0 are left output attenuation select (0-63) */ -/* bits 5-0 are right output attenuation select (0-63) */ - -/* Definitions for clock and data format register (WSSR_PLAY_FORMAT) */ -#define WSSM_STEREO 0x10 /* stero mode */ -#define WSSM_ULAW_8 0x20 /* 8-bit U-law companded */ -#define WSSM_16BITS 0x40 /* 16 bit twos complement data - little endian */ -#define WSSM_ALAW_8 0x60 /* 8-bit A-law companded */ -#define WSSM_16BITS_BE 0xc0 /* 16-bit twos complement data - big endian */ -#define WSSM_ADPCM_16 0xa0 /* 16-bit ADPCM */ -/* Bits 3-1 define frequency divisor */ -#define WSSM_XTAL1 0x00 /* 24.576 crystal */ -#define WSSM_XTAL2 0x01 /* 16.9344 crystal */ - -/* Definitions for interface control register (WSSR_IFACE_CTRL) */ -#define WSSM_CAPTURE_PIO 0x80 /* Capture PIO enable */ -#define WSSM_PLAYBACK_PIO 0x40 /* Playback PIO enable */ -#define WSSM_AUTOCALIB 0x08 /* auto calibrate */ -#define WSSM_SINGLE_DMA 0x04 /* Use single DMA channel */ -#define WSSM_PLAYBACK_ENABLE 0x01 /* playback enable */ - -/* Definitions for Pin control register (WSSR_PIN_CTRL) */ -#define WSSM_IRQ_ENABLE 0x02 /* interrupt enable */ -#define WSSM_XCTL1 0x40 /* external control #1 */ -#define WSSM_XCTL0 0x80 /* external control #0 */ - -/* Definitions for WSSR_TEST_INIT register */ -#define WSSM_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ - -/* Definitions for misc control register (WSR_MISC_INFO) */ -#define WSSM_MODE2 0x40 /* MODE 2 */ -#define WSSM_MODE3 0x6c /* MODE 3 - enhanced mode */ - -/* Definitions for codec irq status (WSSR_IRQ_STATUS) */ -#define WSSM_PLAYBACK_IRQ 0x10 -#define WSSM_RECORD_IRQ 0x20 -#define WSSM_TIMER_IRQ 0x40 - -typedef unsigned char boolean; - -#ifndef FALSE -#define FALSE 0 -#define TRUE 1 -#endif - -/* Play mode bits */ -#define WSSMODE_16BITS 0x0001 -#define WSSMODE_STEREO 0x0002 -#define WSSMODE_SIGNED 0x0004 - -/* You can fill some members of this struct (i.e. port,irq,dma) before - * calling wss_detect() or wss_open()... this will ignore environment settings. - */ -typedef struct __wss_state_s { - boolean ok; /* Set if this structure is properly filled */ - int port; /* Base codec port */ - int irq; /* codec IRQ */ - int dma; /* codec DMA */ - struct irq_handle *irq_handle; /* The interrupt handler */ - dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ - unsigned char mode; /* Current WSS mode (WSSMODE_XXX) */ - boolean open; /* Whenever the card has been opened */ - int samples; /* Number of samples in DMA buffer */ - unsigned char level; /* Output level (63..0): doesn't change when mute */ - unsigned char curlevel; /* Current output level (63(min)..0(max)) */ - volatile int irqcount; /* Incremented on each IRQ... for diagnostics */ - void (*timer_callback) (); /* Called TWICE per buffer play */ -} __wss_state; - -extern __wss_state wss; - -/* Wait until codec finishes initialization */ -static inline boolean __wss_wait() -{ - int count; - for (count = 10000; count >= 0; count--) - if (!(inportb(WSS_ADDR) & WSSM_INIT)) - return TRUE; - return FALSE; -} - -static inline void __wss_outreg(unsigned char reg, unsigned char val) -{ - outportb(WSS_ADDR, reg); - outportb(WSS_DATA, val); -} - -static inline unsigned char __wss_inreg(unsigned char reg) -{ - outportb(WSS_ADDR, reg); - return inportb(WSS_DATA); -} - -/* Set some bits in a specific register */ -static inline void __wss_regbit_set(unsigned char reg, unsigned char mask) -{ - outportb(WSS_ADDR, reg); - outportb(WSS_DATA, inportb(WSS_DATA) | mask); -} - -/* Reset some bits in a specific register */ -static inline void __wss_regbit_reset(unsigned char reg, unsigned char mask) -{ - outportb(WSS_ADDR, reg); - outportb(WSS_DATA, inportb(WSS_DATA) & ~mask); -} - -/* Detect whenever WSS is present and fill "wss" structure */ -extern boolean wss_detect(); -/* Reset WSS */ -extern void wss_reset(); -/* Open WSS for usage */ -extern boolean wss_open(); -/* Finish working with WSS */ -extern boolean wss_close(); -/* Enable/disable speaker output */ -extern void wss_output(boolean enable); -/* Adjust frequency rate to nearest WSS available */ -extern unsigned int wss_adjust_freq(unsigned int freq); -/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ -extern boolean wss_start_dma(unsigned char mode, unsigned int freq); -/* Stop playing from DMA buffer */ -extern void wss_stop_dma(); -/* Query current position/total size of the DMA buffer */ -extern void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos); -/* Set output level (0(min)-63(max)) */ -extern void wss_level(int level); - -#endif /* __DOSWSS_H__ */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/libgus.h b/libs/mikmod/drivers/dos/libgus.h deleted file mode 100644 index 0d66ee9..0000000 --- a/libs/mikmod/drivers/dos/libgus.h +++ /dev/null @@ -1,402 +0,0 @@ -/* MikMod sound library - (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for - complete list. - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -/*============================================================================== - - $Id$ - - Linux libGUS-alike library for DOS, used by drv_ultra.c under DOS. - -==============================================================================*/ - -/* - Current limitations: - - Only a subset of libgus is supported - - Only one GUS card is supported (due to the fact that ULTRASND environment - variable is used) - - No Interwawe support (if IW works the old way, it's ok). -*/ - -#ifndef __LIBGUS_H__ -#define __LIBGUS_H__ - -#include - -#define __LITTLE_ENDIAN - -typedef struct _gus_info_t gus_info_t; -typedef struct _gus_instrument_t gus_instrument_t; -typedef struct _gus_wave_t gus_wave_t; -typedef struct _gus_layer_t gus_layer_t; - -#define GUS_CARD_VERSION_CLASSIC 0x0024 /* revision 2.4 */ -#define GUS_CARD_VERSION_CLASSIC1 0x0034 /* revision 3.4? */ -#define GUS_CARD_VERSION_CLASSIC_ICS 0x0037 /* revision 3.7 (ICS mixer) */ -#define GUS_CARD_VERSION_EXTREME 0x0050 /* GUS Extreme */ -#define GUS_CARD_VERSION_ACE 0x0090 /* GUS ACE */ -#define GUS_CARD_VERSION_MAX 0x00a0 /* GUS MAX - revision 10 */ -#define GUS_CARD_VERSION_MAX1 0x00a1 /* GUS MAX - revision 11 */ -#define GUS_CARD_VERSION_PNP 0x0100 /* GUS Plug & Play */ - -#define GUS_STRU_INFO_F_DB16 0x00000001 /* 16-bit daughter board present */ -#define GUS_STRU_INFO_F_PCM 0x00000004 /* GF1 PCM during SYNTH enabled */ -#define GUS_STRU_INFO_F_ENHANCED 0x00000008 /* InterWave - enhanced mode */ -#define GUS_STRU_INFO_F_DAEMON 0x00000010 /* instrument daemon is present */ - -struct _gus_info_t { - unsigned char id[8]; /* id of this card (warning! maybe unterminated!!!) */ - - unsigned int flags; /* some info flags - see to GUS_STRU_INFO_F_XXXX */ - unsigned int version; /* see to GUS_CARD_VERSION_XXXX constants */ - - unsigned short port; - unsigned short irq; - unsigned short dma1; /* DMA1 - GF1 download & codec record */ - unsigned short dma2; /* DMA2 - GF1 record & codec playback */ - - unsigned int mixing_freq; /* mixing frequency in Hz */ - - unsigned int memory_size; /* in bytes */ - unsigned int memory_free; /* in bytes */ - unsigned int memory_block_8; /* largest free 8-bit block in memory */ - unsigned int memory_block_16; /* largest free 16-bit block in memory */ -}; - -/* struct gus_instrument_t - mode */ - -#define GUS_INSTR_SIMPLE 0x00 /* simple format - for MOD players */ -#define GUS_INSTR_PATCH 0x01 /* old GF1 patch format */ -#define GUS_INSTR_COUNT 2 - -#define GUS_INSTR_F_NORMAL 0x0000 /* normal mode */ -#define GUS_INSTR_F_NOT_FOUND 0x0001 /* instrument can't be loaded */ -#define GUS_INSTR_F_ALIAS 0x0002 /* alias */ -#define GUS_INSTR_F_NOT_LOADED 0x00ff /* instrument not loaded (not found) */ - -#define GUS_INSTR_E_NONE 0x0000 /* exclusion mode - none */ -#define GUS_INSTR_E_SINGLE 0x0001 /* exclude single - single note from this instrument */ -#define GUS_INSTR_E_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ - -#define GUS_INSTR_L_NONE 0x0000 /* not layered */ -#define GUS_INSTR_L_ON 0x0001 /* layered */ -#define GUS_INSTR_L_VELOCITY 0x0002 /* layered by velocity */ -#define GUS_INSTR_L_FREQUENCY 0x0003 /* layered by frequency */ - -struct _gus_instrument_t { - union { - unsigned int instrument;/* instrument number */ - } number; - - char *name; /* name of this instrument or NULL */ - - unsigned int mode:8, /* see to GUS_INSTR_XXXX */ - flags:8, /* see to GUS_INSTR_F_XXXX */ - exclusion:4, /* see to GUS_INSTR_E_XXXX */ - layer:4; /* see to GUS_INSTR_L_XXXX */ - unsigned short exclusion_group; /* 0 - none, 1-65535 */ - - struct { - unsigned char effect1:4,/* use global effect if available */ - effect2:4; /* use global effect if available */ - unsigned char effect1_depth;/* 0-127 */ - unsigned char effect2_depth;/* 0-127 */ - } patch; - - union { - gus_layer_t *layer; /* first layer */ - unsigned int alias; /* pointer to instrument */ - } info; - gus_instrument_t *next; /* next instrument */ -}; - -struct _gus_layer_t { - unsigned char mode; /* see to GUS_INSTR_XXXX constants */ - - gus_wave_t *wave; - gus_layer_t *next; -}; - -/* bits for format variable in gus_wave_t */ - -#define GUS_WAVE_16BIT 0x0001 /* 16-bit wave */ -#define GUS_WAVE_UNSIGNED 0x0002 /* unsigned wave */ -#define GUS_WAVE_INVERT 0x0002 /* same as unsigned wave */ -#define GUS_WAVE_BACKWARD 0x0004 /* forward mode */ -#define GUS_WAVE_LOOP 0x0008 /* loop mode */ -#define GUS_WAVE_BIDIR 0x0010 /* bidirectional mode */ -#define GUS_WAVE_ULAW 0x0020 /* uLaw compressed wave */ -#define GUS_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */ -#define GUS_WAVE_ROM 0x0080 /* wave is in ROM */ -#define GUS_WAVE_DELTA 0x0100 - -#define GUS_WAVE_PATCH_ENVELOPE 0x01 /* envelopes on */ -#define GUS_WAVE_PATCH_SUSTAIN 0x02 /* sustain mode */ - -struct _gus_wave_t { - unsigned char mode; /* see to GUS_INSTR_XXXX constants */ - unsigned char format; /* see to GUS_WAVE_XXXX constants */ - unsigned int size; /* size of waveform in bytes */ - unsigned int start; /* start offset in bytes * 16 (lowest 4 bits - fraction) */ - unsigned int loop_start; /* bits loop start offset in bytes * 16 (lowest 4 bits - fraction) */ - unsigned int loop_end; /* loop start offset in bytes * 16 (lowest 4 bits - fraction) */ - unsigned short loop_repeat; /* loop repeat - 0 = forever */ - struct { - unsigned int memory; /* begin of waveform in GUS's memory */ - unsigned char *ptr; /* pointer to waveform in system memory */ - } begin; - - struct { - unsigned char flags; - unsigned int sample_rate; - unsigned int low_frequency;/* low frequency range for this waveform */ - unsigned int high_frequency;/* high frequency range for this waveform */ - unsigned int root_frequency;/* root frequency for this waveform */ - signed short tune; - unsigned char balance; - unsigned char envelope_rate[6]; - unsigned char envelope_offset[6]; - unsigned char tremolo_sweep; - unsigned char tremolo_rate; - unsigned char tremolo_depth; - unsigned char vibrato_sweep; - unsigned char vibrato_rate; - unsigned char vibrato_depth; - unsigned short scale_frequency; - unsigned short scale_factor;/* 0-2048 or 0-2 */ - } patch; - - gus_wave_t *next; -}; - -/* defines for gus_memory_reset () */ -#define GUS_DOWNLOAD_MODE_NORMAL 0x0000 -#define GUS_DOWNLOAD_MODE_TEST 0x0001 - -/* - A subset of libgus functions (used by MikMod Ultrasound driver) -*/ -int gus_cards(void); - /* - * return value: number of GUS cards installed in system or - * zero if driver isn't installed - */ -int gus_close(int card); - /* - * close file (gus synthesizer) previously opened with gusOpen function - * return value: zero if success - */ -int gus_do_flush(void); - /* - * return value: zero if command queue was successfully flushed - * in non block mode - number of written bytes - */ -void gus_do_tempo(unsigned int tempo); - /* - * set new tempo - */ -void gus_do_voice_frequency(unsigned char voice, unsigned int freq); - /* - * set voice frequency in Hz - */ -void gus_do_voice_pan(unsigned char voice, unsigned short pan); - /* - * set voice pan (0-16384) (full left - full right) - */ -void gus_do_voice_start(unsigned char voice, unsigned int program, - unsigned int freq, unsigned short volume, - unsigned short pan); - /* - * start voice - * voice : voice # - * program : program # or ~0 = current - * freq : frequency in Hz - * volume : volume level (0-16384) or ~0 = current - * pan : pan level (0-16384) or ~0 = current - */ -void gus_do_voice_start_position(unsigned char voice, unsigned int program, - unsigned int freq, unsigned short volume, - unsigned short pan, unsigned int position); - /* - * start voice - * voice : voice # - * program : program # or ~0 = current - * freq : frequency in Hz - * volume : volume level (0-16384) or ~0 = current - * pan : pan level (0-16384) or ~0 = current - * position : offset to wave in bytes * 16 (lowest 4 bits - fraction) - */ -void gus_do_voice_stop(unsigned char voice, unsigned char mode); - /* - * stop voice - * mode = 0 : stop voice now - * mode = 1 : disable wave loop and finish it - */ -void gus_do_voice_volume(unsigned char voice, unsigned short vol); - /* - * set voice volume level 0-16384 (linear) - */ -void gus_do_wait(unsigned int ticks); - /* - * wait x ticks - this command is block separator - * all commands between blocks are interpreted in the begining of one tick - */ -int gus_get_voice_status(int voice); - /* - * THIS IS NOT A FUNCTION OF ORIGINAL libGUS! - * Return voice status: -1 on error, 0 if voice stopped, 1 if playing - */ -int gus_get_handle(void); - /* - * return value: file handle (descriptor) for /dev/gus - */ -int gus_info(gus_info_t * info, int reread); - /* - * return value: filled info variable with actual values - * (look at gus.h header file for more informations) - * version field: 0x0024 - GUS revision 2.4 - * 0x0035 - GUS revision 3.7 with flipped mixer channels - * 0x0037 - GUS revision 3.7 - * 0x0090 - GUS ACE - * 0x00a0 - GUS MAX revision 10 - * 0x00a1 - GUS MAX revision 11 - * 0x0100 - InterWave (full version) - * flags field: see to GUS_STRU_INFO_F_???? constants (gus.h header file) - * port field: port number (for example 0x220) - * irq field: irq number (for example 11) - * dma1 field: dma1 number (for example 5) - * dma2 field: dma2 number (for example 6) - * note: dma1 and dma2 could be same in case of only one dma channel used - */ -int gus_memory_alloc(gus_instrument_t * instrument); - /* - * input value: look at gus.h for more details about gus_instrument_t structure - * return value: zero if instrument was successfully allocated - */ -int gus_memory_free(gus_instrument_t * instrument); - /* - * input value: look at gus.h for more details about gus_instrument_t structure - * return value: zero if instrument was successfully removed - */ -int gus_memory_size(void); - /* - * return value: gus memory size in bytes - */ -int gus_memory_free_size(void); - /* - * return value: unused gus memory in bytes - * warning: reset function must be called before - */ -int gus_memory_free_block(int w_16bit); - /* - * return value: current largest free block for 8-bit or 16-bit wave - */ -int gus_memory_pack(void); - /* - * return value: zero if success - */ -int gus_memory_reset(int mode); - /* - * input value: see to GUS_DOWNLOAD_MODE_XXXX constants (gus.h) - * return value: zero if samples & instruments was successfully removed - * from GF1 memory manager - */ - -int gus_open(int card, size_t queue_buffer_size, int non_block); - /* - * input values: card number, - * size of command queue buffer (512-1MB) - * buffer is allocated dynamically, - * non block mode - * return value: zero if success - * note 1: this function must be called as first - * open file /dev/gus - * note 2: you can open more cards with one process - */ -int gus_queue_flush(void); - /* - * return value: zero if command queue was successfully flushed - */ -int gus_queue_read_set_size(int items); - /* - * input value: echo buffer size in items (if 0 - erase echo buffer) - */ -int gus_queue_write_set_size(int items); - /* - * input value: write queue size in items (each item have 8 bytes) - */ -int gus_reset(int voices, unsigned int channel_voices); - /* - * input values: active voices and channel voices (for dynamic allocation) - * return value: number of active voices if reset was successfull (GF1 chip active) - */ -int gus_reset_engine_only(void); - /* - * return value: same as gus_reset function - * note: this command doesn't change number of active - * voices and doesn't do hardware reset - */ -int gus_select(int card); - /* - * select specified card - * return value: zero if success - */ -int gus_timer_start(void); - /* - * return value: zero if successfull - */ -int gus_timer_stop(void); - /* - * return value: zero if timer was stoped - */ -int gus_timer_continue(void); - /* - * return value: zero if timer will be continue - */ -int gus_timer_tempo(int ticks); - /* - * return value: zero if setup was success - */ -int gus_timer_base(int base); - /* - * return value: zero if setup was success (default timebase = 100) - */ - -void gus_convert_delta(unsigned int type, unsigned char *dest, - unsigned char *src, size_t size); - /* - * note: dest and src pointers can be equal - */ - -void gus_timer_callback(void (*timer_callback) ()); - /* - * Set a callback to be called once per tempo tick - */ - -int gus_dma_usage (int use); - /* - * Tell GUS library to use/to not use DMA for sample transfer. - * In some environments/on some hardware platforms you will need - * to disable DMA in order to function properly. You should call - * this function before opening the card. - */ - -#endif /* __LIBGUS_H__ */ - -/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dosdma.c b/libs/mikmod/drvdos/dosdma.c new file mode 100644 index 0000000..e232c30 --- /dev/null +++ b/libs/mikmod/drvdos/dosdma.c @@ -0,0 +1,214 @@ +/* + Implementation of DMA routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "dosdma.h" + +#include /* includes sys/version.h (djgpp >= 2.02) */ +#include +#include +#include +#include +#include "mikmod.h" /* for MikMod_malloc() & co */ + +/* 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 *) MikMod_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); + MikMod_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); + MikMod_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(); +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dosdma.h b/libs/mikmod/drvdos/dosdma.h new file mode 100644 index 0000000..882c9ac --- /dev/null +++ b/libs/mikmod/drvdos/dosdma.h @@ -0,0 +1,190 @@ +/* + Interface for DMA routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __DOSDMA_H__ +#define __DOSDMA_H__ + +#include + +#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__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dosgus.c b/libs/mikmod/drvdos/dosgus.c new file mode 100644 index 0000000..637b526 --- /dev/null +++ b/libs/mikmod/drvdos/dosgus.c @@ -0,0 +1,1907 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Driver for GUS cards under DOS + Written by Andrew Zabolotny + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_ULTRA + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosgus.h" +#include "mikmod.h" /* for MikMod_malloc() & co */ + +/********************************************* Private variables/routines *****/ + +/* The Gravis Ultrasound state/info */ +__gus_state gus; + +/* Try to avoid holes in DRAM less than this size */ +#define DRAM_HOLE_THRESHOLD 8192 +/* If hole is larger than that, create a free block describing it */ +#define DRAM_SPLIT_THRESHOLD 64 +/* The size of DMA buffer used for RAM->DRAM transfers */ +#define GF1_DMA_BUFFER_SIZE 8192 + +/* Debug macro: useful to change screen locations when some event occurs */ +#ifdef MIKMOD_DEBUG +# define DEBUG_PRINT(x) printf x; +# define DEBUG_OFS(addr, attr) \ + { \ + unsigned short x; \ + _dosmemgetw (0xb8780 + addr*2, 1, &x); \ + if ((x >> 8) != attr) x = '0'; \ + x = ((x + 1) & 0xff) | (attr << 8); \ + _dosmemputw (&x, 1, 0xb8780 + addr*2); \ + } +#else +# define DEBUG_PRINT(x) +# define DEBUG_OFS(addr, attr) +#endif + +static unsigned short __gus_volume_table[512] = { + 0x0000, 0x7000, 0x7ff0, 0x8800, 0x8ff0, 0x9400, 0x9800, 0x9c00, + 0x9ff0, 0xa200, 0xa400, 0xa600, 0xa800, 0xaa00, 0xac00, 0xae00, + 0xaff0, 0xb100, 0xb200, 0xb300, 0xb400, 0xb500, 0xb600, 0xb700, + 0xb800, 0xb900, 0xba00, 0xbb00, 0xbc00, 0xbd00, 0xbe00, 0xbf00, + 0xbff0, 0xc080, 0xc100, 0xc180, 0xc200, 0xc280, 0xc300, 0xc380, + 0xc400, 0xc480, 0xc500, 0xc580, 0xc600, 0xc680, 0xc700, 0xc780, + 0xc800, 0xc880, 0xc900, 0xc980, 0xca00, 0xca80, 0xcb00, 0xcb80, + 0xcc00, 0xcc80, 0xcd00, 0xcd80, 0xce00, 0xce80, 0xcf00, 0xcf80, + 0xcff0, 0xd040, 0xd080, 0xd0c0, 0xd100, 0xd140, 0xd180, 0xd1c0, + 0xd200, 0xd240, 0xd280, 0xd2c0, 0xd300, 0xd340, 0xd380, 0xd3c0, + 0xd400, 0xd440, 0xd480, 0xd4c0, 0xd500, 0xd540, 0xd580, 0xd5c0, + 0xd600, 0xd640, 0xd680, 0xd6c0, 0xd700, 0xd740, 0xd780, 0xd7c0, + 0xd800, 0xd840, 0xd880, 0xd8c0, 0xd900, 0xd940, 0xd980, 0xd9c0, + 0xda00, 0xda40, 0xda80, 0xdac0, 0xdb00, 0xdb40, 0xdb80, 0xdbc0, + 0xdc00, 0xdc40, 0xdc80, 0xdcc0, 0xdd00, 0xdd40, 0xdd80, 0xddc0, + 0xde00, 0xde40, 0xde80, 0xdec0, 0xdf00, 0xdf40, 0xdf80, 0xdfc0, + 0xdff0, 0xe020, 0xe040, 0xe060, 0xe080, 0xe0a0, 0xe0c0, 0xe0e0, + 0xe100, 0xe120, 0xe140, 0xe160, 0xe180, 0xe1a0, 0xe1c0, 0xe1e0, + 0xe200, 0xe220, 0xe240, 0xe260, 0xe280, 0xe2a0, 0xe2c0, 0xe2e0, + 0xe300, 0xe320, 0xe340, 0xe360, 0xe380, 0xe3a0, 0xe3c0, 0xe3e0, + 0xe400, 0xe420, 0xe440, 0xe460, 0xe480, 0xe4a0, 0xe4c0, 0xe4e0, + 0xe500, 0xe520, 0xe540, 0xe560, 0xe580, 0xe5a0, 0xe5c0, 0xe5e0, + 0xe600, 0xe620, 0xe640, 0xe660, 0xe680, 0xe6a0, 0xe6c0, 0xe6e0, + 0xe700, 0xe720, 0xe740, 0xe760, 0xe780, 0xe7a0, 0xe7c0, 0xe7e0, + 0xe800, 0xe820, 0xe840, 0xe860, 0xe880, 0xe8a0, 0xe8c0, 0xe8e0, + 0xe900, 0xe920, 0xe940, 0xe960, 0xe980, 0xe9a0, 0xe9c0, 0xe9e0, + 0xea00, 0xea20, 0xea40, 0xea60, 0xea80, 0xeaa0, 0xeac0, 0xeae0, + 0xeb00, 0xeb20, 0xeb40, 0xeb60, 0xeb80, 0xeba0, 0xebc0, 0xebe0, + 0xec00, 0xec20, 0xec40, 0xec60, 0xec80, 0xeca0, 0xecc0, 0xece0, + 0xed00, 0xed20, 0xed40, 0xed60, 0xed80, 0xeda0, 0xedc0, 0xede0, + 0xee00, 0xee20, 0xee40, 0xee60, 0xee80, 0xeea0, 0xeec0, 0xeee0, + 0xef00, 0xef20, 0xef40, 0xef60, 0xef80, 0xefa0, 0xefc0, 0xefe0, + 0xeff0, 0xf010, 0xf020, 0xf030, 0xf040, 0xf050, 0xf060, 0xf070, + 0xf080, 0xf090, 0xf0a0, 0xf0b0, 0xf0c0, 0xf0d0, 0xf0e0, 0xf0f0, + 0xf100, 0xf110, 0xf120, 0xf130, 0xf140, 0xf150, 0xf160, 0xf170, + 0xf180, 0xf190, 0xf1a0, 0xf1b0, 0xf1c0, 0xf1d0, 0xf1e0, 0xf1f0, + 0xf200, 0xf210, 0xf220, 0xf230, 0xf240, 0xf250, 0xf260, 0xf270, + 0xf280, 0xf290, 0xf2a0, 0xf2b0, 0xf2c0, 0xf2d0, 0xf2e0, 0xf2f0, + 0xf300, 0xf310, 0xf320, 0xf330, 0xf340, 0xf350, 0xf360, 0xf370, + 0xf380, 0xf390, 0xf3a0, 0xf3b0, 0xf3c0, 0xf3d0, 0xf3e0, 0xf3f0, + 0xf400, 0xf410, 0xf420, 0xf430, 0xf440, 0xf450, 0xf460, 0xf470, + 0xf480, 0xf490, 0xf4a0, 0xf4b0, 0xf4c0, 0xf4d0, 0xf4e0, 0xf4f0, + 0xf500, 0xf510, 0xf520, 0xf530, 0xf540, 0xf550, 0xf560, 0xf570, + 0xf580, 0xf590, 0xf5a0, 0xf5b0, 0xf5c0, 0xf5d0, 0xf5e0, 0xf5f0, + 0xf600, 0xf610, 0xf620, 0xf630, 0xf640, 0xf650, 0xf660, 0xf670, + 0xf680, 0xf690, 0xf6a0, 0xf6b0, 0xf6c0, 0xf6d0, 0xf6e0, 0xf6f0, + 0xf700, 0xf710, 0xf720, 0xf730, 0xf740, 0xf750, 0xf760, 0xf770, + 0xf780, 0xf790, 0xf7a0, 0xf7b0, 0xf7c0, 0xf7d0, 0xf7e0, 0xf7f0, + 0xf800, 0xf810, 0xf820, 0xf830, 0xf840, 0xf850, 0xf860, 0xf870, + 0xf880, 0xf890, 0xf8a0, 0xf8b0, 0xf8c0, 0xf8d0, 0xf8e0, 0xf8f0, + 0xf900, 0xf910, 0xf920, 0xf930, 0xf940, 0xf950, 0xf960, 0xf970, + 0xf980, 0xf990, 0xf9a0, 0xf9b0, 0xf9c0, 0xf9d0, 0xf9e0, 0xf9f0, + 0xfa00, 0xfa10, 0xfa20, 0xfa30, 0xfa40, 0xfa50, 0xfa60, 0xfa70, + 0xfa80, 0xfa90, 0xfaa0, 0xfab0, 0xfac0, 0xfad0, 0xfae0, 0xfaf0, + 0xfb00, 0xfb10, 0xfb20, 0xfb30, 0xfb40, 0xfb50, 0xfb60, 0xfb70, + 0xfb80, 0xfb90, 0xfba0, 0xfbb0, 0xfbc0, 0xfbd0, 0xfbe0, 0xfbf0, + 0xfc00, 0xfc10, 0xfc20, 0xfc30, 0xfc40, 0xfc50, 0xfc60, 0xfc70, + 0xfc80, 0xfc90, 0xfca0, 0xfcb0, 0xfcc0, 0xfcd0, 0xfce0, 0xfcf0, + 0xfd00, 0xfd10, 0xfd20, 0xfd30, 0xfd40, 0xfd50, 0xfd60, 0xfd70, + 0xfd80, 0xfd90, 0xfda0, 0xfdb0, 0xfdc0, 0xfdd0, 0xfde0, 0xfdf0, + 0xfe00, 0xfe10, 0xfe20, 0xfe30, 0xfe40, 0xfe50, 0xfe60, 0xfe70, + 0xfe80, 0xfe90, 0xfea0, 0xfeb0, 0xfec0, 0xfed0, 0xfee0, 0xfef0, + 0xff00, 0xff10, 0xff20, 0xff30, 0xff40, 0xff50, 0xff60, 0xff70, + 0xff80, 0xff90, 0xffa0, 0xffb0, 0xffc0, 0xffd0, 0xffe0, 0xfff0 +}; + +/* Wait a bit for GUS before doing something + * Mark function as volatile: don't allow it to be inlined. + * It *should* be slow, no need to make it work faster :-) + */ +#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 __gus_delay() +{ + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); +} + +static void __gus_stop_controller(unsigned char gf1reg) +{ + register unsigned char value = __gus_inregb(gf1reg); + __gus_outregb(gf1reg, (value | GF1VC_STOPPED | GF1VC_STOP) & + ~(GF1VC_IRQ_PENDING | GF1VC_IRQ)); +} + +/* Returns 1 if volume is already at given position */ +static boolean __gus_volume_ramp_to(unsigned short volume, + unsigned char rate, + unsigned char vol_ctrl) +{ + int svol = __gus_inregw(GF1R_VOLUME) & 0xfff0; + int evol = volume; + + /* First of all, disable volume ramp */ + __gus_stop_controller(GF1R_VOLUME_CONTROL); + + /* If voice is stopped, set the volume to zero and return */ + if (__gus_inregb(GF1R_VOICE_CONTROL) & GF1VC_STOPPED) { + __gus_outregw(GF1R_VOLUME, 0); + return 1; + } + + /* Avoid clicks when volume ramp goes too high or too low */ + if (svol < 0x0400) + svol = 0x0400; + if (svol > 0xfc00) + svol = 0xfc00; + if (evol < 0x0400) + evol = 0x0400; + if (evol > 0xfc00) + evol = 0xfc00; + + /* Adjust start/end positions */ + if (svol > evol) { + unsigned short tmp = evol; + evol = svol; + svol = tmp; + vol_ctrl |= GF1VL_BACKWARD; + } + + /* If we already are (near) the target volume, quit */ + if (evol - svol < 0x1000) { + __gus_outregw(GF1R_VOLUME, volume); + return 1; + } + + __gus_outregb(GF1R_VOLUME_START, svol >> 8); + __gus_outregb(GF1R_VOLUME_END, evol >> 8); + __gus_outregb(GF1R_VOLUME_RATE, rate); + __gus_outregb_slow(GF1R_VOLUME_CONTROL, vol_ctrl); + return 0; +} + +static inline void __gus_stop_voice() +{ + __gus_stop_controller(GF1R_VOICE_CONTROL); + __gus_outregb_slow(GF1R_VOICE_CONTROL, GF1VC_STOPPED | GF1VC_STOP); +} + +/* The GUS IRQ handler */ +static void gf1_irq() +{ + unsigned char irq_source; /* The contents of GF1_IRQ_STATUS register */ + boolean timer_cb = 0; /* Call timer callback function */ + + DEBUG_OFS(0, 0xCE) + gus.eow_ignore = 0; + while ((irq_source = inportb(GF1_IRQ_STATUS))) { + DEBUG_OFS(1, 0xCE) + + if (irq_source & GF1M_IRQ_DMA_COMPLETE) { + DEBUG_OFS(4, 0x9F) + /* reset the IRQ pending bit */ + __gus_inregb(GF1R_DMA_CONTROL); + gus.dma_active = 0; + + if (gus.dma_callback) + gus.dma_callback(); + } + + if (irq_source & (GF1M_IRQ_WAVETABLE | GF1M_IRQ_ENVELOPE)) { + unsigned char vcirq; + unsigned int done_mask = 0; + + /* IRQ bits are inverse (i.e. 0 = IRQ pending) */ + while ((vcirq = __gus_inregb(GF1R_IRQ_SOURCE) ^ + (GF1IRQ_WAVE | GF1IRQ_VOLUME)) & + (GF1IRQ_WAVE | GF1IRQ_VOLUME)) { + unsigned long voice = (vcirq & 0x1f); + unsigned char voice_ctl, volume_ctl; + unsigned int voice_mask = (1 << voice); + + /* Don't handle more than one IRQ from same voice */ + if (done_mask & voice_mask) + continue; + + done_mask |= voice_mask; + + /* Read voice/volume selection registers */ + __gus_select_voice(voice); + voice_ctl = __gus_inregb(GF1R_VOICE_CONTROL); + volume_ctl = __gus_inregb(GF1R_VOLUME_CONTROL); + + if ((vcirq & GF1IRQ_WAVE) && (gus.wt_callback) + && !(gus.eow_ignore & voice_mask)) { + DEBUG_OFS(5, 0xAF) + gus.wt_callback(voice, voice_ctl, volume_ctl); + } + + if ((vcirq & GF1IRQ_VOLUME) && (gus.vl_callback)) { + DEBUG_OFS(6, 0xAF) + gus.vl_callback(voice, voice_ctl, volume_ctl); + } + } + } + + /* Reset timers that sent this IRQ */ + if (irq_source & (GF1M_IRQ_TIMER1 | GF1M_IRQ_TIMER2)) { + unsigned char timer_ctl = gus.timer_ctl_reg; + + if (irq_source & GF1M_IRQ_TIMER1) + timer_ctl &= ~GF1M_TIMER1; + + if (irq_source & GF1M_IRQ_TIMER2) + timer_ctl &= ~GF1M_TIMER2; + + __gus_outregb_slow(GF1R_TIMER_CONTROL, timer_ctl); + __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); + } + + if (irq_source & GF1M_IRQ_TIMER1) + if (--gus.t1_countdown == 0) { + gus.t1_countdown = gus.t1_multiple; + gus.t1_ticks++; + + DEBUG_OFS(2, 0xCF) + + if (gus.t1_callback) { + timer_cb = 1; + gus.t1_callback(); + } + } + + if (irq_source & GF1M_IRQ_TIMER2) + if (--gus.t2_countdown == 0) { + gus.t2_countdown = gus.t2_multiple; + gus.t2_ticks++; + + DEBUG_OFS(3, 0xCF) + + if (gus.t2_callback) + gus.t2_callback(); + } +#if 0 + /* The following are not used and implemented yet */ + if (irq_source & (GF1M_IRQ_MIDI_TX | GF1M_IRQ_MIDI_RX)) { + } +#endif + } + + irq_ack(gus.gf1_irq); + + if (timer_cb && gus.timer_callback) + gus.timer_callback(); +} + +static void gf1_irq_end() +{ +} + +static boolean __gus_detect() +{ + /* A relatively relaxed autodetection; + We don't count on DRAM: GUS PnP could not have it + (although its anyway bad for us) + */ + __gus_select_voice(0); + __gus_stop_voice(); + __gus_outregw(GF1R_FREQUENCY, 0x1234); + __gus_outregw(GF1R_VOLUME, 0x5670); + return ((__gus_inregw(GF1R_FREQUENCY) & 0xfffe) == 0x1234) + && ((__gus_inregw(GF1R_VOLUME) & 0xfff0) == 0x5670); +} + +static void __gus_reset(boolean reset_io_dma) +{ + static unsigned char irqctl[16] = { 0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7 }; + static unsigned char dmactl[8] = { 0, 1, 0, 2, 0, 3, 4, 5 }; + unsigned char irqtmp, dmatmp; + + /* Disable interrupts while resetting to avoid spurious IRQs */ + int i, timer, old_ints = disable(); + + /* Stop the timer so that GUS IRQ won't clobber registers */ + timer = (gus.timer_ctl_reg & GF1M_TIMER1); + if (timer) + gus_timer_stop(); + + gus.dma_active = 0; + + __gus_outregb(GF1R_RESET, 0); + for (i = 0; i < 10; i++) + __gus_delay(); + __gus_outregb(GF1R_RESET, GF1M_MASTER_RESET); + for (i = 0; i < 10; i++) + __gus_delay(); + + outportb(GF1_MIDI_CTRL, GF1M_MIDI_RESET); + for (i = 0; i < 10; i++) + __gus_delay(); + outportb(GF1_MIDI_CTRL, 0); + + /* Reset all IRQ sources */ + __gus_outregb(GF1R_DMA_CONTROL, 0); + __gus_outregb(GF1R_TIMER_CONTROL, 0); + __gus_outregb(GF1R_SAMPLE_CONTROL, 0); + + /* Reset all voices */ + gus_reset(gus.voices, gus.dynmask); + + /* Flush any pending IRQs */ + inportb(GF1_IRQ_STATUS); + __gus_inregb(GF1R_DMA_CONTROL); + __gus_inregb(GF1R_SAMPLE_CONTROL); + __gus_inregb(GF1R_IRQ_SOURCE); + + if (reset_io_dma) { + /* Now set up the GUS card to required IRQs and DMAs */ + if (gus.irq[0] == gus.irq[1]) + irqtmp = irqctl[gus.irq[0]] | GF1M_IRQ_EQUAL; + else + irqtmp = irqctl[gus.irq[0]] | (irqctl[gus.irq[1]] << 3); + + if (gus.dma[0] == gus.dma[1]) + dmatmp = dmactl[gus.dma[0]] | GF1M_DMA_EQUAL; + else + dmatmp = dmactl[gus.dma[0]] | (dmactl[gus.dma[1]] << 3); + + /* Reset IRQs if possible */ + gus.mixer = + GF1M_MIXER_NO_LINE_IN | GF1M_MIXER_NO_OUTPUT | GF1M_MIXER_GF1_IRQ; + if (gus.version >= GUS_CARD_VERSION_CLASSIC1) { + outportb(GF1_REG_CTRL, 0x05); + outportb(GF1_MIX_CTRL, gus.mixer); + outportb(GF1_IRQ_CTRL, 0x00); /* Reset IRQs */ + outportb(GF1_REG_CTRL, 0x00); + } + + /* Set up DMA channels: NEVER disable MIXER_GF1_IRQ in the future */ + outportb(GF1_MIX_CTRL, gus.mixer); + outportb(GF1_IRQ_CTRL, dmatmp); + + /* Set up IRQ channels */ + outportb(GF1_MIX_CTRL, gus.mixer | GF1M_CONTROL_SELECT); + outportb(GF1_IRQ_CTRL, irqtmp); + } + + __gus_outregb(GF1R_RESET, GF1M_MASTER_RESET | GF1M_OUTPUT_ENABLE | GF1M_MASTER_IRQ); + __gus_delay(); + + /* Flush IRQs again */ + inportb(GF1_IRQ_STATUS); + __gus_inregb(GF1R_DMA_CONTROL); + __gus_inregb(GF1R_SAMPLE_CONTROL); + __gus_inregb(GF1R_IRQ_SOURCE); + + _irq_ack(gus.irq[0]); + _irq_ack(gus.irq[1]); + + if (timer) + gus_timer_continue(); + + if (old_ints) + enable(); + + /* Enable output */ + __gus_mixer_output(1); +} + +/* Transfer a block of data from GUS DRAM to main RAM through port I/O */ +static void __gus_transfer_io_in(unsigned long address, unsigned char *source, + unsigned long size) +{ + while (size) { + register unsigned int size64k; + + size64k = 0x10000 - (address & 0xffff); + if (size64k > size) + size64k = size; + size -= size64k; + + __gus_outregb(GF1R_DRAM_HIGH, address >> 16); + while (size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + *source++ = inportb(GF1_DRAM); + } + } +} + +/* Transfer a block of data into GUS DRAM through port I/O */ +static void __gus_transfer_io(unsigned long address, unsigned char *source, + unsigned long size, int flags) +{ + while (size) { + register unsigned int size64k; + + size64k = 0x10000 - (address & 0xffff); + if (size64k > size) + size64k = size; + size -= size64k; + + __gus_outregb(GF1R_DRAM_HIGH, address >> 16); + if (flags & GUS_WAVE_INVERT) + if (flags & GUS_WAVE_16BIT) + while (size64k-- && size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, *source++); + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, (*source++) ^ 0x80); + } else + while (size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, (*source++) ^ 0x80); + } else + while (size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, *source++); + } + } +} + +/* Wait for DMA transfer to finish between 8-9 1/18sec timer ticks */ +static int __gus_wait_dma() +{ + unsigned long timer; + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + while (gus.dma_active) + if (_farnspeekl(0x46c) - timer > 8) { + /* Force DMA abort since something went wrong */ + __gus_reset(0); + return -1; + } + + return 0; +} + +/* Transfer a block of data into GUS DRAM through DMA controller */ +static void __gus_transfer_dma(unsigned long address, unsigned char *source, + unsigned long size, int flags) +{ + unsigned char dma_control; + unsigned long bytes_left; + unsigned long cur_size; + unsigned long dest_addr; + + if ((gus.dma[0] > 3) || (flags & GUS_WAVE_16BIT)) + size = (size + 1) & ~1; + + bytes_left = size; + while (bytes_left) { + __gus_wait_dma(); + + cur_size = gus.dma_buff->size; + if (cur_size > bytes_left) + cur_size = bytes_left; + bytes_left -= cur_size; + dest_addr = address; + + if (gus.dma_buff->linear != source) + memmove(gus.dma_buff->linear, source, cur_size); + source += cur_size; + address += cur_size; + + /* Disable GUS -> DMA tie */ + __gus_outregb(GF1R_DMA_CONTROL, 0); + __gus_delay(); + + /* Set up the DMA */ + dma_start(gus.dma_buff, cur_size, DMA_MODE_WRITE); + gus.dma_active = 1; + + /* Reset the DMA IRQ pending bit if set */ + __gus_inregb(GF1R_DMA_CONTROL); + + /* The 16-bit DMA channels needs a slightly different approach */ + dma_control = GF1M_DMAR_ENABLE | GF1M_DMAR_IRQ_ENABLE | gus.dma_rate; + if (gus.dma[0] > 3) { + dest_addr = __gus_convert_addr16(dest_addr); + dma_control |= GF1M_DMAR_CHAN16; + } + + __gus_outregw(GF1R_DMA_ADDRESS, dest_addr >> 4); + + if (flags & GUS_WAVE_16BIT) + dma_control |= GF1M_DMAR_DATA16; + if (flags & GUS_WAVE_INVERT) + dma_control |= GF1M_DMAR_TOGGLE_SIGN; + + /* Tell GUS to start transfer */ + __gus_outregb(GF1R_DMA_CONTROL, dma_control); + } +} + +static void __gus_detect_version() +{ + unsigned char tmp; + + switch (gus.version = inportb(GF1_REVISION)) { + case 5: + gus.version = GUS_CARD_VERSION_CLASSIC_ICS; + gus.ics = 1; + gus.ics_flipped = 1; + break; + case 6: + case 7: + case 8: + case 9: + gus.version = GUS_CARD_VERSION_CLASSIC_ICS; + gus.ics = 1; + break; + case 10: + gus.version = GUS_CARD_VERSION_MAX; + gus.codec = 1; + break; + case 11: + gus.version = GUS_CARD_VERSION_MAX1; + gus.codec = 1; + break; + case 0x30: + gus.version = GUS_CARD_VERSION_ACE; + break; + case 0x50: + gus.version = GUS_CARD_VERSION_EXTREME; + break; + case 0xff: + /* Pre-3.7 board */ + outportb(GF1_REG_CTRL, 0x20); + tmp = inportb(GF1_REG_CTRL); + if ((tmp != 0xff) && (tmp & 0x06)) + gus.version = GUS_CARD_VERSION_CLASSIC1; + else + gus.version = GUS_CARD_VERSION_CLASSIC; + break; + default: + /* Hmm... unknown revision. Assume a safe Classic model */ +#ifdef MIKMOD_DEBUG + fprintf(stderr, "libgus: Unknown board revision (%02x)\n", + gus.version); +#endif + gus.version = GUS_CARD_VERSION_CLASSIC; + break; + } +} + +static void __gus_detect_transfer() +{ + unsigned char *outbuff, *inbuff; + unsigned int i, j, seed = 0x13243546; + __gus_transfer_func func; + +#define TRANSFER_SIZE 0x4000 + + outbuff = (unsigned char *) MikMod_malloc(TRANSFER_SIZE); + inbuff = (unsigned char *) MikMod_malloc(TRANSFER_SIZE); + + /* Suppose we have an malfunctioning GUS */ + gus.transfer = NULL; + + for (i = (gus.dma_buff ? 0 : 4); i <= 4; i++) { + switch (i) { + case 0: + gus.dma_rate = GF1M_DMAR_RATE0; + func = __gus_transfer_dma; + break; + case 1: + gus.dma_rate = GF1M_DMAR_RATE1; + func = __gus_transfer_dma; + break; + case 2: + gus.dma_rate = GF1M_DMAR_RATE2; + func = __gus_transfer_dma; + break; + case 3: + gus.dma_rate = GF1M_DMAR_RATE3; + func = __gus_transfer_dma; + break; + case 4: + func = __gus_transfer_io; + break; + } + + /* Fill data array each time with pseudo-random values */ + for (j = 0; j < TRANSFER_SIZE; j++) + outbuff[j] = seed, seed = + ((seed + 358979323) ^ (seed >> 16)) * 314159265; + + /* Transfer the random array to GUS */ + /* Poke a security fence around dest block */ + __gus_poke(0x100 - 1, 0xAA); + __gus_poke(0x100 - 2, 0x55); + __gus_poke(0x100 + TRANSFER_SIZE + 0, 0xAA); + __gus_poke(0x100 + TRANSFER_SIZE + 1, 0x55); + + func(0x100, outbuff, TRANSFER_SIZE, 0); + + if (__gus_wait_dma() == 0) { + /* Check if the security fence was not damaged */ + if ((__gus_peek(0x100 - 1) != 0xAA) + || (__gus_peek(0x100 - 2) != 0x55) + || (__gus_peek(0x100 + TRANSFER_SIZE + 0) != 0xAA) + || (__gus_peek(0x100 + TRANSFER_SIZE + 1) != 0x55)) + continue; + + /* Now check if GUS DRAM really data that we expects to be transferred */ + __gus_transfer_io_in(0x100, inbuff, TRANSFER_SIZE); + if (memcmp(outbuff, inbuff, TRANSFER_SIZE) == 0) { + gus.transfer = func; + break; + } + } + } + +#undef TRANSFER_SIZE + + MikMod_free(inbuff); + MikMod_free(outbuff); +} + +static void __gus_detect_memory() +{ + unsigned int size; + for (size = 0; size < 1024; size += 256) { + __gus_poke(size * 1024, 0xaa); + if (__gus_peek(size * 1024) != 0xaa) + break; + __gus_poke(size * 1024, 0x55); + if (__gus_peek(size * 1024) != 0x55) + break; + } + gus.ram = size; +} + +static void __gus_init() +{ + char *gusenv = getenv("ULTRASND"); + + memset((void *)&gus, 0, sizeof(gus)); + gus.cmd_voice = -1; + + if (!gusenv) + return; + + sscanf(gusenv, "%x,%d,%d,%d,%d", &gus.port, &gus.dma[0], &gus.dma[1], + &gus.irq[0], &gus.irq[1]); + + /* A relaxed sanity check */ + if ((gus.port < 0x100) || (gus.port > 0x1000) + || (gus.irq[0] < 2) || (gus.irq[0] > 15) + || (gus.irq[1] < 2) || (gus.irq[1] > 15) + || (gus.dma[0] < 0) || (gus.dma[0] > 7) + || (gus.dma[1] < 0) || (gus.dma[1] > 7)) + return; + + gus.voices = 32; + gus.timer_ctl = GF1M_MASK_TIMER1 | GF1M_MASK_TIMER2; + + /* Detect if the card is really there */ + if (__gus_detect() == 0) + return; + + /* Detect the version of Gravis Ultrasound */ + __gus_detect_version(); + + /* Reset the card */ + __gus_reset(1); + + /* Detect the amount of on-board memory */ + __gus_detect_memory(); + + gus.ok = 1; +} + +static void __gus_kick(gus_wave_t * wave, unsigned int wave_offset) +{ + unsigned char vc; + + vc = GF1VC_IRQ; + if (wave->format & GUS_WAVE_16BIT) + vc |= GF1VC_DATA16; + if (wave->format & GUS_WAVE_BACKWARD) + vc |= GF1VC_BACKWARD; + if (wave->format & GUS_WAVE_LOOP) { + vc |= GF1VC_LOOP_ENABLE; + if (wave->format & GUS_WAVE_BIDIR) + vc |= GF1VC_BI_LOOP; + } + __gus_set_loop_start(vc, (wave->begin.memory << 4) + wave->loop_start); + if (wave->format & GUS_WAVE_LOOP) + __gus_set_loop_end(vc, (wave->begin.memory << 4) + wave->loop_end); + else + __gus_set_loop_end(vc, (wave->begin.memory + wave->size) << 4); + __gus_set_current(vc, (wave->begin.memory << 4) + wave_offset + 100); + __gus_outregb_slow(GF1R_VOICE_CONTROL, vc); +} + +/* Timer 1 callback function (updates voices) */ +static void __gus_timer_update() +{ + gus_wave_t *wave; + unsigned long wave_offset; + unsigned char *src, *top; + unsigned int vmask = (1 << gus.cur_voice); + + if (!gus.cmd_pool_ready) + return; + + __gus_select_voice(gus.cur_voice); + wave_offset = 0; + src = gus.cmd_pool; + top = gus.cmd_pool + gus.cmd_pool_top; + +#define GET_B *src +#define GET_W *((unsigned short *)src) +#define GET_L *((unsigned long *)src) + + while (src < top) { + __gus_delay(); + switch (GET_B++) { + case PCMD_VOICE: + __gus_select_voice(gus.cur_voice = GET_B++); + vmask = (1 << gus.cur_voice); + break; + case PCMD_FREQ: + /* __gus_outregw(GF1R_FREQUENCY, GET_W++);*/ + __gus_outregw(GF1R_FREQUENCY, *(unsigned short *)src); + src += 2; + break; + case PCMD_PAN: + __gus_outregb(GF1R_BALANCE, GET_B++); + break; + case PCMD_VOLUME: + __gus_volume_ramp_to(gus.cur_vol[gus.cur_voice] = + /* GET_W++, GUS_VOLCHANGE_RAMP, GF1VL_IRQ);*/ + *(unsigned short *)src, GUS_VOLCHANGE_RAMP, GF1VL_IRQ); + src += 2; + break; + case PCMD_VOLUME_PREPARE: + /* gus.cur_vol[gus.cur_voice] = GET_W++;*/ + gus.cur_vol[gus.cur_voice] = *(unsigned short *)src; + src += 2; + break; + case PCMD_OFFSET: + /* wave_offset = GET_L++;*/ + wave_offset = *(unsigned long *)src; + src += 4; + break; + case PCMD_START: + /* wave = (gus_wave_t *) GET_L++;*/ + wave = (gus_wave_t *) *(unsigned long *)src; + src += 4; + gus.cur_wave[gus.cur_voice] = wave; + gus.kick_offs[gus.cur_voice] = wave_offset; + if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) { + __gus_kick(wave, wave_offset); + __gus_volume_ramp_to(gus.cur_vol[gus.cur_voice], + GUS_VOLCHANGE_RAMP, GF1VL_IRQ); + } else + gus.voice_kick[gus.cur_voice] = 1; + wave_offset = 0; + gus.eow_ignore |= vmask; + break; + case PCMD_STOP: + /* If volume is close to nothing, abort immediately instead of + ramping */ + gus.cur_vol[gus.cur_voice] = 0; + gus.cur_wave[gus.cur_voice] = NULL; + if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) + __gus_stop_voice(); + break; + case PCMD_STOP_LOOP: + __gus_outregb_slow(GF1R_VOICE_CONTROL, + (__gus_inregb(GF1R_VOICE_CONTROL) | GF1VC_IRQ) + & ~GF1VC_LOOP_ENABLE); + __gus_outregb_slow(GF1R_VOLUME_CONTROL, + __gus_inregb(GF1R_VOLUME_CONTROL) & + ~GF1VL_ROLLOVER); + break; + default: + /* Alarm! Break out immediately */ + src = top; + break; + } + } + +#undef GET_B +#undef GET_W +#undef GET_L + + gus.cmd_pool_ready = 0; + gus.cmd_pool_top = 0; +} + +static void __gus_wavetable_update(unsigned int voice, unsigned int voice_ctl, + unsigned int volume_ctl) +{ + gus_wave_t *wave = gus.cur_wave[voice]; + + if (!wave || !(wave->format & GUS_WAVE_LOOP)) { + __gus_stop_voice(); + gus.cur_wave[voice] = NULL; + gus.cur_vol[voice] = 0; + if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) + __gus_stop_voice(); + } +} + +static void __gus_volume_update(unsigned int voice, unsigned int voice_ctl, + unsigned int volume_ctl) +{ + __gus_volume_ramp_to(gus.cur_vol[voice], GUS_VOLCHANGE_RAMP, GF1VL_IRQ); + if (!gus.cur_wave[voice]) + __gus_stop_voice(); + else if (gus.voice_kick[voice]) + __gus_kick(gus.cur_wave[voice], gus.kick_offs[voice]); + gus.voice_kick[voice] = 0; +} + +/***************************************************** GUS memory manager *****/ + +/* Mark all GUS memory as available */ +static void __gus_mem_clear() +{ + __gus_mcb *cur = gus.mcb; + + while (cur) { + __gus_mcb *next = cur->next; + if (cur != gus.mcb) + MikMod_free(cur); + cur = next; + } + + if (!gus.mcb) + gus.mcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); + + gus.mcb->next = gus.mcb->prev = NULL; + gus.mcb->addr = 0; + gus.mcb->size = gus.ram * 1024; + gus.mcb->free = 1; +} + +/* Return amount of free memory */ +static unsigned int __gus_mem_get_free() +{ + __gus_mcb *cur = gus.mcb; + unsigned int size = 0; + + if (!gus.open) + return gus.ram * 1024; + + while (cur) { + if (cur->free) + size += cur->size; + cur = cur->next; + } + + return size; +} + +/* Return largest size for a 8-bit sample */ +static unsigned int __gus_mem_get_free_8() +{ + __gus_mcb *cur = gus.mcb; + unsigned int size = 0; + + if (!gus.open) + return 0; + + while (cur) { + if (cur->free && (cur->size > size)) + size = cur->size; + cur = cur->next; + } + + return size; +} + +/* Return largest size for a 16-bit sample */ +static unsigned int __gus_mem_get_free_16() +{ + __gus_mcb *cur = gus.mcb; + unsigned int size = 0; + + if (!gus.open) + return 0; + + while (cur) { + if (cur->free) { + unsigned int size16 = cur->size; + unsigned int tmp; + /* 16-bit samples cannot cross 256K boundaries */ + tmp = 0x40000 - (cur->addr & 0x3ffff); + if (size16 > tmp) + size16 = tmp; + /* 16-bit samples should be aligned on a 32-byte boundary */ + size16 -= (32 - cur->addr) & 0x1f; + + if (size16 > size) + size = size16; + + /* Now try vice versa: skip a portion of aligned memory */ + size16 = + (cur->addr + cur->size) - ((cur->addr + 0x3ffff) & ~0x3ffff); + if ((size16 < 0x7fffffff) && (size16 > size)) + size = size16; + } + cur = cur->next; + } + + return size; +} + +/* Allocate a segment of GUS DRAM for a sample with given bits per sample. + * The algorithm tries to find the smallest free block that fits requested + * size; but if found free block is larger by some (large) delta than + * requested block size, the largest possible block is preffered. + */ +static unsigned int __gus_mem_alloc(unsigned int size, int bits16) +{ + __gus_mcb *cur = gus.mcb; + __gus_mcb *best_max = NULL, *best_min = NULL; + unsigned int best_max_delta = 0, best_min_delta = 0xffffffff; + unsigned int best_max_prefix = 0, best_min_prefix = 0; + unsigned int memaddr, memsize; + + if (!gus.open || !size || (bits16 && size > 0x40000)) + return -1; + + /* Round block size up to nearest acceptable DMA bound */ + if (bits16) + size = (size + 0x1f) & ~0x1f; + else + size = (size + 0x0f) & ~0x0f; + + while (cur) { + if (cur->free) { + unsigned char fits = 0; + + memsize = cur->size; + memaddr = cur->addr; + + if (bits16) { + /* 16-bit samples cannot cross 256K boundaries */ + unsigned int tmp = 256 * 1024 - (memaddr & 0x3ffff); + if (memsize > tmp) + memsize = tmp; + /* 16-bit samples should be aligned on a 32-byte boundary */ + memsize -= (32 - memaddr) & 0x1f; + memaddr = (memaddr + 0x1f) & ~0x1f; + } + + /* If block fits, analyze it */ + if (size <= memsize) + fits = 1; + /* Look if we still can complete the request by creating a free + block */ + else if (size <= cur->size) { + /* Align start address to next 256k boundary */ + unsigned int endaddr = cur->addr + cur->size; + memaddr = (cur->addr + 0x3ffff) & ~0x3ffff; + /* Can we split current block by inserting a free block at the + beginning? */ + if ((memaddr < endaddr) && (memaddr + size <= endaddr)) + fits = 1; + } + + if (fits) { + unsigned int size_delta = cur->size - size; + unsigned int size_prefix = memaddr - cur->addr; + if (size_delta < best_min_delta) + best_min = cur, best_min_delta = + size_delta, best_min_prefix = size_prefix; + if (size_delta > best_max_delta) + best_max = cur, best_max_delta = + size_delta, best_max_prefix = size_prefix; + } + } + + cur = cur->next; + } + + if (!best_min) + return -1; + + /* If minimal block that fits is too large, use largest block that fits */ + /* But if using the maximal block is going to create a small hole, forget + it */ + if ((best_max_prefix == 0) + || (best_max_prefix >= DRAM_HOLE_THRESHOLD) + || (best_min_prefix != 0)) + if ( + ((best_min_delta < DRAM_HOLE_THRESHOLD) && + (best_max_delta >= DRAM_HOLE_THRESHOLD)) || + ((best_min_prefix > 0) && (best_min_prefix < DRAM_HOLE_THRESHOLD) + && ((best_max_prefix == 0) || + (best_max_prefix > best_min_prefix))) || + ((best_min_prefix != 0) && (best_max_prefix == 0))) { + best_min = best_max; + best_min_delta = best_max_delta; + best_min_prefix = best_max_prefix; + } + + /* Compute the DRAM address to return */ + memaddr = best_min->addr + best_min_prefix; + if (bits16) + memaddr = (memaddr + 0x1f) & ~0x1f; + else + memaddr = (memaddr + 0x0f) & ~0x0f; + + /* If we have a considerable hole at the beginning of sample, + create a free node describing the hole */ + if (memaddr - best_min->addr >= DRAM_SPLIT_THRESHOLD) { + __gus_mcb *newmcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); + newmcb->prev = best_min->prev; + newmcb->next = best_min; + newmcb->addr = best_min->addr; + newmcb->size = memaddr - best_min->addr; + newmcb->free = 1; + best_min->addr = memaddr; + best_min->size -= newmcb->size; + best_min->prev = newmcb; + if (newmcb->prev) + newmcb->prev->next = newmcb; + } + + /* Compute the size of hole at the end of block */ + memsize = (best_min->addr + best_min->size) - (memaddr + size); + + /* Split the block if the block is larger than requested amount */ + if (memsize > DRAM_SPLIT_THRESHOLD) { + /* The next node cannot be free since free blocks are always glued + together */ + __gus_mcb *newmcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); + best_min->size -= memsize; + newmcb->prev = best_min; + newmcb->next = best_min->next; + newmcb->addr = best_min->addr + best_min->size; + newmcb->size = memsize; + newmcb->free = 1; + if (best_min->next) + best_min->next->prev = newmcb; + best_min->next = newmcb; + } + best_min->free = 0; + + return memaddr; +} + +static void __gus_mem_free(unsigned int addr) +{ + __gus_mcb *cur = gus.mcb; + while (cur) { + if (!cur->free && (cur->addr <= addr) && + (cur->addr + cur->size > addr)) { + cur->free = 1; + + /* If next block is free as well, link them together */ + if (cur->next && cur->next->free) { + __gus_mcb *next = cur->next; + cur->size += next->size; + cur->next = next->next; + if (next->next) + next->next->prev = cur; + MikMod_free(next); + } + + /* If previous block is free, link current block with it */ + if (cur->prev && cur->prev->free) { + cur->prev->size += cur->size; + cur->prev->next = cur->next; + if (cur->next) + cur->next->prev = cur->prev; + MikMod_free(cur); + } + return; + } + cur = cur->next; + } +} + +static void __gus_mem_pack() +{ +} + +#ifdef MIKMOD_DEBUG + +/* Debug dump of GUS DRAM heap */ +void __gus_mem_dump() +{ + __gus_mcb *cur = gus.mcb; + fprintf(stderr, "/-- Offset --+-- Prev --+-- Size --+-- Free --\\\n"); + while (cur) { + fprintf(stderr, "| %08X | %08X | %6d | %s |\n", + cur->addr, cur->prev ? cur->prev->addr : -1, cur->size, + cur->free ? "yes" : " no"); + cur = cur->next; + } + fprintf(stderr, "\\------------+----------+----------+----------/\n"); +} + +#endif + +/************************************************** Middle-level routines *****/ + +static int __gus_instrument_free(gus_instrument_t * instrument) +{ + gus_instrument_t **cur_instr; + gus_layer_t *cur_layer; + gus_wave_t *cur_wave, *wave_head; + + /* Remove the instrument from the list of registered instruments */ + cur_instr = (gus_instrument_t **) & gus.instr; + while (*cur_instr) { + if (*cur_instr == instrument) { + *cur_instr = instrument->next; + goto instr_loaded; + } + cur_instr = &(*cur_instr)->next; + } + return -1; + +instr_loaded: + wave_head = NULL; + for (cur_layer = instrument->info.layer; cur_layer; + cur_layer = cur_layer->next) + /* Free all waves */ + for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) { + if (!wave_head) + wave_head = cur_wave; + if (cur_wave->begin.memory != (unsigned int)-1) + __gus_mem_free(cur_wave->begin.memory); + } + if (wave_head) + MikMod_free(wave_head); + + MikMod_free(instrument->info.layer); + if (instrument->name) + MikMod_free(instrument->name); + MikMod_free(instrument); + return 0; +} + +static gus_instrument_t *__gus_instrument_get(int program) +{ + gus_instrument_t *cur_instr = (gus_instrument_t *) gus.instr; + while (cur_instr) { + if (cur_instr->number.instrument == program) + return cur_instr; + cur_instr = cur_instr->next; + } + return NULL; +} + +static gus_instrument_t *__gus_instrument_copy(gus_instrument_t * instrument) +{ + gus_instrument_t **cur_instr, *instr; + gus_layer_t *cur_layer, *dest_layer; + gus_wave_t *cur_wave, *dest_wave; + unsigned int waves, layers; + + if (!instrument || !instrument->info.layer || !gus.open) + return NULL; + + if (__gus_instrument_get(instrument->number.instrument)) + return NULL; + + instr = (gus_instrument_t *) MikMod_malloc(sizeof(gus_instrument_t)); + *instr = *instrument; + + if (instrument->name) + instr->name = MikMod_strdup(instrument->name); + + /* Make a copy of all layers at once */ + for (layers = 0, cur_layer = instrument->info.layer; cur_layer; layers++) + cur_layer = cur_layer->next; + + if (!(dest_layer = instr->info.layer = (gus_layer_t *) MikMod_malloc(sizeof(gus_layer_t) * layers))) { + if (instr->name) + MikMod_free(instr->name); + MikMod_free(instr); + return NULL; + } + for (waves = 0, cur_layer = instrument->info.layer; cur_layer; + cur_layer = cur_layer->next) { + *dest_layer = *cur_layer; + dest_layer->wave = NULL; + /* Count the total number of waves */ + for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) + waves++; + if (cur_layer->next) + dest_layer->next = dest_layer + 1; + else + dest_layer->next = NULL; + dest_layer++; + } + + /* Allocate memory for waves */ + if (!(dest_wave = (gus_wave_t *) MikMod_malloc(sizeof(gus_wave_t) * waves))) { + MikMod_free(instr->info.layer); + if (instr->name) + MikMod_free(instr->name); + MikMod_free(instr); + return NULL; + } + for (cur_layer = instrument->info.layer, dest_layer = instr->info.layer; + cur_layer; cur_layer = cur_layer->next, dest_layer = dest_layer->next) + /* Copy all waves */ + for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) { + if (!dest_layer->wave) + dest_layer->wave = dest_wave; + + *dest_wave = *cur_wave; + /* Mark DRAM address as unallocated */ + dest_wave->begin.memory = -1; + + if (cur_wave->next) + dest_wave->next = (dest_wave + 1); + else + dest_wave->next = NULL; + dest_wave++; + } + + /* Insert the instrument into list of registered instruments */ + cur_instr = (gus_instrument_t **) & gus.instr; + while (*cur_instr) + cur_instr = &(*cur_instr)->next; + *cur_instr = instr; + + return instr; +} + +static void __gus_instruments_clear() +{ + gus_instrument_t *next_instr, *cur_instr = (gus_instrument_t *) gus.instr; + while (cur_instr) { + next_instr = cur_instr->next; + __gus_instrument_free(cur_instr); + cur_instr = next_instr; + } +} + +/******************************************************* libGUS interface *****/ + +/* return value: number of GUS cards installed in system */ +int gus_cards() +{ + if (!gus.ok) + __gus_init(); + return gus.ok ? 1 : 0; +} + +int gus_info(gus_info_t * info, int reread) +{ + if (!gus.ok) + __gus_init(); + if (!gus.ok) + return -1; + + strcpy((char *)info->id, "gus0"); + info->flags = (gus.ram ? GUS_STRU_INFO_F_PCM : 0); + info->version = gus.version; + info->port = gus.port; + info->irq = gus.irq[0]; + info->dma1 = gus.dma[0]; + info->dma2 = gus.dma[1]; + + info->mixing_freq = gus.freq; + + info->memory_size = gus.ram * 1024; + info->memory_free = __gus_mem_get_free(); + info->memory_block_8 = __gus_mem_get_free_8(); + info->memory_block_16 = __gus_mem_get_free_16(); + return 0; +} + +int gus_open(int card, size_t queue_buffer_size, int non_block) +{ + __dpmi_meminfo struct_info, pool_info; + + if (!gus.ok) + __gus_init(); + + if (!gus.ok || gus.open || card != 0) + return -1; + + /* Now lock the gus structure in memory */ + struct_info.address = __djgpp_base_address + (unsigned long)&gus; + struct_info.size = sizeof(gus); + if (__dpmi_lock_linear_region(&struct_info)) + return -1; + + /* And hook the GF1 interrupt */ + __irq_stack_count = 4; + gus.gf1_irq = + irq_hook(gus.irq[0], gf1_irq, (long)gf1_irq_end - (long)gf1_irq); + __irq_stack_count = 1; + if (!gus.gf1_irq) { + __dpmi_unlock_linear_region(&struct_info); + return -1; + } + + /* Enable the interrupt */ + irq_enable(gus.gf1_irq); + if (gus.irq[0] > 7) + _irq_enable(2); + + /* Allocate a DMA buffer: if we fail, we just use I/O so don't fail */ + if ((gus.transfer == NULL) || (gus.transfer == __gus_transfer_dma)) + gus.dma_buff = dma_allocate(gus.dma[0], GF1_DMA_BUFFER_SIZE); + else + gus.dma_buff = NULL; + + /* Detect the best available RAM -> DRAM transfer function */ + if (!gus.transfer) { + __gus_detect_transfer(); + if (gus.transfer != __gus_transfer_dma || !gus.transfer) + dma_free(gus.dma_buff), gus.dma_buff = NULL; + + /* If no transfer function worked, fail */ + if (!gus.transfer) { + if (gus.dma_buff) { + dma_free(gus.dma_buff); + gus.dma_buff = NULL; + } + __dpmi_unlock_linear_region(&struct_info); + irq_unhook(gus.gf1_irq); + gus.gf1_irq = NULL; + return -1; + } + } + + /* Allocate and lock command pool buffer */ + if (queue_buffer_size < 64) + queue_buffer_size = 64; + if (queue_buffer_size > 16384) + queue_buffer_size = 16384; + gus.cmd_pool = (unsigned char *) MikMod_malloc(queue_buffer_size); + pool_info.address = __djgpp_base_address + (unsigned long)&gus.cmd_pool; + pool_info.size = sizeof(queue_buffer_size); + if (__dpmi_lock_linear_region(&pool_info)) { + if (gus.dma_buff) { + dma_free(gus.dma_buff); + gus.dma_buff = NULL; + } + __dpmi_unlock_linear_region(&struct_info); + irq_unhook(gus.gf1_irq); + gus.gf1_irq = NULL; + return -1; + } + + gus.open++; + + __gus_mem_clear(); + gus.t1_callback = __gus_timer_update; + gus.wt_callback = __gus_wavetable_update; + gus.vl_callback = __gus_volume_update; + gus_do_tempo(60); /* Default is 60 Hz */ + + return 0; +} + +int gus_close(int card) +{ + __dpmi_meminfo struct_info; + + if (!gus.open || card != 0) + return -1; + + /* First reset the card: disable any operation it can currently perform */ + __gus_reset(0); + + gus.open--; + + /* Stop the timer */ + gus_timer_stop(); + + /* Free DMA buffer if used */ + if (gus.dma_buff) { + dma_free(gus.dma_buff); + gus.dma_buff = NULL; + } + + /* And unhook the GF1 interrupt */ + irq_unhook(gus.gf1_irq); + gus.gf1_irq = NULL; + + /* Unlock the gus structure */ + struct_info.address = __djgpp_base_address + (unsigned long)&gus; + struct_info.size = sizeof(gus); + __dpmi_unlock_linear_region(&struct_info); + + __gus_mem_clear(); + __gus_instruments_clear(); + + return 0; +} + +int gus_select(int card) +{ + if (!gus.open || (card != 0)) + return -1; + + return 0; +} + +/* return value: same as gus_reset function + note: this command doesn't change number of active voices and doesn't do + hardware reset */ +int gus_reset_engine_only() +{ + gus.timer_base = 100; + return 0; +} + +int gus_reset(int voices, unsigned int channel_voices) +{ + static unsigned short freq_table[32 - 14 + 1] = { + 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, + 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 + }; + int voice; + int timer; + + /* No support for dynamically allocated voices for now */ + gus.dynmask = channel_voices; + + if (voices < 14) + voices = 14; + if (voices > 32) + voices = 32; + + /* Stop the timer so that GUS IRQ won't clobber registers */ + timer = (gus.timer_ctl_reg & GF1M_TIMER1); + if (timer) + gus_timer_stop(); + + /* Stop all voices */ + for (voice = 0; voice < 32; voice++) { + __gus_select_voice(voice); + __gus_stop_voice(); + gus.cur_wave[voice] = NULL; + gus.cur_vol[voice] = 0; + + __gus_delay(); + + /* Reset voice parameters to reasonable values */ + __gus_set_current(0, 0); + __gus_set_loop_start(0, 0); + __gus_set_loop_end(0, 0); + __gus_outregw(GF1R_VOLUME, 0); + __gus_outregb(GF1R_VOLUME_RATE, 0); + __gus_outregb(GF1R_VOLUME_START, 0); + __gus_outregb(GF1R_VOLUME_END, 0); + __gus_outregb(GF1R_BALANCE, 0x7); + } + + voice = (__gus_inregb(GF1R_VOICES) & 0x1f) + 1; + + if (voice != voices) { + int reset = __gus_inregb(GF1R_RESET); + __gus_outregb(GF1R_RESET, reset & ~GF1M_OUTPUT_ENABLE); + __gus_delay(); + __gus_outregb(GF1R_VOICES, 0xc0 | (voices - 1)); + __gus_delay(); + __gus_outregb(GF1R_RESET, reset); + } + + /* Compute the discretization frequence */ + gus.voices = voices; + if (gus.interwave) + gus.freq = 44100; + else + gus.freq = freq_table[voices - 14]; + + gus_reset_engine_only(); + + if (timer) + gus_timer_continue(); + + return gus.voices; +} + +int gus_do_flush() +{ + DEBUG_PRINT(("gus_do_flush: top = %d\n", gus.cmd_pool_top)) + gus.cmd_pool_ready = 1; + return 0; +} + +/* set new tempo */ +void gus_do_tempo(unsigned int tempo) +{ + DEBUG_PRINT(("gus_do_tempo (%d)\n", tempo)) + gus_timer_tempo(tempo); + gus_timer_start(); +} + +/* set voice frequency in Hz */ +void gus_do_voice_frequency(unsigned char voice, unsigned int freq) +{ + DEBUG_PRINT(("gus_do_voice_frequency (%d, %d)\n", voice, freq)) + __pool_select_voice(voice); + __pool_command_w(PCMD_FREQ, + (((freq << 9) + (gus.freq >> 1)) / gus.freq) << 1); +} + +/* set voice pan (0-16384) (full left - full right) */ +void gus_do_voice_pan(unsigned char voice, unsigned short pan) +{ + DEBUG_PRINT(("gus_do_voice_pan (%d, %d)\n", voice, pan)) + pan >>= 10; + if (pan > 15) + pan = 15; + __pool_select_voice(voice); + __pool_command_b(PCMD_PAN, pan); +} + +/* set voice volume level 0-16384 (linear) */ +void gus_do_voice_volume(unsigned char voice, unsigned short vol) +{ + DEBUG_PRINT(("gus_do_voice_volume (%d, %d)\n", voice, vol)) + if (vol > 0x3fff) + vol = 0x3fff; + __pool_select_voice(voice); + __pool_command_w(PCMD_VOLUME, __gus_volume_table[vol >> 5]); +} + +/* start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + */ +void gus_do_voice_start(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan) +{ + gus_do_voice_start_position(voice, program, freq, volume, pan, 0); +} + +/* start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + * position : offset to wave in bytes * 16 (lowest 4 bits - fraction) + */ +void gus_do_voice_start_position(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan, unsigned int position) +{ + gus_instrument_t *instrument; + gus_wave_t *wave; + + DEBUG_PRINT( + ("gus_do_voice_start_position (%d, %d, pos: %d)\n", voice, + program, position)) + + instrument = __gus_instrument_get(program); + + if (!instrument + || !instrument->info.layer + || !instrument->info.layer->wave + || instrument->flags == GUS_INSTR_F_NOT_FOUND + || instrument->flags == GUS_INSTR_F_NOT_LOADED) return; + + gus_do_voice_frequency(voice, freq); + gus_do_voice_pan(voice, pan); + + /* We have to set volume different way, to avoid unneeded work in handler */ + if (volume > 0x3fff) + volume = 0x3fff; + __pool_command_w(PCMD_VOLUME_PREPARE, __gus_volume_table[volume >> 5]); + + switch (instrument->mode) { + case GUS_INSTR_SIMPLE: + wave = instrument->info.layer->wave; + if (position) + __pool_command_l(PCMD_OFFSET, position); + __pool_command_l(PCMD_START, (unsigned long)wave); + break; + } +} + +/* stop voice + * mode = 0 : stop voice now + * mode = 1 : disable wave loop and finish it + */ +void gus_do_voice_stop(unsigned char voice, unsigned char mode) +{ + __pool_select_voice(voice); + if (mode) + __pool_command(PCMD_STOP_LOOP); + else + __pool_command(PCMD_STOP); +} + +/* wait x ticks - this command is block separator + all commands between blocks are interpreted in the begining of one tick */ +void gus_do_wait(unsigned int ticks) +{ + DEBUG_PRINT(("gus_do_wait (%d)\n", ticks)) + + ticks += gus.t1_ticks; + while ((int)(ticks - gus.t1_ticks) > 0); +} + +int gus_get_voice_status(int voice) +{ + __gus_select_voice(voice); + return __gus_inregb(GF1R_VOICE_CONTROL) & GF1VC_STOPPED ? 0 : 1; +} + +/* return value: file handle (descriptor) for /dev/gus */ +int gus_get_handle() +{ + /* Return stdout handle so that select() will "work" with it */ + return 0; +} + +/* return value: zero if instrument was successfully allocated */ +int gus_memory_alloc(gus_instrument_t * instrument) +{ + gus_instrument_t *instr = __gus_instrument_copy(instrument); + gus_layer_t *cur_layer; + gus_wave_t *cur_wave; + + DEBUG_PRINT(("gus_memory_alloc (%d)\n", instrument->number.instrument)) + + if (!instr) + return -1; + + for (cur_layer = instr->info.layer; cur_layer; + cur_layer = cur_layer->next) for (cur_wave = cur_layer->wave; + cur_wave; + cur_wave = cur_wave->next) { + if (cur_layer->mode == GUS_INSTR_SIMPLE) { + cur_wave->begin.memory = __gus_mem_alloc(cur_wave->size, + cur_wave->format & + GUS_WAVE_16BIT); + if (cur_wave->begin.memory == (unsigned int)-1) { + __gus_instrument_free(instr); + return -1; + } + gus.transfer(cur_wave->begin.memory, cur_wave->begin.ptr, + cur_wave->size, cur_wave->format); + } else if (cur_layer->mode == GUS_INSTR_PATCH) + /* not supported yet */ ; + } + + return 0; +} + +/* return value: zero if instrument was successfully removed */ +int gus_memory_free(gus_instrument_t * instrument) +{ + gus_instrument_t *cur_instr = gus.instr; + + DEBUG_PRINT(("gus_memory_free (%d)\n", instrument->number.instrument)) + + for (; cur_instr; cur_instr = cur_instr->next) + if (cur_instr->number.instrument == instrument->number.instrument) + return __gus_instrument_free(cur_instr); + + return -1; +} + +/* return value: unused gus memory in bytes */ +int gus_memory_free_size() +{ + return __gus_mem_get_free(); +} + +/* return value: zero if success */ +int gus_memory_pack() +{ + __gus_mem_pack(); + return 0; +} + +/* return value: gus memory size in bytes */ +int gus_memory_size() +{ + return gus.ram * 1024; +} + +/* return value: current largest free block for 8-bit or 16-bit wave */ +int gus_memory_free_block(int w_16bit) +{ + return w_16bit ? __gus_mem_get_free_16() : __gus_mem_get_free_8(); +} + +/* input value: see to GUS_DOWNLOAD_MODE_XXXX constants (gus.h) + return value: zero if samples & instruments was successfully removed from + GF1 memory manager */ +int gus_memory_reset(int mode) +{ + __gus_mem_clear(); + __gus_instruments_clear(); + return 0; +} + +/* return value: zero if command queue was successfully flushed */ +int gus_queue_flush() +{ + return 0; +} + +/* input value: echo buffer size in items (if 0 - erase echo buffer) */ +int gus_queue_read_set_size(int items) +{ + return 0; +} + +/* input value: write queue size in items (each item have 8 bytes) */ +int gus_queue_write_set_size(int items) +{ + return 0; +} + +/* return value: zero if successfull */ +int gus_timer_start() +{ + gus.timer_ctl_reg |= GF1M_TIMER1; + __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); + + gus.timer_ctl = gus.timer_ctl & ~GF1M_MASK_TIMER1; + outportb(GF1_TIMER_CTRL, 0x04); + outportb(GF1_TIMER_DATA, gus.timer_ctl | GF1M_START_TIMER1); + return 0; +} + +/* return value: zero if timer was stoped */ +int gus_timer_stop() +{ + gus.timer_ctl_reg &= ~GF1M_TIMER1; + __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); + + gus.timer_ctl = gus.timer_ctl | GF1M_MASK_TIMER1; + outportb(GF1_TIMER_CTRL, 0x04); + outportb(GF1_TIMER_DATA, gus.timer_ctl); + return 0; +} + +/* return value: zero if setup was success */ +int gus_timer_tempo(int ticks) +{ + unsigned int counter; + + /* Limit ticks per second to 1..1000 range */ + if (ticks < 1) + ticks = 1; + if (ticks > 1000) + ticks = 1000; + + /* GF1 timer1 period is 80 usecs, 12500 times per second */ + counter = 1250000 / (ticks * gus.timer_base); + gus.t1_multiple = 1; + while (counter > 255) { + counter >>= 1; + gus.t1_multiple <<= 1; + } + gus.t1_countdown = gus.t1_multiple; + __gus_outregb(GF1R_TIMER1, 256 - counter); + return 0; +} + +/* return value: zero if timer will be continue */ +int gus_timer_continue() +{ + return gus_timer_start(); +} + +/* return value: zero if setup was success (default timebase = 100) */ +int gus_timer_base(int base) +{ + gus.timer_base = base; + return 0; +} + +void gus_timer_callback(void (*timer_callback) ()) +{ + gus.timer_callback = timer_callback; +} + +void gus_convert_delta(unsigned int type, unsigned char *dest, + unsigned char *src, size_t size) +{ + if (!(type & GUS_WAVE_DELTA)) + return; + + /* This doesn't depend much on wave signedness, since addition/subtraction + do not depend on operand signedness */ + if (type & GUS_WAVE_16BIT) { + unsigned short delta = type & GUS_WAVE_UNSIGNED ? 0x8000 : 0; + while (size--) { + delta = *(unsigned short *)dest = *(unsigned short *)src + delta; + src += sizeof(unsigned short); + dest += sizeof(unsigned short); + } + } else { + unsigned char delta = type & GUS_WAVE_UNSIGNED ? 0x80 : 0; + while (size--) { + delta = *(unsigned char *)dest = *(unsigned char *)src + delta; + src++; + dest++; + } + } +} + +int gus_dma_usage (int use) +{ + if (gus.dma_buff) + return -1; + gus.transfer = __gus_transfer_io; + return 0; +} + +#endif /* DRV_ULTRA */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dosgus.h b/libs/mikmod/drvdos/dosgus.h new file mode 100644 index 0000000..ae3488a --- /dev/null +++ b/libs/mikmod/drvdos/dosgus.h @@ -0,0 +1,514 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + libGUS-alike definitions for DOS + +==============================================================================*/ + +#ifndef __DOSGUS_H__ +#define __DOSGUS_H__ + +#include +#include "dosdma.h" +#include "dosirq.h" +#include "libgus.h" + +/* Private header file for a libGUS-alike library for DOS */ + +#define JOYSTICK_TIMER (gus.port+0x201) /* 201 */ +#define JOYSTICK_DATA (gus.port+0x201) /* 201 */ + +#define GF1_MIDI_CTRL (gus.port+0x100) /* 3X0 */ +#define GF1_MIDI_DATA (gus.port+0x101) /* 3X1 */ + +#define GF1_VOICESEL (gus.port+0x102) /* 3X2 */ +#define GF1_REGSEL (gus.port+0x103) /* 3X3 */ +#define GF1_DATA (gus.port+0x104) /* 3X4 */ +#define GF1_DATA_LOW (gus.port+0x104) /* 3X4 */ +#define GF1_DATA_HIGH (gus.port+0x105) /* 3X5 */ +#define GF1_IRQ_STATUS (gus.port+0x006) /* 2X6 */ +#define GF1_DRAM (gus.port+0x107) /* 3X7 */ + +#define GF1_MIX_CTRL (gus.port+0x000) /* 2X0 */ +#define GF1_TIMER_CTRL (gus.port+0x008) /* 2X8 */ +#define GF1_TIMER_DATA (gus.port+0x009) /* 2X9 */ +#define GF1_IRQ_CTRL (gus.port+0x00B) /* 2XB */ +#define GF1_REG_CTRL (gus.port+0x00F) /* 2XF */ + +#define GF1_REVISION (gus.port+0x506) /* 7X6 */ + +/* The GF1 hardware clock rate */ +#define CLOCK_RATE 9878400L + +/* GF1 voice-independent registers */ +#define GF1R_DMA_CONTROL 0x41 +#define GF1R_DMA_ADDRESS 0x42 +#define GF1R_DRAM_LOW 0x43 +#define GF1R_DRAM_HIGH 0x44 + +#define GF1R_TIMER_CONTROL 0x45 +#define GF1R_TIMER1 0x46 +#define GF1R_TIMER2 0x47 + +#define GF1R_SAMPLE_RATE 0x48 +#define GF1R_SAMPLE_CONTROL 0x49 + +#define GF1R_JOYSTICK 0x4B +#define GF1R_RESET 0x4C + +/* GF1 voice-specific registers */ +#define GF1R_VOICE_CONTROL 0x00 +#define GF1R_FREQUENCY 0x01 +#define GF1R_START_HIGH 0x02 +#define GF1R_START_LOW 0x03 +#define GF1R_END_HIGH 0x04 +#define GF1R_END_LOW 0x05 +#define GF1R_VOLUME_RATE 0x06 +#define GF1R_VOLUME_START 0x07 +#define GF1R_VOLUME_END 0x08 +#define GF1R_VOLUME 0x09 +#define GF1R_ACC_HIGH 0x0a +#define GF1R_ACC_LOW 0x0b +#define GF1R_BALANCE 0x0c +#define GF1R_VOLUME_CONTROL 0x0d +#define GF1R_VOICES 0x0e +#define GF1R_IRQ_SOURCE 0x0f + +/* Add this to above registers for reading */ +#define GF1R_READ_MASK 0x80 + +/* MIDI */ +#define GF1M_MIDI_RESET 0x03 +#define GF1M_MIDI_ENABLE_XMIT 0x20 +#define GF1M_MIDI_ENABLE_RCV 0x80 + +#define GF1M_MIDI_RCV_FULL 0x01 +#define GF1M_MIDI_XMIT_EMPTY 0x02 +#define GF1M_MIDI_FRAME_ERR 0x10 +#define GF1M_MIDI_OVERRUN 0x20 +#define GF1M_MIDI_IRQ_PEND 0x80 + +/* Joystick */ +#define GF1M_JOY_POSITION 0x0f +#define GF1M_JOY_BUTTONS 0xf0 + +/* GF1_IRQ_STATUS (port 2X6) */ +#define GF1M_IRQ_MIDI_TX 0x01 /* pending MIDI xmit IRQ */ +#define GF1M_IRQ_MIDI_RX 0x02 /* pending MIDI recv IRQ */ +#define GF1M_IRQ_TIMER1 0x04 /* general purpose timer */ +#define GF1M_IRQ_TIMER2 0x08 /* general purpose timer */ +#define GF1M_IRQ_WAVETABLE 0x20 /* pending wavetable IRQ */ +#define GF1M_IRQ_ENVELOPE 0x40 /* pending volume envelope IRQ */ +#define GF1M_IRQ_DMA_COMPLETE 0x80 /* pending dma transfer complete IRQ */ + +/* GF1_MIX_CTRL (port 2X0) */ +#define GF1M_MIXER_NO_LINE_IN 0x01 /* 0: enable */ +#define GF1M_MIXER_NO_OUTPUT 0x02 /* 0: enable */ +#define GF1M_MIXER_MIC_IN 0x04 /* 1: enable */ +#define GF1M_MIXER_GF1_IRQ 0x08 /* 1: enable */ +#define GF1M_GF1_COMBINED_IRQ 0x10 /* 1: IRQ1 == IRQ2 */ +#define GF1M_MIDI_LOOPBACK 0x20 /* 1: enable loop back */ +#define GF1M_CONTROL_SELECT 0x40 /* 0: DMA latches; 1: IRQ latches */ + +/* Timer data register (2X9) */ +#define GF1M_START_TIMER1 0x01 +#define GF1M_START_TIMER2 0x02 +#define GF1M_MASK_TIMER1 0x20 +#define GF1M_MASK_TIMER2 0x40 +#define GF1M_TIMER_CLRIRQ 0x80 + +/* IRQ/DMA control register (2XB) */ +#define GF1M_IRQ_EQUAL 0x40 +#define GF1M_DMA_EQUAL 0x40 + +/* (0x41) DMA control register bits */ +#define GF1M_DMAR_ENABLE 0x01 /* 1: go */ +#define GF1M_DMAR_READ 0x02 /* 1: read (->RAM), 0: write (->DRAM) */ +#define GF1M_DMAR_CHAN16 0x04 /* 1: 16 bit, 0: 8 bit DMA channel */ +#define GF1M_DMAR_RATE 0x18 /* 00: fast, 11: slow */ +#define GF1M_DMAR_IRQ_ENABLE 0x20 /* 1: enable */ +#define GF1M_DMAR_IRQ_PENDING 0x40 /* R: DMA irq pending */ +#define GF1M_DMAR_DATA16 0x40 /* W: 0: 8 bits; 1: 16 bits per sample */ +#define GF1M_DMAR_TOGGLE_SIGN 0x80 /* W: 1: invert high bit */ + +/* DMA transfer rate divisors */ +#define GF1M_DMAR_RATE0 0x00 /* Fastest DMA xfer (~650khz) */ +#define GF1M_DMAR_RATE1 0x08 /* fastest / 2 */ +#define GF1M_DMAR_RATE2 0x10 /* fastest / 4 */ +#define GF1M_DMAR_RATE3 0x18 /* Slowest DMA xfer (fastest / 8) */ + +/* (0x45) Timer Control */ +#define GF1M_TIMER1 0x04 /* Enable timer 1 IRQ */ +#define GF1M_TIMER2 0x08 /* Enable timer 2 IRQ */ + +/* (0x49) Sampling (ADC) control register */ +#define GF1M_DMAW_ENABLE 0x01 /* 1: Start sampling */ +#define GF1M_DMAW_MODE 0x02 /* 0: mono, 1: stereo */ +#define GF1M_DMAW_CHAN16 0x04 /* 0: 8 bit, 1: 16 bit */ +#define GF1M_DMAW_IRQ_ENABLE 0x20 /* 1: enable IRQ */ +#define GF1M_DMAW_IRQ_PENDING 0x40 /* 1: irq pending */ +#define GF1M_DMAW_TOGGLE_SIGN 0x80 /* 1: invert sign bit */ + +/* (0x4C) GF1 reset register */ +#define GF1M_MASTER_RESET 0x01 /* 0: hold in reset */ +#define GF1M_OUTPUT_ENABLE 0x02 /* 1: enable output */ +#define GF1M_MASTER_IRQ 0x04 /* 1: master IRQ enable */ + +/* (0x0,0x80) Voice control register - GF1R_VOICE_CONTROL */ +#define GF1VC_STOPPED 0x01 /* 1: voice has stopped */ +#define GF1VC_STOP 0x02 /* 1: stop voice */ +#define GF1VC_DATA16 0x04 /* 0: 8 bit, 1: 16 bit */ +#define GF1VC_LOOP_ENABLE 0x08 /* 1: enable */ +#define GF1VC_BI_LOOP 0x10 /* 1: bi directional looping */ +#define GF1VC_IRQ 0x20 /* 1: enable voice's wave irq */ +#define GF1VC_BACKWARD 0x40 /* 0: increasing, 1: decreasing */ +#define GF1VC_IRQ_PENDING 0x80 /* 1: wavetable irq pending */ + +/* (0x01,0x81) Frequency control */ +/* Bit 0 - Unused */ +/* Bits 1-9 - Fractional portion */ +/* Bits 10-15 - Integer portion */ + +/* (0x02,0x82) Accumulator start address - GF1R_START_HIGH */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* (0x03,0x83) Accumulator start address - GF1R_START_LOW */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* (0x04,0x84) Accumulator end address - GF1R_END_HIGH */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* (0x05,0x85) Accumulator end address - GF1R_END_LOW */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* (0x06,0x86) Volume Envelope control register - GF1R_VOLUME_RATE */ +#define GF1VL_RATE_MANTISSA 0x3f +#define GF1VL_RATE_RANGE 0xC0 + +/* (0x07,0x87) Volume envelope start - GF1R_VOLUME_START */ +#define GF1VL_START_MANT 0x0F +#define GF1VL_START_EXP 0xF0 + +/* (0x08,0x88) Volume envelope end - GF1R_VOLUME_END */ +#define GF1VL_END_MANT 0x0F +#define GF1VL_END_EXP 0xF0 + +/* (0x09,0x89) Current volume register - GF1R_VOLUME */ +/* Bits 0-3 - Unused */ +/* Bits 4-11 - Mantissa of current volume */ +/* Bits 10-15 - Exponent of current volume */ + +/* (0x0A,0x8A) Accumulator value (high) */ +/* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */ + +/* (0x0B,0x8B) Accumulator value (low) */ +/* Bits 0-8 - Fractional portion */ +/* Bits 9-15 - Integer portion of low adress (a6-a0) */ + +/* (0x0C,0x8C) Pan (balance) position */ +/* Bits 0-3 - Balance position 0=full left, 0x0f=full right */ + +/* (0x0D,0x8D) Volume control register - GF1R_VOLUME_CONTROL */ +#define GF1VL_STOPPED 0x01 /* volume has stopped */ +#define GF1VL_STOP 0x02 /* stop volume */ +#define GF1VL_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */ +#define GF1VL_LOOP_ENABLE 0x08 /* 1: enable */ +#define GF1VL_BI_LOOP 0x10 /* 1: bi directional looping */ +#define GF1VL_IRQ 0x20 /* 1: enable voice's volume irq */ +#define GF1VL_BACKWARD 0x40 /* 0: increasing, 1: decreasing */ +#define GF1VL_IRQ_PENDING 0x80 /* 1: wavetable irq pending */ + +/* (0x0E,0x8E) Number of active voices */ +/* Bits 0-5 - Number of active voices - 1 */ + +/* (0x0F,0x8F) Sources of IRQs */ +/* Bits 0-4 - interrupting voice number */ +/* Bit 5 - Always a 1 */ +#define GF1IRQ_VOLUME 0x40 /* individual voice irq bit */ +#define GF1IRQ_WAVE 0x80 /* individual waveform irq bit */ + +/* Commands are pooled and executed ON TIMER (1st timer) interrupt. + * Currently there is a limit on the number of commands that you can + * issue between gus_do_flush (...); this should not be an issue however + * because each voice has a limited (little) set of parameters that + * you can change (freq, vol, pan... what else?) + * + * The pool is a pseudo-CPU code that gets executed once per timer interrupt. + */ + +/* Below are definitions for commands placed in GUS command pool */ +#define PCMD_NOP 0x00 /* Traditionally ... */ +#define PCMD_VOICE 0x01 /* +B: select voice */ +#define PCMD_START 0x02 /* +L: start voice */ +#define PCMD_STOP 0x03 /* stop voice */ +#define PCMD_FREQ 0x04 /* +W: set frequence */ +#define PCMD_VOLUME 0x05 /* +W: set volume */ +#define PCMD_VOLUME_PREPARE 0x06 /* +W: prepare to set volume on (soon to follow) kick */ +#define PCMD_PAN 0x07 /* +B: set panning */ +#define PCMD_OFFSET 0x08 /* +L: set DRAM offset */ +#define PCMD_STOP_LOOP 0x09 /* stop looping */ + +#define GUS_VOLCHANGE_RAMP 0x20 /* Volume change ramp speed */ + +/* Definition for the boolean type */ +typedef unsigned char boolean; +/* Prototype for functions that do block transfers to GUS DRAM: + flags can contain any of the following bits: + GUS_WAVE_16BIT - sample is 16-bit + GUS_WAVE_UNSIGNED - do not invert sign bit while downloading + */ +typedef void (*__gus_transfer_func) (unsigned long address, + unsigned char *source, + unsigned long size, int flags); +typedef void (*__gus_callback) (); +typedef void (*__gus_callback_3) (unsigned int, unsigned int, unsigned int); + +/* Structure used to keep track of all on-board GUS memory */ +typedef struct __struct_gus_mcb { + struct __struct_gus_mcb *next; /* Next MCB in chain */ + struct __struct_gus_mcb *prev; /* Previous MCB in chain */ + unsigned int addr; /* GUS DRAM address */ + unsigned int size; /* Memory block size */ + int free; /* 1: block is free */ +} __gus_mcb; + +/* Structure defining overall GUS state/information */ +typedef struct __gus_state_s { + unsigned int port; /* Base I/O port (0x220, 0x240, ...) */ + unsigned int irq[2]; /* GF1 IRQ and MIDI IRQ */ + unsigned int dma[2]; /* Play / record DMA */ + unsigned int ram; /* Memory size (K), i.e. 256, 1024 etc */ + unsigned int version; /* GUS version (see GUS_CARD_VERSION_XXX in libgus.h */ + unsigned int freq; /* Current mixing frequency */ + unsigned int voices; /* Active voices (14-32) */ + unsigned int dynmask; /* Dynamically allocated voices mask */ + unsigned int timer_base; /* The relative timer speed in percents (def: 100) */ + volatile unsigned int t1_ticks; /* Incremented per each timer1 tick */ + volatile unsigned int t2_ticks; /* Incremented per each timer2 tick */ + volatile unsigned int t1_countdown; /* t1_callback is called when this reaches zero */ + volatile unsigned int t2_countdown; /* t2_callback is called when this reaches zero */ + unsigned int t1_multiple; /* Timer1 handler is called once per such many ticks */ + unsigned int t2_multiple; /* Timer2 handler is called once per such many ticks */ + struct irq_handle *gf1_irq; /* The interrupt handler for GF1 events */ + dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ + __gus_callback dma_callback; /* Routine called at end of DMA transfers */ + __gus_callback t1_callback; /* Routine called on Timer1 events */ + __gus_callback t2_callback; /* Routine called on Timer1 events */ + __gus_callback timer_callback; /* Called once per TEMPO ticks */ + __gus_callback_3 wt_callback; /* Routine called on WaveTable events */ + __gus_callback_3 vl_callback; /* Routine called on Volume ramp events */ + __gus_mcb *mcb; /* Chained list of memory control blocks */ + __gus_transfer_func transfer; /* Best working function for DRAM transfer */ + gus_instrument_t *instr; /* The list of registered instruments */ + unsigned short mixer; /* Current mixer register state */ + unsigned char dma_rate; /* One of GF1M_DMAR_RATEX constants defined above */ + unsigned char timer_ctl; /* Timer control register value (2x8/2x9) */ + unsigned char timer_ctl_reg; /* Timer control register value (GF1/0x45) */ + boolean ok; /* Is the information below okay? */ + boolean open; /* 1 if between gus_open() and gus_close() */ + boolean ics; /* Is it equipped with an ICS mixer? */ + boolean ics_flipped; /* rev 5 (3.7) has flipped R/L mixer */ + boolean codec; /* Is it equipped with a GUS MAX codec? */ + boolean interwave; /* GUS InterWave card */ + volatile boolean dma_active; /* DMA is transferring data */ + volatile boolean cmd_pool_ready; /* Flush cmd_pool during timer interrupt */ + unsigned char cmd_voice; /* Pool selection index cache */ + unsigned int cmd_pool_top; /* Command pool top */ + unsigned char *cmd_pool; /* Async commands pool */ + /* The following data is for private use only by interrupt routines! */ + gus_wave_t *cur_wave[32]; /* Currently played waves */ + boolean voice_kick[32]; /* Kick wave on next volume ramp IRQ */ + unsigned int kick_offs[32]; /* Sample start position on kick */ + unsigned short cur_vol[32]; /* Current voice volumes */ + unsigned int cur_voice; /* Current voice */ + unsigned int eow_ignore; /* Temp ignore end-of-wave IRQ for these voices */ +} __gus_state; + +extern __gus_state gus; +extern void __gus_delay(); + +static unsigned long __gus_convert_addr16(unsigned long address) +{ + return ((address & 0x0003ffff) >> 1) | (address & ~0x0003ffff); +} + +/* The XXX_slow routines cannot be used outside IRQ handler! */ +static inline void __gus_outregb_slow(unsigned char reg, unsigned char value) +{ + outportb(GF1_REGSEL, reg); + outportb(GF1_DATA_HIGH, value); + __gus_delay(); + outportb(GF1_DATA_HIGH, value); +} + +static inline void __gus_outregw_slow(unsigned char reg, unsigned short value) +{ + outportb(GF1_REGSEL, reg); + outportw(GF1_DATA, value); + __gus_delay(); + outportw(GF1_DATA, value); +} + +static inline void __gus_outregb(unsigned char reg, unsigned char value) +{ + outportb(GF1_REGSEL, reg); + outportb(GF1_DATA_HIGH, value); +} + +static inline void __gus_outregw(unsigned char reg, unsigned short value) +{ + outportb(GF1_REGSEL, reg); + outportw(GF1_DATA, value); +} + +static inline unsigned char __gus_inregb(unsigned char reg) +{ + if (reg < 0x10) + reg |= GF1R_READ_MASK; + outportb(GF1_REGSEL, reg); + return inportb(GF1_DATA_HIGH); +} + +static inline unsigned short __gus_inregw(unsigned char reg) +{ + if (reg < 0x10) + reg |= GF1R_READ_MASK; + outportb(GF1_REGSEL, reg); + return inportw(GF1_DATA); +} + +static inline void __gus_set_dram_address(unsigned int address) +{ + __gus_outregb(GF1R_DRAM_HIGH, address >> 16); + __gus_outregw(GF1R_DRAM_LOW, address); +} + +static inline unsigned char __gus_peek(unsigned int address) +{ + __gus_set_dram_address(address); + return inportb(GF1_DRAM); +} + +static inline void __gus_poke(unsigned int address, unsigned char value) +{ + __gus_set_dram_address(address); + outportb(GF1_DRAM, value); +} + +static inline void __gus_select_voice(unsigned char voice) +{ + outportb(GF1_VOICESEL, voice); +} + +static inline void __gus_set_current(unsigned char mode, + unsigned long address) +{ + if (mode & GF1VC_DATA16) + address = __gus_convert_addr16(address); + __gus_outregw_slow(GF1R_ACC_HIGH, address >> 11); + __gus_outregw_slow(GF1R_ACC_LOW, address << 5); +} + +static inline void __gus_set_loop_start(unsigned char mode, + unsigned long address) +{ + if (mode & GF1VC_DATA16) + address = __gus_convert_addr16(address); + __gus_outregw_slow(GF1R_START_HIGH, address >> 11); + __gus_outregw_slow(GF1R_START_LOW, address << 5); +} + +static inline void __gus_set_loop_end(unsigned char mode, + unsigned long address) +{ + address--; + if (mode & GF1VC_DATA16) + address = __gus_convert_addr16(address); + __gus_outregw_slow(GF1R_END_HIGH, address >> 11); + __gus_outregw_slow(GF1R_END_LOW, address << 5); +} + +static inline void __gus_mixer_output(boolean state) +{ + if (state) + gus.mixer &= ~GF1M_MIXER_NO_OUTPUT; + else + gus.mixer |= GF1M_MIXER_NO_OUTPUT; + outportb(GF1_MIX_CTRL, gus.mixer); + /* Dummy read to avoid touching DMA latches */ + __gus_inregb(GF1R_BALANCE); +} + +/* Inline routines for working with command pools */ + +/* WARNING: no bounds checking due to performance reasons */ +#define __POOL_VALUE(type,value) \ + *((unsigned type *)&gus.cmd_pool [gus.cmd_pool_top]) = value; \ + gus.cmd_pool_top += sizeof (type); + +static inline void __pool_command(unsigned char command) +{ + __POOL_VALUE(char, command); +} + +static inline void __pool_command_b(unsigned char command, unsigned char arg) +{ + __POOL_VALUE(char, command); + __POOL_VALUE(char, arg); +} + +static inline void __pool_command_w(unsigned char command, unsigned short arg) +{ + __POOL_VALUE(char, command); + __POOL_VALUE(short, arg); +} + +static inline void __pool_command_l(unsigned char command, unsigned long arg) +{ + __POOL_VALUE(char, command); + __POOL_VALUE(long, arg); +} + +static inline void __pool_select_voice(unsigned char voice) +{ + if (gus.cmd_voice != voice) + __pool_command_b(PCMD_VOICE, gus.cmd_voice = voice); +} + +#undef __POOL_VALUE + +#ifdef DEBUG +/* Debug dump of GUS DRAM heap */ +extern void __gus_mem_dump(); +#endif + +#endif /* __DOSGUS_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dosirq.c b/libs/mikmod/drvdos/dosirq.c new file mode 100644 index 0000000..9b0e21f --- /dev/null +++ b/libs/mikmod/drvdos/dosirq.c @@ -0,0 +1,323 @@ +/* + Implementation of IRQ routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "dosirq.h" + +#include +#include +#include +#include +#include +#include +#include "mikmod.h" /* for MikMod_malloc() & co */ + +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 + +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 *) MikMod_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)) { + MikMod_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); + + MikMod_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 *) MikMod_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)) { + MikMod_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); + + MikMod_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(); +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dosirq.h b/libs/mikmod/drvdos/dosirq.h new file mode 100644 index 0000000..eaf60a1 --- /dev/null +++ b/libs/mikmod/drvdos/dosirq.h @@ -0,0 +1,122 @@ +/* + Interface for IRQ routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __DOSIRQ_H__ +#define __DOSIRQ_H__ + +#include + +#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__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dossb.c b/libs/mikmod/drvdos/dossb.c new file mode 100644 index 0000000..344ac85 --- /dev/null +++ b/libs/mikmod/drvdos/dossb.c @@ -0,0 +1,575 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Sound Blaster I/O routines, common for SB8, SBPro and SB16 + Written by Andrew Zabolotny + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_SB + +#include +#include +#include +#include +#include +#include +#include + +#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; + unsigned int timer, oldcount; + + 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; +} + +#endif /* DRV_SB */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/dossb.h b/libs/mikmod/drvdos/dossb.h new file mode 100644 index 0000000..cb8dc70 --- /dev/null +++ b/libs/mikmod/drvdos/dossb.h @@ -0,0 +1,341 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + SoundBlaster and compatible soundcards definitions + +==============================================================================*/ + +#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__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/doswss.c b/libs/mikmod/drvdos/doswss.c new file mode 100644 index 0000000..41cdf38 --- /dev/null +++ b/libs/mikmod/drvdos/doswss.c @@ -0,0 +1,577 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Windows Sound System I/O routines (CS42XX, ESS18XX, GUS+DaughterBoard etc) + Written by Andrew Zabolotny + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_WSS + +#include +#include +#include +#include +#include +#include +#include + +#include "doswss.h" + +/********************************************* Private variables/routines *****/ + +__wss_state wss; + +/* WSS frequency rates... lower bit selects one of two frequency generators */ +static unsigned int wss_rates[14][2] = { + {5510, 0x00 | WSSM_XTAL2}, + {6620, 0x0E | WSSM_XTAL2}, + {8000, 0x00 | WSSM_XTAL1}, + {9600, 0x0E | WSSM_XTAL1}, + {11025, 0x02 | WSSM_XTAL2}, + {16000, 0x02 | WSSM_XTAL1}, + {18900, 0x04 | WSSM_XTAL2}, + {22050, 0x06 | WSSM_XTAL2}, + {27420, 0x04 | WSSM_XTAL1}, + {32000, 0x06 | WSSM_XTAL1}, + {33075, 0x0C | WSSM_XTAL2}, + {37800, 0x08 | WSSM_XTAL2}, + {44100, 0x0A | WSSM_XTAL2}, + {48000, 0x0C | WSSM_XTAL1} +}; + +static void wss_irq() +{ + /* Make sure its not a spurious IRQ */ + if (!irq_check(wss.irq_handle)) + return; + + wss.irqcount++; + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + /* Write transfer count again */ + __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff); + __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8); + irq_ack(wss.irq_handle); + + enable(); + if (wss.timer_callback) + wss.timer_callback(); +} + +static void wss_irq_end() +{ +} + +/* WSS accepts some conventional values instead of frequency in Hz... */ +static unsigned char __wss_getrate(unsigned int *freq) +{ + int i, best = -1, delta = 0xffff; + + for (i = 0; i < 14; i++) { + int newdelta = abs(wss_rates[i][0] - *freq); + if (newdelta < delta) + best = i, delta = newdelta; + } + + *freq = wss_rates[best][0]; + return wss_rates[best][1]; +} + +/* Check if we really have a WSS compatible card on given address */ +static boolean __wss_ping() +{ + /* Disable CODEC operations first */ + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + /* Now put some harmless values in registers and check them */ + __wss_outreg(WSSR_COUNT_LOW, 0xaa); + __wss_outreg(WSSR_COUNT_HIGH, 0x55); + return (__wss_inreg(WSSR_COUNT_LOW) == 0xaa) + && (__wss_inreg(WSSR_COUNT_HIGH) == 0x55); +} + +static boolean __wss_reset() +{ + int count; + + /* Disable output */ + wss_output(FALSE); + + /* Now select the test/initialization register */ + count = 10000; + while (inportb(WSS_ADDR) != WSSR_TEST_INIT) { + outportb(WSS_ADDR, WSSR_TEST_INIT); + if (!--count) + return FALSE; + } + + count = 10000; + while (inportb(WSS_DATA) & WSSM_CALIB_IN_PROGRESS) { + outportb(WSS_ADDR, WSSR_TEST_INIT); + if (!--count) + return FALSE; + } + + /* Enable playback IRQ */ + __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); + __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + return TRUE; +} + +static boolean __wss_setformat(unsigned char format) +{ + int count; + + outportb(WSS_ADDR, WSSM_MCE | WSSR_PLAY_FORMAT); + outportb(WSS_DATA, format); + inportb(WSS_DATA); /* ERRATA SHEETS ... */ + inportb(WSS_DATA); /* ERRATA SHEETS ... */ + + /* Wait end of syncronization ... */ + if (!__wss_wait()) + return FALSE; + + /* Turn off the ModeChangeEnable bit: do it until it works */ + count = 10000; + while (inportb(WSS_ADDR) != WSSR_PLAY_FORMAT) { + outportb(WSS_ADDR, WSSR_PLAY_FORMAT); + if (!--count) + return FALSE; + } + + return __wss_reset(); +} + +/**************************************************** WSS detection stuff *****/ + +static int __wss_irq_irqdetect(int irqno) +{ + unsigned char status = inportb(WSS_STATUS); + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + /* Reset transfer counter */ + __wss_outreg(WSSR_COUNT_LOW, 0); + __wss_outreg(WSSR_COUNT_HIGH, 0); + return (status & WSSM_INT); +} + +static boolean __wss_detect() +{ + /* First find the port number */ + if (!wss.port) { + static unsigned int wss_ports[] = + { 0x32c, 0x530, 0x604, 0xE80, 0xF40 }; + int i; + for (i = 0; i < 5; i++) { + wss.port = wss_ports[i]; + if (__wss_ping()) + break; + } + if (i < 0) { + wss.port = 0; + return FALSE; + } + } + + /* Now disable output */ + wss_output(FALSE); + + /* Detect the DMA channel */ + if (!wss.dma) { + static int __dma[] = { 0, 1, 3 }; + int i; + + /* Enable playback IRQ */ + __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); + __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); + + /* Start a short DMA transfer and check if DMA count is zero */ + for (i = 0; i < 3; i++) { + unsigned int timer, status, freq = 44100; + + wss.dma = __dma[i]; + + dma_disable(wss.dma); + dma_set_mode(wss.dma, DMA_MODE_WRITE); + dma_clear_ff(wss.dma); + dma_set_count(wss.dma, 10); + dma_enable(wss.dma); + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + __wss_setformat(__wss_getrate(&freq)); + __wss_outreg(WSSR_COUNT_LOW, 1); + __wss_outreg(WSSR_COUNT_HIGH, 0); + /* Tell codec to start transfer */ + __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + + while (_farnspeekl(0x46c) - timer <= 2) + if (dma_get_count(wss.dma) == 0) + break; + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + dma_disable(wss.dma); + + /* Now check if DMA transfer count is zero and an IRQ is pending */ + status = inportb(WSS_STATUS); + outportb(WSS_STATUS, 0); + if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT)) + break; + + wss.dma = 0; + } + + if (!wss.dma) + return FALSE; + } + + /* Now detect the IRQ number */ + if (!wss.irq) { + unsigned int i, irqmask, freq = 5510; + unsigned long timer, delta = 0x7fffffff; + + /* IRQ can be one of 2,3,5,7,10 */ + irq_detect_start(0x04ac, __wss_irq_irqdetect); + + dma_disable(wss.dma); + dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + dma_clear_ff(wss.dma); + dma_set_count(wss.dma, 1); + dma_enable(wss.dma); + + __wss_setformat(__wss_getrate(&freq)); + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + __wss_outreg(WSSR_COUNT_LOW, 0); + __wss_outreg(WSSR_COUNT_HIGH, 0); + + /* Prepare timeout counter */ + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + while (timer == _farnspeekl(0x46c)); + timer = _farnspeekl(0x46c); + + /* Reset all IRQ counters */ + irq_detect_clear(); + + /* Tell codec to start transfer */ + __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + + /* Now wait 1/18 seconds */ + while (timer == _farnspeekl(0x46c)); + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + dma_disable(wss.dma); + + /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval + of 1/18.2 second, we should have received about 302 interrupts */ + for (i = 2; i <= 10; i++) { + int count = abs(302 - irq_detect_get(i, &irqmask)); + if (count < delta) + wss.irq = i, delta = count; + } + if (delta > 150) + wss.irq = 0; + + irq_detect_end(); + if (!wss.irq) + return FALSE; + } + + return TRUE; +} + +/*************************************************** High-level interface *****/ + +/* Detect whenever WSS is present and fill "wss" structure */ +boolean wss_detect() +{ + char *env; + + /* Try to find the port and DMA from environment */ + env = getenv("WSS"); + + while (env && *env) { + /* Skip whitespace */ + while ((*env == ' ') || (*env == '\t')) + env++; + if (!*env) + break; + + switch (*env++) { + case 'A': + case 'a': + if (!wss.port) + wss.port = strtol(env, &env, 16); + break; + case 'I': + case 'i': + if (!wss.irq) + wss.irq = strtol(env, &env, 10); + break; + case 'D': + case 'd': + if (!wss.dma) + wss.dma = strtol(env, &env, 10); + break; + default: + /* Skip other values */ + while (*env && (*env != ' ') && (*env != '\t')) + env++; + break; + } + } + + /* Try to fill the gaps in wss hardware parameters */ + __wss_detect(); + + if (!wss.port || !wss.irq || !wss.dma) + return FALSE; + + if (!__wss_ping()) + return FALSE; + + if (!__wss_reset()) + return FALSE; + + wss.ok = 1; + return TRUE; +} + +/* Reset WSS */ +void wss_reset() +{ + wss_stop_dma(); + __wss_reset(); +} + +/* Open WSS for usage */ +boolean wss_open() +{ + __dpmi_meminfo struct_info; + + if (!wss.ok) + if (!wss_detect()) + return FALSE; + + if (wss.open) + return FALSE; + + /* Now lock the wss structure in memory */ + struct_info.address = __djgpp_base_address + (unsigned long)&wss; + struct_info.size = sizeof(wss); + if (__dpmi_lock_linear_region(&struct_info)) + return FALSE; + + /* Hook the WSS IRQ */ + wss.irq_handle = + irq_hook(wss.irq, wss_irq, (long)wss_irq_end - (long)wss_irq); + if (!wss.irq_handle) { + __dpmi_unlock_linear_region(&struct_info); + return FALSE; + } + + /* Enable the interrupt */ + irq_enable(wss.irq_handle); + if (wss.irq > 7) + _irq_enable(2); + + wss.open++; + + return TRUE; +} + +/* Finish working with WSS */ +boolean wss_close() +{ + __dpmi_meminfo struct_info; + if (!wss.open) + return FALSE; + + wss.open--; + + /* Stop/free DMA buffer */ + wss_stop_dma(); + + /* Unhook IRQ */ + irq_unhook(wss.irq_handle); + wss.irq_handle = NULL; + + /* Unlock the wss structure */ + struct_info.address = __djgpp_base_address + (unsigned long)&wss; + struct_info.size = sizeof(wss); + __dpmi_unlock_linear_region(&struct_info); + + return TRUE; +} + +/* Adjust frequency rate to nearest WSS available */ +unsigned int wss_adjust_freq(unsigned int freq) +{ + __wss_getrate(&freq); + return freq; +} + +/* Enable/disable speaker output */ +/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ +boolean wss_start_dma(unsigned char mode, unsigned int freq) +{ + int dmabuffsize; + unsigned char format; + + /* Stop DMA transfer if it is enabled */ + wss_stop_dma(); + + /* Sanity check: we support only 8-bit unsigned and 16-bit signed formats */ + if (((mode & WSSMODE_16BITS) && !(mode & WSSMODE_SIGNED)) + || (!(mode & WSSMODE_16BITS) && (mode & WSSMODE_SIGNED))) + return FALSE; + + /* Find the nearest frequency divisor (rate) */ + format = __wss_getrate(&freq); + wss.mode = mode; + + /* Get a DMA buffer enough for a 1sec interval... 4K <= dmasize <= 32K */ + dmabuffsize = freq; + if (mode & WSSMODE_STEREO) + dmabuffsize *= 2; + if (mode & WSSMODE_16BITS) + dmabuffsize *= 2; + dmabuffsize >>= 2; + if (dmabuffsize < 4096) + dmabuffsize = 4096; + if (dmabuffsize > 32768) + dmabuffsize = 32768; + dmabuffsize = (dmabuffsize + 255) & 0xffffff00; + + wss.dma_buff = dma_allocate(wss.dma, dmabuffsize); + if (!wss.dma_buff) + return FALSE; + + /* Fill DMA buffer with silence */ + dmabuffsize = wss.dma_buff->size; + if (mode & WSSMODE_SIGNED) + memset(wss.dma_buff->linear, 0, dmabuffsize); + else + memset(wss.dma_buff->linear, 0x80, dmabuffsize); + + /* Check data size and build a WSSR_PLAY_FORMAT value accordingly */ + wss.samples = dmabuffsize; + if (mode & WSSMODE_16BITS) { + wss.samples >>= 1; + format |= WSSM_16BITS; + } + + if (mode & WSSMODE_STEREO) { + wss.samples >>= 1; + format |= WSSM_STEREO; + } + + if (!__wss_setformat(format)) { + wss_stop_dma(); + return FALSE; + } + + /* Prime DMA for transfer */ + dma_start(wss.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + + /* Tell codec how many samples to transfer */ + wss.samples = (wss.samples >> 1) - 1; + __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff); + __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8); + + /* Tell codec to start transfer */ + __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + + return TRUE; +} + +/* Stop playing from DMA buffer */ +void wss_stop_dma() +{ + if (!wss.dma_buff) + return; + + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + dma_disable(wss.dma); + dma_free(wss.dma_buff); + wss.dma_buff = NULL; +} + +/* Query current position/total size of the DMA buffer */ +void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos) +{ + unsigned int dma_left; + *dma_size = wss.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(wss.dma); + dma_left_test = dma_get_count(wss.dma); + dma_left = dma_get_count(wss.dma); + if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10)) + break; + } + *dma_pos = *dma_size - dma_left; +} + +void wss_output(boolean enable) +{ + if (enable) + wss.curlevel = wss.level; + else + wss.curlevel = 0x3f; + + __wss_outreg(WSSR_MASTER_L, wss.curlevel); + __wss_outreg(WSSR_MASTER_R, wss.curlevel); +} + +void wss_level(int level) +{ + if (level < 0) + level = 0; + if (level > 63) + level = 63; + wss.curlevel = wss.level = level ^ 63; + + __wss_outreg(WSSR_MASTER_L, wss.curlevel); + __wss_outreg(WSSR_MASTER_R, wss.curlevel); +} + +#endif /* DRV_WSS */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/doswss.h b/libs/mikmod/drvdos/doswss.h new file mode 100644 index 0000000..ae77bb5 --- /dev/null +++ b/libs/mikmod/drvdos/doswss.h @@ -0,0 +1,217 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Windows Sound System and compatible soundcards definitions + +==============================================================================*/ + +#ifndef __DOSWSS_H__ +#define __DOSWSS_H__ + +#include "dosdma.h" +#include "dosirq.h" + +#define WSS_ADDR (wss.port + 0x04) +#define WSS_DATA (wss.port + 0x05) +#define WSS_STATUS (wss.port + 0x06) +#define WSS_PIO (wss.port + 0x07) + +/* WSS_ADDR: Bits 0-4 select an internal register to read/write */ +#define WSSR_INPUT_L 0x00 /* Left input control register */ +#define WSSR_INPUT_R 0x01 /* RIght input control register */ +#define WSSR_AUX1_L 0x02 /* Left Aux #1 input control */ +#define WSSR_AUX1_R 0x03 /* Right Aux #1 input control */ +#define WSSR_CD_L 0x04 /* Left Aux #2 input control */ +#define WSSR_CD_R 0x05 /* Right Aux #2 input control */ +#define WSSR_MASTER_L 0x06 /* Left output control */ +#define WSSR_MASTER_R 0x07 /* Right output control */ +#define WSSR_PLAY_FORMAT 0x08 /* Clock and data format */ +#define WSSR_IFACE_CTRL 0x09 /* Interface control */ +#define WSSR_PIN_CTRL 0x0a /* Pin control */ +#define WSSR_TEST_INIT 0x0b /* Test and initialization */ +#define WSSR_MISC_INFO 0x0c /* Miscellaneaous information */ +#define WSSR_LOOPBACK 0x0d /* Digital Mix */ +#define WSSR_COUNT_HIGH 0x0e /* Playback Upper Base Count */ +#define WSSR_COUNT_LOW 0x0f /* Playback Lower Base Count */ +#define WSSR_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ +#define WSSR_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ +#define WSSR_LINE_IN_L 0x12 /* left line input control */ +#define WSSR_LINE_IN_R 0x13 /* right line input control */ +#define WSSR_TIMER_LOW 0x14 /* timer low byte */ +#define WSSR_TIMER_HIGH 0x15 /* timer high byte */ +#define WSSR_IRQ_STATUS 0x18 /* irq status register */ +#define WSSR_MONO_IO_CTRL 0x1a /* mono input/output control */ +#define WSSR_REC_FORMAT 0x1c /* record format */ +#define WSSR_REC_COUNT_HIGH 0x1e /* record upper count */ +#define WSSR_REC_COUNT_LOW 0x1f /* record lower count */ + +/* WSS_ADDR bits 7-5 definition */ +#define WSSM_INIT 0x80 /* Codec is initializing */ +#define WSSM_MCE 0x40 /* Mode change enable */ +#define WSSM_TRD 0x20 /* Transfer Request Disable */ +/* bits 4-0 are indirect register address (0-15) */ + +/* WSS_STATUS bit masks */ +#define WSSM_CUL 0x80 /* Capture data upper/lower byte */ +#define WSSM_CLR 0x40 /* Capture left/right sample */ +#define WSSM_CRDY 0x20 /* Capture data read */ +#define WSSM_SOUR 0x10 /* Playback over/under run error */ +#define WSSM_PUL 0x08 /* Playback upper/lower byte */ +#define WSSM_PLR 0x04 /* Playback left/right sample */ +#define WSSM_PRDY 0x02 /* Playback data register read */ +#define WSSM_INT 0x01 /* interrupt status */ + +/* Definitions for output level registers */ +#define WSSM_MUTE 0x80 /* Mute this output source */ +/* bits 5-0 are left output attenuation select (0-63) */ +/* bits 5-0 are right output attenuation select (0-63) */ + +/* Definitions for clock and data format register (WSSR_PLAY_FORMAT) */ +#define WSSM_STEREO 0x10 /* stero mode */ +#define WSSM_ULAW_8 0x20 /* 8-bit U-law companded */ +#define WSSM_16BITS 0x40 /* 16 bit twos complement data - little endian */ +#define WSSM_ALAW_8 0x60 /* 8-bit A-law companded */ +#define WSSM_16BITS_BE 0xc0 /* 16-bit twos complement data - big endian */ +#define WSSM_ADPCM_16 0xa0 /* 16-bit ADPCM */ +/* Bits 3-1 define frequency divisor */ +#define WSSM_XTAL1 0x00 /* 24.576 crystal */ +#define WSSM_XTAL2 0x01 /* 16.9344 crystal */ + +/* Definitions for interface control register (WSSR_IFACE_CTRL) */ +#define WSSM_CAPTURE_PIO 0x80 /* Capture PIO enable */ +#define WSSM_PLAYBACK_PIO 0x40 /* Playback PIO enable */ +#define WSSM_AUTOCALIB 0x08 /* auto calibrate */ +#define WSSM_SINGLE_DMA 0x04 /* Use single DMA channel */ +#define WSSM_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* Definitions for Pin control register (WSSR_PIN_CTRL) */ +#define WSSM_IRQ_ENABLE 0x02 /* interrupt enable */ +#define WSSM_XCTL1 0x40 /* external control #1 */ +#define WSSM_XCTL0 0x80 /* external control #0 */ + +/* Definitions for WSSR_TEST_INIT register */ +#define WSSM_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ + +/* Definitions for misc control register (WSR_MISC_INFO) */ +#define WSSM_MODE2 0x40 /* MODE 2 */ +#define WSSM_MODE3 0x6c /* MODE 3 - enhanced mode */ + +/* Definitions for codec irq status (WSSR_IRQ_STATUS) */ +#define WSSM_PLAYBACK_IRQ 0x10 +#define WSSM_RECORD_IRQ 0x20 +#define WSSM_TIMER_IRQ 0x40 + +typedef unsigned char boolean; + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* Play mode bits */ +#define WSSMODE_16BITS 0x0001 +#define WSSMODE_STEREO 0x0002 +#define WSSMODE_SIGNED 0x0004 + +/* You can fill some members of this struct (i.e. port,irq,dma) before + * calling wss_detect() or wss_open()... this will ignore environment settings. + */ +typedef struct __wss_state_s { + boolean ok; /* Set if this structure is properly filled */ + int port; /* Base codec port */ + int irq; /* codec IRQ */ + int dma; /* codec DMA */ + struct irq_handle *irq_handle; /* The interrupt handler */ + dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ + unsigned char mode; /* Current WSS mode (WSSMODE_XXX) */ + boolean open; /* Whenever the card has been opened */ + int samples; /* Number of samples in DMA buffer */ + unsigned char level; /* Output level (63..0): doesn't change when mute */ + unsigned char curlevel; /* Current output level (63(min)..0(max)) */ + volatile int irqcount; /* Incremented on each IRQ... for diagnostics */ + void (*timer_callback) (); /* Called TWICE per buffer play */ +} __wss_state; + +extern __wss_state wss; + +/* Wait until codec finishes initialization */ +static inline boolean __wss_wait() +{ + int count; + for (count = 10000; count >= 0; count--) + if (!(inportb(WSS_ADDR) & WSSM_INIT)) + return TRUE; + return FALSE; +} + +static inline void __wss_outreg(unsigned char reg, unsigned char val) +{ + outportb(WSS_ADDR, reg); + outportb(WSS_DATA, val); +} + +static inline unsigned char __wss_inreg(unsigned char reg) +{ + outportb(WSS_ADDR, reg); + return inportb(WSS_DATA); +} + +/* Set some bits in a specific register */ +static inline void __wss_regbit_set(unsigned char reg, unsigned char mask) +{ + outportb(WSS_ADDR, reg); + outportb(WSS_DATA, inportb(WSS_DATA) | mask); +} + +/* Reset some bits in a specific register */ +static inline void __wss_regbit_reset(unsigned char reg, unsigned char mask) +{ + outportb(WSS_ADDR, reg); + outportb(WSS_DATA, inportb(WSS_DATA) & ~mask); +} + +/* Detect whenever WSS is present and fill "wss" structure */ +extern boolean wss_detect(); +/* Reset WSS */ +extern void wss_reset(); +/* Open WSS for usage */ +extern boolean wss_open(); +/* Finish working with WSS */ +extern boolean wss_close(); +/* Enable/disable speaker output */ +extern void wss_output(boolean enable); +/* Adjust frequency rate to nearest WSS available */ +extern unsigned int wss_adjust_freq(unsigned int freq); +/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ +extern boolean wss_start_dma(unsigned char mode, unsigned int freq); +/* Stop playing from DMA buffer */ +extern void wss_stop_dma(); +/* Query current position/total size of the DMA buffer */ +extern void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos); +/* Set output level (0(min)-63(max)) */ +extern void wss_level(int level); + +#endif /* __DOSWSS_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drvdos/libgus.h b/libs/mikmod/drvdos/libgus.h new file mode 100644 index 0000000..0d66ee9 --- /dev/null +++ b/libs/mikmod/drvdos/libgus.h @@ -0,0 +1,402 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Linux libGUS-alike library for DOS, used by drv_ultra.c under DOS. + +==============================================================================*/ + +/* + Current limitations: + - Only a subset of libgus is supported + - Only one GUS card is supported (due to the fact that ULTRASND environment + variable is used) + - No Interwawe support (if IW works the old way, it's ok). +*/ + +#ifndef __LIBGUS_H__ +#define __LIBGUS_H__ + +#include + +#define __LITTLE_ENDIAN + +typedef struct _gus_info_t gus_info_t; +typedef struct _gus_instrument_t gus_instrument_t; +typedef struct _gus_wave_t gus_wave_t; +typedef struct _gus_layer_t gus_layer_t; + +#define GUS_CARD_VERSION_CLASSIC 0x0024 /* revision 2.4 */ +#define GUS_CARD_VERSION_CLASSIC1 0x0034 /* revision 3.4? */ +#define GUS_CARD_VERSION_CLASSIC_ICS 0x0037 /* revision 3.7 (ICS mixer) */ +#define GUS_CARD_VERSION_EXTREME 0x0050 /* GUS Extreme */ +#define GUS_CARD_VERSION_ACE 0x0090 /* GUS ACE */ +#define GUS_CARD_VERSION_MAX 0x00a0 /* GUS MAX - revision 10 */ +#define GUS_CARD_VERSION_MAX1 0x00a1 /* GUS MAX - revision 11 */ +#define GUS_CARD_VERSION_PNP 0x0100 /* GUS Plug & Play */ + +#define GUS_STRU_INFO_F_DB16 0x00000001 /* 16-bit daughter board present */ +#define GUS_STRU_INFO_F_PCM 0x00000004 /* GF1 PCM during SYNTH enabled */ +#define GUS_STRU_INFO_F_ENHANCED 0x00000008 /* InterWave - enhanced mode */ +#define GUS_STRU_INFO_F_DAEMON 0x00000010 /* instrument daemon is present */ + +struct _gus_info_t { + unsigned char id[8]; /* id of this card (warning! maybe unterminated!!!) */ + + unsigned int flags; /* some info flags - see to GUS_STRU_INFO_F_XXXX */ + unsigned int version; /* see to GUS_CARD_VERSION_XXXX constants */ + + unsigned short port; + unsigned short irq; + unsigned short dma1; /* DMA1 - GF1 download & codec record */ + unsigned short dma2; /* DMA2 - GF1 record & codec playback */ + + unsigned int mixing_freq; /* mixing frequency in Hz */ + + unsigned int memory_size; /* in bytes */ + unsigned int memory_free; /* in bytes */ + unsigned int memory_block_8; /* largest free 8-bit block in memory */ + unsigned int memory_block_16; /* largest free 16-bit block in memory */ +}; + +/* struct gus_instrument_t - mode */ + +#define GUS_INSTR_SIMPLE 0x00 /* simple format - for MOD players */ +#define GUS_INSTR_PATCH 0x01 /* old GF1 patch format */ +#define GUS_INSTR_COUNT 2 + +#define GUS_INSTR_F_NORMAL 0x0000 /* normal mode */ +#define GUS_INSTR_F_NOT_FOUND 0x0001 /* instrument can't be loaded */ +#define GUS_INSTR_F_ALIAS 0x0002 /* alias */ +#define GUS_INSTR_F_NOT_LOADED 0x00ff /* instrument not loaded (not found) */ + +#define GUS_INSTR_E_NONE 0x0000 /* exclusion mode - none */ +#define GUS_INSTR_E_SINGLE 0x0001 /* exclude single - single note from this instrument */ +#define GUS_INSTR_E_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define GUS_INSTR_L_NONE 0x0000 /* not layered */ +#define GUS_INSTR_L_ON 0x0001 /* layered */ +#define GUS_INSTR_L_VELOCITY 0x0002 /* layered by velocity */ +#define GUS_INSTR_L_FREQUENCY 0x0003 /* layered by frequency */ + +struct _gus_instrument_t { + union { + unsigned int instrument;/* instrument number */ + } number; + + char *name; /* name of this instrument or NULL */ + + unsigned int mode:8, /* see to GUS_INSTR_XXXX */ + flags:8, /* see to GUS_INSTR_F_XXXX */ + exclusion:4, /* see to GUS_INSTR_E_XXXX */ + layer:4; /* see to GUS_INSTR_L_XXXX */ + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + struct { + unsigned char effect1:4,/* use global effect if available */ + effect2:4; /* use global effect if available */ + unsigned char effect1_depth;/* 0-127 */ + unsigned char effect2_depth;/* 0-127 */ + } patch; + + union { + gus_layer_t *layer; /* first layer */ + unsigned int alias; /* pointer to instrument */ + } info; + gus_instrument_t *next; /* next instrument */ +}; + +struct _gus_layer_t { + unsigned char mode; /* see to GUS_INSTR_XXXX constants */ + + gus_wave_t *wave; + gus_layer_t *next; +}; + +/* bits for format variable in gus_wave_t */ + +#define GUS_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define GUS_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define GUS_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define GUS_WAVE_BACKWARD 0x0004 /* forward mode */ +#define GUS_WAVE_LOOP 0x0008 /* loop mode */ +#define GUS_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define GUS_WAVE_ULAW 0x0020 /* uLaw compressed wave */ +#define GUS_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */ +#define GUS_WAVE_ROM 0x0080 /* wave is in ROM */ +#define GUS_WAVE_DELTA 0x0100 + +#define GUS_WAVE_PATCH_ENVELOPE 0x01 /* envelopes on */ +#define GUS_WAVE_PATCH_SUSTAIN 0x02 /* sustain mode */ + +struct _gus_wave_t { + unsigned char mode; /* see to GUS_INSTR_XXXX constants */ + unsigned char format; /* see to GUS_WAVE_XXXX constants */ + unsigned int size; /* size of waveform in bytes */ + unsigned int start; /* start offset in bytes * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in bytes * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in bytes * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + struct { + unsigned int memory; /* begin of waveform in GUS's memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } begin; + + struct { + unsigned char flags; + unsigned int sample_rate; + unsigned int low_frequency;/* low frequency range for this waveform */ + unsigned int high_frequency;/* high frequency range for this waveform */ + unsigned int root_frequency;/* root frequency for this waveform */ + signed short tune; + unsigned char balance; + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + unsigned short scale_frequency; + unsigned short scale_factor;/* 0-2048 or 0-2 */ + } patch; + + gus_wave_t *next; +}; + +/* defines for gus_memory_reset () */ +#define GUS_DOWNLOAD_MODE_NORMAL 0x0000 +#define GUS_DOWNLOAD_MODE_TEST 0x0001 + +/* + A subset of libgus functions (used by MikMod Ultrasound driver) +*/ +int gus_cards(void); + /* + * return value: number of GUS cards installed in system or + * zero if driver isn't installed + */ +int gus_close(int card); + /* + * close file (gus synthesizer) previously opened with gusOpen function + * return value: zero if success + */ +int gus_do_flush(void); + /* + * return value: zero if command queue was successfully flushed + * in non block mode - number of written bytes + */ +void gus_do_tempo(unsigned int tempo); + /* + * set new tempo + */ +void gus_do_voice_frequency(unsigned char voice, unsigned int freq); + /* + * set voice frequency in Hz + */ +void gus_do_voice_pan(unsigned char voice, unsigned short pan); + /* + * set voice pan (0-16384) (full left - full right) + */ +void gus_do_voice_start(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan); + /* + * start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + */ +void gus_do_voice_start_position(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan, unsigned int position); + /* + * start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + * position : offset to wave in bytes * 16 (lowest 4 bits - fraction) + */ +void gus_do_voice_stop(unsigned char voice, unsigned char mode); + /* + * stop voice + * mode = 0 : stop voice now + * mode = 1 : disable wave loop and finish it + */ +void gus_do_voice_volume(unsigned char voice, unsigned short vol); + /* + * set voice volume level 0-16384 (linear) + */ +void gus_do_wait(unsigned int ticks); + /* + * wait x ticks - this command is block separator + * all commands between blocks are interpreted in the begining of one tick + */ +int gus_get_voice_status(int voice); + /* + * THIS IS NOT A FUNCTION OF ORIGINAL libGUS! + * Return voice status: -1 on error, 0 if voice stopped, 1 if playing + */ +int gus_get_handle(void); + /* + * return value: file handle (descriptor) for /dev/gus + */ +int gus_info(gus_info_t * info, int reread); + /* + * return value: filled info variable with actual values + * (look at gus.h header file for more informations) + * version field: 0x0024 - GUS revision 2.4 + * 0x0035 - GUS revision 3.7 with flipped mixer channels + * 0x0037 - GUS revision 3.7 + * 0x0090 - GUS ACE + * 0x00a0 - GUS MAX revision 10 + * 0x00a1 - GUS MAX revision 11 + * 0x0100 - InterWave (full version) + * flags field: see to GUS_STRU_INFO_F_???? constants (gus.h header file) + * port field: port number (for example 0x220) + * irq field: irq number (for example 11) + * dma1 field: dma1 number (for example 5) + * dma2 field: dma2 number (for example 6) + * note: dma1 and dma2 could be same in case of only one dma channel used + */ +int gus_memory_alloc(gus_instrument_t * instrument); + /* + * input value: look at gus.h for more details about gus_instrument_t structure + * return value: zero if instrument was successfully allocated + */ +int gus_memory_free(gus_instrument_t * instrument); + /* + * input value: look at gus.h for more details about gus_instrument_t structure + * return value: zero if instrument was successfully removed + */ +int gus_memory_size(void); + /* + * return value: gus memory size in bytes + */ +int gus_memory_free_size(void); + /* + * return value: unused gus memory in bytes + * warning: reset function must be called before + */ +int gus_memory_free_block(int w_16bit); + /* + * return value: current largest free block for 8-bit or 16-bit wave + */ +int gus_memory_pack(void); + /* + * return value: zero if success + */ +int gus_memory_reset(int mode); + /* + * input value: see to GUS_DOWNLOAD_MODE_XXXX constants (gus.h) + * return value: zero if samples & instruments was successfully removed + * from GF1 memory manager + */ + +int gus_open(int card, size_t queue_buffer_size, int non_block); + /* + * input values: card number, + * size of command queue buffer (512-1MB) + * buffer is allocated dynamically, + * non block mode + * return value: zero if success + * note 1: this function must be called as first + * open file /dev/gus + * note 2: you can open more cards with one process + */ +int gus_queue_flush(void); + /* + * return value: zero if command queue was successfully flushed + */ +int gus_queue_read_set_size(int items); + /* + * input value: echo buffer size in items (if 0 - erase echo buffer) + */ +int gus_queue_write_set_size(int items); + /* + * input value: write queue size in items (each item have 8 bytes) + */ +int gus_reset(int voices, unsigned int channel_voices); + /* + * input values: active voices and channel voices (for dynamic allocation) + * return value: number of active voices if reset was successfull (GF1 chip active) + */ +int gus_reset_engine_only(void); + /* + * return value: same as gus_reset function + * note: this command doesn't change number of active + * voices and doesn't do hardware reset + */ +int gus_select(int card); + /* + * select specified card + * return value: zero if success + */ +int gus_timer_start(void); + /* + * return value: zero if successfull + */ +int gus_timer_stop(void); + /* + * return value: zero if timer was stoped + */ +int gus_timer_continue(void); + /* + * return value: zero if timer will be continue + */ +int gus_timer_tempo(int ticks); + /* + * return value: zero if setup was success + */ +int gus_timer_base(int base); + /* + * return value: zero if setup was success (default timebase = 100) + */ + +void gus_convert_delta(unsigned int type, unsigned char *dest, + unsigned char *src, size_t size); + /* + * note: dest and src pointers can be equal + */ + +void gus_timer_callback(void (*timer_callback) ()); + /* + * Set a callback to be called once per tempo tick + */ + +int gus_dma_usage (int use); + /* + * Tell GUS library to use/to not use DMA for sample transfer. + * In some environments/on some hardware platforms you will need + * to disable DMA in order to function properly. You should call + * this function before opening the card. + */ + +#endif /* __LIBGUS_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/oldmik/Makefile b/libs/oldmik/Makefile new file mode 100644 index 0000000..433b6e2 --- /dev/null +++ b/libs/oldmik/Makefile @@ -0,0 +1,39 @@ +oloader = mloader.obj load_s3m.obj load_mod.obj load_uni.obj load_mtm.obj & +load_m15.obj load_ult.obj load_stm.obj load_xm.obj +odriver = mdriver.obj drv_nos.obj drv_gus.obj drv_sb.obj drv_ss.obj + +obj = mmio.obj mirq.obj mdma.obj virtch.obj resample.obj munitrk.obj mplayer.obj & +$(oloader) $(odriver) + +alib = mikmod.lib + +opt = -5 -fp5 -otexan +dbg = -d1 +def = + +CC = wcc386 +AS = wasm +CFLAGS = $(dbg) $(opt) $(def) -zq -zu -bt=dos -Isrc +ASFLAGS = -zq -5pr + +$(alib): cflags.occ $(obj) + %write objects.lbc $(obj) + wlib -b -n $@ @objects + +.c: src +.asm: src + +cflags.occ: Makefile + %write $@ $(CFLAGS) + +.c.obj: .autodepend + $(CC) -fo=$@ @cflags.occ $[* + +.asm.obj: + $(AS) -fo=$@ $(ASFLAGS) $[* + +clean: .symbolic + del *.obj + del *.occ + del *.lbc + del $(alib) diff --git a/libs/oldmik/credits.txt b/libs/oldmik/credits.txt new file mode 100644 index 0000000..ba45f84 --- /dev/null +++ b/libs/oldmik/credits.txt @@ -0,0 +1,26 @@ +MikMod 2.10 +=========== + + +Main programmer: + + Jean-Paul Mikkers (MikMak) + + +Additional programming: what: + + Paul Fisher (Rao) everything + Jake Stine (Air Richter) everything + Peter Breitling DJGPP porting + Kodiak interfacing + Sylvain Marchand GNU-C portability, portable loaders + Vince Vu (Judge Dredd) helped with AWE driver + Mike Leibow GUS driver + Jean Phillippe Ajirent EMS routines + Frank Becker Sun audio driver + Roine Gustafsson Dec Alpha AF audio driver + Tom Stokes Os/2 porting + +If you feel you deserve a place here, please remind mikmak@via.nl. +Sometimes email & code gets lost, ya know ! :) + diff --git a/libs/oldmik/src/drv_gus.c b/libs/oldmik/src/drv_gus.c new file mode 100644 index 0000000..7d65432 --- /dev/null +++ b/libs/oldmik/src/drv_gus.c @@ -0,0 +1,1985 @@ +/* + +Name: +DRV_GUS.C + +Description: +Mikmod driver for output on Gravis Ultrasound (native mode i.e. using +the onboard DRAM) + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include +#include +#include "mikmod.h" +#include "mirq.h" + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS defines <<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +/* Special macros for Least-most sig. bytes */ +#define MAKE_MSW(x) ((long)((long)(x)) << 16) +#define LSW(x) ((unsigned int)(x)) +#define MSW(x) ((unsigned int)(((long)x)>>16)) +#define MSB(x) (unsigned char)((unsigned int)(x)>>8) +#define LSB(x) ((unsigned char)(x)) + +/* Make GF1 address for direct chip i/o. */ +#define ADDR_HIGH(x) ((unsigned int)((unsigned int)((x>>7L)&0x1fffL))) +#define ADDR_LOW(x) ((unsigned int)((unsigned int)((x&0x7fL)<<9L))) + +#define JOYSTICK_TIMER (GUS_PORT+0x201) /* 201 */ +#define JOYSTICK_DATA (GUS_PORT+0x201) /* 201 */ + +#define GF1_MIDI_CTRL (GUS_PORT+0x100) /* 3X0 */ +#define GF1_MIDI_DATA (GUS_PORT+0x101) /* 3X1 */ + +#define GF1_PAGE (GUS_PORT+0x102) /* 3X2 */ +#define GF1_REG_SELECT (GUS_PORT+0x103) /* 3X3 */ +#define GF1_VOICE_SELECT (GUS_PORT+0x102) /* 3X3 */ +#define GF1_DATA_LOW (GUS_PORT+0x104) /* 3X4 */ +#define GF1_DATA_HI (GUS_PORT+0x105) /* 3X5 */ +#define GF1_IRQ_STAT (GUS_PORT+0x006) /* 2X6 */ +#define GF1_DRAM (GUS_PORT+0x107) /* 3X7 */ + +#define GF1_MIX_CTRL (GUS_PORT+0x000) /* 2X0 */ +#define GF1_TIMER_CTRL (GUS_PORT+0x008) /* 2X8 */ +#define GF1_TIMER_DATA (GUS_PORT+0x009) /* 2X9 */ +#define GF1_IRQ_CTRL (GUS_PORT+0x00B) /* 2XB */ + +/* The GF1 Hardware clock. */ +#define CLOCK_RATE 9878400L + +/* Mixer control bits. */ +#define ENABLE_LINE 0x01 +#define ENABLE_DAC 0x02 +#define ENABLE_MIC 0x04 + +/* interrupt controller 1 */ +#define CNTRL_8259 0x21 +#define OCR_8259 0x20 +#define EOI 0x20 +#define REARM3 0x2F3 +#define REARM5 0x2F5 + +/* interrupt controller 2 */ +#define CNTRL_M_8259 0x21 +#define CNTRL_M2_8259 0xA1 +#define OCR_2_8259 0xA0 + +#define DMA_CONTROL 0x41 +#define SET_DMA_ADDRESS 0x42 +#define SET_DRAM_LOW 0x43 +#define SET_DRAM_HIGH 0x44 + +#define TIMER_CONTROL 0x45 +#define TIMER1 0x46 +#define TIMER2 0x47 + +#define SET_SAMPLE_RATE 0x48 +#define SAMPLE_CONTROL 0x49 + +#define SET_JOYSTICK 0x4B +#define MASTER_RESET 0x4C + +/* Voice register mapping. */ +#define SET_CONTROL 0x00 +#define SET_FREQUENCY 0x01 +#define SET_START_HIGH 0x02 +#define SET_START_LOW 0x03 +#define SET_END_HIGH 0x04 +#define SET_END_LOW 0x05 +#define SET_VOLUME_RATE 0x06 +#define SET_VOLUME_START 0x07 +#define SET_VOLUME_END 0x08 +#define SET_VOLUME 0x09 +#define SET_ACC_HIGH 0x0a +#define SET_ACC_LOW 0x0b +#define SET_BALANCE 0x0c +#define SET_VOLUME_CONTROL 0x0d +#define SET_VOICES 0x0e + +#define GET_CONTROL 0x80 +#define GET_FREQUENCY 0x81 +#define GET_START_HIGH 0x82 +#define GET_START_LOW 0x83 +#define GET_END_HIGH 0x84 +#define GET_END_LOW 0x85 +#define GET_VOLUME_RATE 0x86 +#define GET_VOLUME_START 0x87 +#define GET_VOLUME_END 0x88 +#define GET_VOLUME 0x89 +#define GET_ACC_HIGH 0x8a +#define GET_ACC_LOW 0x8b +#define GET_BALANCE 0x8c +#define GET_VOLUME_CONTROL 0x8d +#define GET_VOICES 0x8e +#define GET_IRQV 0x8f + +/******************************************************************** + * + * MIDI defines + * + *******************************************************************/ + +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 + +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 + +/******************************************************************** + * + * JOYSTICK defines + * + *******************************************************************/ + +#define JOY_POSITION 0x0f +#define JOY_BUTTONS 0xf0 + +/******************************************************************** + * + * GF1 irq/dma programmable latches + * + *******************************************************************/ + +/* GF1_IRQ_STATUS (port 3X6) */ +#define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +#define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +#define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +#define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +#define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +#define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +#define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + + +/* GF1_MIX_CTRL (port 2X0) */ +#define ENABLE_LINE_IN 0x01 /* 0=enable */ +#define ENABLE_OUTPUT 0x02 /* 0=enable */ +#define ENABLE_MIC_IN 0x04 /* 1=enable */ +#define ENABLE_GF1_IRQ 0x08 /* 1=enable */ +#define GF122 0x10 /* ?? */ +#define ENABLE_MIDI_LOOP 0x20 /* 1=enable loop back */ +#define SELECT_GF1_REG 0x40 /* 0=irq latches */ + /* 1=dma latches */ + +/******************************************************************** + * + * GF1 global registers ($41-$4C) + * + *******************************************************************/ + +/* DMA control register */ +#define DMA_ENABLE 0x01 +#define DMA_READ 0x02 /* 1=read,0=write */ +#define DMA_WIDTH_16 0x04 /* 1=16 bit,0=8 bit (dma chan width)*/ +#define DMA_RATE 0x18 /* 00=fast, 11=slow */ +#define DMA_IRQ_ENABLE 0x20 /* 1=enable */ +#define DMA_IRQ_PENDING 0x40 /* read */ +#define DMA_DATA_16 0x40 /* write (data width) */ +#define DMA_TWOS_COMP 0x80 /* 1=do twos comp */ + +/* These are the xfer rate bits ... */ +#define DMA_R0 0x00 /* Fastest DMA xfer (~650khz) */ +#define DMA_R1 0x08 /* fastest / 2 */ +#define DMA_R2 0x10 /* fastest / 4 */ +#define DMA_R3 0x18 /* Slowest DMA xfer (fastest / 8) */ + +/* SAMPLE control register */ +#define ENABLE_ADC 0x01 +#define ADC_MODE 0x02 /* 0=mono, 1=stereo */ +#define ADC_DMA_WIDTH 0x04 /* 0=8 bit, 1=16 bit */ +#define ADC_IRQ_ENABLE 0x20 /* 1=enable */ +#define ADC_IRQ_PENDING 0x40 /* 1=irq pending */ +#define ADC_TWOS_COMP 0x80 /* 1=do twos comp */ + +/* RESET control register */ +#define GF1_MASTER_RESET 0x01 /* 0=hold in reset */ +#define GF1_OUTPUT_ENABLE 0x02 /* enable output */ +#define GF1_MASTER_IRQ 0x04 /* master IRQ enable */ + +/******************************************************************** + * + * GF1 voice specific registers ($00 - $0E and $80-$8f) + * + *******************************************************************/ + +/* ($0,$80) Voice control register */ +#define VOICE_STOPPED 0x01 /* voice has stopped */ +#define STOP_VOICE 0x02 /* stop voice */ +#define VC_DATA_TYPE 0x04 /* 0=8 bit,1=16 bit */ +#define VC_LOOP_ENABLE 0x08 /* 1=enable */ +#define VC_BI_LOOP 0x10 /* 1=bi directional looping */ +#define VC_WAVE_IRQ 0x20 /* 1=enable voice's wave irq */ +#define VC_DIRECT 0x40 /* 0=increasing,1=decreasing */ +#define VC_IRQ_PENDING 0x80 /* 1=wavetable irq pending */ + +/* ($1,$81) Frequency control */ +/* Bit 0 - Unused */ +/* Bits 1-9 - Fractional portion */ +/* Bits 10-15 - Integer portion */ + +/* ($2,$82) Accumulator start address (high) */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* ($3,$83) Accumulator start address (low) */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* ($4,$84) Accumulator end address (high) */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* ($5,$85) Accumulator end address (low) */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + + +/* ($6,$86) Volume Envelope control register */ +#define VL_RATE_MANTISSA 0x3f +#define VL_RATE_RANGE 0xC0 + +/* ($7,$87) Volume envelope start */ +#define VL_START_MANT 0x0F +#define VL_START_EXP 0xF0 + +/* ($8,$88) Volume envelope end */ +#define VL_END_MANT 0x0F +#define VL_END_EXP 0xF0 + +/* ($9,$89) Current volume register */ +/* Bits 0-3 are unused */ +/* Bits 4-11 - Mantissa of current volume */ +/* Bits 10-15 - Exponent of current volume */ + +/* ($A,$8A) Accumulator value (high) */ +/* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */ + +/* ($B,$8B) Accumulator value (low) */ +/* Bits 0-8 - Fractional portion */ +/* Bits 9-15 - Integer portion of low adress (a6-a0) */ + +/* ($C,$8C) Pan (balance) position */ +/* Bits 0-3 - Balance position 0=full left, 0x0f=full right */ + +/* ($D,$8D) Volume control register */ +#define VOLUME_STOPPED 0x01 /* volume has stopped */ +#define STOP_VOLUME 0x02 /* stop volume */ +#define VC_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */ +#define VL_LOOP_ENABLE 0x08 /* 1=enable */ +#define VL_BI_LOOP 0x10 /* 1=bi directional looping */ +#define VL_WAVE_IRQ 0x20 /* 1=enable voice's wave irq */ +#define VL_DIRECT 0x40 /* 0=increasing,1=decreasing */ +#define VL_IRQ_PENDING 0x80 /* 1=wavetable irq pending */ + +/* ($E,$8E) # of Active voices */ +/* Bits 0-5 - # of active voices -1 */ + +/* ($F,$8F) - Sources of IRQs */ +/* Bits 0-4 - interrupting voice number */ +/* Bit 5 - Always a 1 */ +#define VOICE_VOLUME_IRQ 0x40 /* individual voice irq bit */ +#define VOICE_WAVE_IRQ 0x80 /* individual waveform irq bit */ + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +static UWORD GUS_PORT; +static UBYTE GUS_VOICES; +static UBYTE GUS_TIMER_CTRL; +static UBYTE GUS_TIMER_MASK; +static UBYTE GUS_MIX_IMAGE; + +static UWORD GUS_DRAM_DMA; +static UWORD GUS_ADC_DMA; +static UWORD GUS_GF1_IRQ; +static UWORD GUS_MIDI_IRQ; +static ULONG GUS_POOL; /* dram address of first gusmem pool node */ + +static UBYTE GUS_SELECT; /* currently selected GF1 register */ + +static void (*GUS_TIMER1_FUNC)(void); +static void (*GUS_TIMER2_FUNC)(void); + +#define UltraSelect(x) outportb(GF1_REG_SELECT,GUS_SELECT=x) + +#define USE_ROLLOVER 0 + +/*************************************************************** + * This function will convert the value read from the GF1 registers + * back to a 'real' address. + ***************************************************************/ + +#define MAKE_MS_SWORD( x ) ((unsigned long)((unsigned long)(x)) << 16) + +static ULONG make_physical_address(UWORD low,UWORD high,UBYTE mode) +{ + UWORD lower_16, upper_16; + ULONG ret_address, bit_19_20; + + upper_16 = high >> 9; + lower_16 = ((high & 0x01ff) << 7) | ((low >> 9) & 0x007f); + + ret_address = MAKE_MS_SWORD(upper_16) + lower_16; + + if (mode & VC_DATA_TYPE) + { + bit_19_20 = ret_address & 0xC0000; + ret_address <<= 1; + ret_address &= 0x3ffff; + ret_address |= bit_19_20; + } + + return( ret_address ); +} + +/*************************************************************** + * This function will translate the address if the dma channel + * is a 16 bit channel. This translation is not necessary for + * an 8 bit dma channel. + ***************************************************************/ + +static ULONG convert_to_16bit(ULONG address) +/* unsigned long address; /* 20 bit ultrasound dram address */ +{ + ULONG hold_address; + + hold_address = address; + + /* Convert to 16 translated address. */ + address = address >> 1; + + /* Zero out bit 17. */ + address &= 0x0001ffffL; + + /* Reset bits 18 and 19. */ + address |= (hold_address & 0x000c0000L); + + return(address); +} + + +static void GF1OutB(UBYTE x,UBYTE y) +{ + UltraSelect(x); + outportb(GF1_DATA_HI,y); +} + + +static void GF1OutW(UBYTE x,UWORD y) +{ + UltraSelect(x); + outport(GF1_DATA_LOW,y); +} + + +static UBYTE GF1InB(UBYTE x) +{ + UltraSelect(x); + return inportb(GF1_DATA_HI); +} + + +static UWORD GF1InW(UBYTE x) +{ + UltraSelect(x); + return inport(GF1_DATA_LOW); +} + + +static void gf1_delay(void) +{ + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); +} + + +static UBYTE UltraPeek(ULONG address) +{ + GF1OutW(SET_DRAM_LOW,address); + GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff); /* 8 bits */ + return(inportb(GF1_DRAM)); +} + + +static void UltraPoke(ULONG address,UBYTE data) +{ + GF1OutW(SET_DRAM_LOW,address); + GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff); + outportb(GF1_DRAM,data); +} + + +static void UltraPokeFast(ULONG address,UBYTE *src,ULONG size) +/* + [address,size> doesn't cross 64k page boundary +*/ +{ + if(!size) return; + + UltraSelect(SET_DRAM_HIGH); + outportb(GF1_DATA_HI,(address>>16)&0xff); /* 8 bits */ + UltraSelect(SET_DRAM_LOW); + + while(size--){ + outport(GF1_DATA_LOW,address); + outportb(GF1_DRAM,*src); + address++; + src++; + } +} + + +static void UltraPokeChunk(ULONG address,UBYTE *src,ULONG size) +{ + ULONG todo; + + /* first 'todo' is number of bytes 'till first 64k boundary */ + + todo=0x10000-(address&0xffff); + if(todo>size) todo=size; + + do{ + UltraPokeFast(address,src,todo); + address+=todo; + src+=todo; + size-=todo; + + /* next 'todo' is in chunks of max 64k at once. */ + todo=(size>0xffff) ? 0x10000 : size; + + } while(todo); +} + + +static ULONG UltraPeekLong(ULONG address) +{ + ULONG data; + char *s=(char *)&data; + s[0]=UltraPeek(address); + s[1]=UltraPeek(address+1); + s[2]=UltraPeek(address+2); + s[3]=UltraPeek(address+3); + return data; +} + + +static void UltraPokeLong(ULONG address,ULONG data) +{ + UltraPokeChunk(address,(UBYTE *)&data,4); +} + + +static void UltraEnableOutput(void) +{ + GUS_MIX_IMAGE &= ~ENABLE_OUTPUT; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraDisableOutput(void) +{ + GUS_MIX_IMAGE |= ENABLE_OUTPUT; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraEnableLineIn(void) +{ + GUS_MIX_IMAGE &= ~ENABLE_LINE_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraDisableLineIn(void) +{ + GUS_MIX_IMAGE |= ENABLE_LINE_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraEnableMicIn(void) +{ + GUS_MIX_IMAGE |= ENABLE_MIC_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + + +static void UltraDisableMicIn(void) +{ + GUS_MIX_IMAGE &= ~ENABLE_MIC_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + + +static void UltraReset(int voices) +{ + int v; + + if(voices<14) voices=14; + if(voices>32) voices=32; + + GUS_VOICES=voices; + GUS_TIMER_CTRL=0; + GUS_TIMER_MASK=0; + + UltraPokeLong(0,0); + + GF1OutB(MASTER_RESET,0x00); + for(v=0;v<10;v++) gf1_delay(); + + /* Release Reset and wait */ + GF1OutB(MASTER_RESET,GF1_MASTER_RESET); + for (v=0;v<10;v++) gf1_delay(); + + /* Reset the MIDI port also */ + outportb(GF1_MIDI_CTRL,MIDI_RESET); + for (v=0;v<10;v++) gf1_delay(); + outportb(GF1_MIDI_CTRL,0x00); + + /* Clear all interrupts. */ + GF1OutB(DMA_CONTROL,0x00); + GF1OutB(TIMER_CONTROL,0x00); + GF1OutB(SAMPLE_CONTROL,0x00); + + /* Set the number of active voices */ + GF1OutB(SET_VOICES,((voices-1) | 0xC0)); + + /* Clear interrupts on voices. */ + /* Reading the status ports will clear the irqs. */ + + inportb(GF1_IRQ_STAT); + + GF1InB(DMA_CONTROL); + GF1InB(SAMPLE_CONTROL); + GF1InB(GET_IRQV); + + for(v=0;v=reqsize){ + + /* it fits, so we're allocating the first + 'size' bytes of this node */ + + /* find new node position and size */ + + newnode=curnode+reqsize; + newsize=cursize-reqsize; + + /* create a new freenode if needed: */ + + if(newsize>=8){ + UltraPokeLong(newnode,newsize); + UltraPokeLong(newnode+4,succ); + succ=newnode; + } + + /* link prednode & succnode */ + + if(pred) + UltraPokeLong(pred+4,succ); + else + GUS_POOL=succ; + + /* store size of allocated memory block in block itself: */ + + UltraPokeLong(curnode,reqsize); + return curnode; + } + + /* doesn't fit, try next node */ + curnode=succ; + } + return 0; +} + + + +static ULONG UltraMalloc16(ULONG reqsize) +/* + Allocates a free block of gus memory, suited for 16 bit samples i.e. + smaller than 256k and doesn't cross a 256k page. +*/ +{ + ULONG p,spage,epage; + + if(reqsize>262144) return 0; + + /* round size to 32 bytes */ + + reqsize+=31; + reqsize&=-32L; + + p=UltraMalloc(reqsize); + spage=p>>18; + epage=(p+reqsize-1)>>18; + + if(p && spage!=epage){ + ULONG newp,esize; + + /* free the second part of the block, and try again */ + + esize=(p+reqsize)-(epage<<18); + UltraFree(esize,epage<<18); + + newp=UltraMalloc16(reqsize); + + /* free first part of the previous block */ + + UltraFree(reqsize-esize,p); + p=newp; + } + + return p; +} + + + +static void UltraMemInit(void) +{ + UWORD memsize; + GUS_POOL=32; + memsize=UltraSizeDram(); + UltraPokeLong(GUS_POOL,((ULONG)memsize<<10)-32); + UltraPokeLong(GUS_POOL+4,0); +} + + +static void UltraNumVoices(int voices) +{ + UltraDisableLineIn(); + UltraDisableMicIn(); + UltraDisableOutput(); + UltraReset(voices); + UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ); +} + + +static void interrupt gf1handler(MIRQARGS) +{ + UBYTE irq_source; + UBYTE oldselect=GUS_SELECT; + + while(irq_source=inportb(GF1_IRQ_STAT)){ + +/* if(irq_source & DMA_TC_IRQ){ + no provisions for DMA-ready irq yet + } + + if(irq_source & (MIDI_TX_IRQ|MIDI_RX_IRQ)){ + no provisions for MIDI-ready irq yet + } +*/ + if (irq_source & GF1_TIMER1_IRQ){ + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL & ~0x04); + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); + if(GUS_TIMER1_FUNC!=NULL) GUS_TIMER1_FUNC(); + } + + if (irq_source & GF1_TIMER2_IRQ){ + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL & ~0x08); + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); + if(GUS_TIMER2_FUNC!=NULL) GUS_TIMER2_FUNC(); + } + +/* if (irq_source & (WAVETABLE_IRQ | ENVELOPE_IRQ)){ + no wavetable or envelope irq provisions yet + } +*/ + } + + MIrq_EOI(GUS_GF1_IRQ); + UltraSelect(oldselect); +} + + +static PVI oldhandler; +typedef void (*PFV)(void); + + +static PFV UltraTimer1Handler(PFV handler) +{ + PFV old=GUS_TIMER1_FUNC; + GUS_TIMER1_FUNC=handler; + return old; +} + + +static PFV UltraTimer2Handler(PFV handler) +{ + PFV old=GUS_TIMER1_FUNC; + GUS_TIMER1_FUNC=handler; + return old; +} + + +static void UltraOpen(int voices) +{ + GUS_MIX_IMAGE=0x0b; + GUS_TIMER1_FUNC=NULL; + GUS_TIMER2_FUNC=NULL; + + UltraDisableLineIn(); + UltraDisableMicIn(); + UltraDisableOutput(); + + UltraReset(voices); + UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ); + UltraMemInit(); + oldhandler=MIrq_SetHandler(GUS_GF1_IRQ,gf1handler); + MIrq_OnOff(GUS_GF1_IRQ,1); +} + + +static void UltraClose(void) +{ + MIrq_OnOff(GUS_GF1_IRQ,0); + MIrq_SetHandler(GUS_GF1_IRQ,oldhandler); + UltraDisableOutput(); + UltraDisableLineIn(); + UltraDisableMicIn(); + UltraReset(14); +} + + +static void UltraSelectVoice(UBYTE voice) +{ + /* Make sure was are talking to proper voice */ + outportb(GF1_VOICE_SELECT,voice); +} + + + +static void UltraSetVoiceEnd(ULONG end) +{ + ULONG phys_end; + UBYTE data; + + data=GF1InB(GET_CONTROL); + + phys_end=(data&VC_DATA_TYPE)?convert_to_16bit(end):end; + + /* Set end address of buffer */ + GF1OutW(SET_END_LOW,ADDR_LOW(phys_end)); + GF1OutW(SET_END_HIGH,ADDR_HIGH(phys_end)); + + data&=~(VC_IRQ_PENDING|VOICE_STOPPED|STOP_VOICE); + + GF1OutB(SET_CONTROL,data); + gf1_delay(); + + GF1OutB(SET_CONTROL,data); +} + + +/* The formula for this table is: + 1,000,000 / (1.619695497 * # of active voices) + + The 1.619695497 is calculated by knowing that 14 voices + gives exactly 44.1 Khz. Therefore, + 1,000,000 / (X * 14) = 44100 + X = 1.619695497 +*/ + +static UWORD freq_divisor[19] = { + 44100, /* 14 active voices */ + 41160, /* 15 active voices */ + 38587, /* 16 active voices */ + 36317, /* 17 active voices */ + 34300, /* 18 active voices */ + 32494, /* 19 active voices */ + 30870, /* 20 active voices */ + 29400, /* 21 active voices */ + 28063, /* 22 active voices */ + 26843, /* 23 active voices */ + 25725, /* 24 active voices */ + 24696, /* 25 active voices */ + 23746, /* 26 active voices */ + 22866, /* 27 active voices */ + 22050, /* 28 active voices */ + 21289, /* 29 active voices */ + 20580, /* 30 active voices */ + 19916, /* 31 active voices */ + 19293} /* 32 active voices */ +; + +static void UltraSetFrequency(ULONG speed_khz) +{ + UWORD fc; + ULONG temp; + + /* FC is calculated based on the # of active voices ... */ + temp=freq_divisor[GUS_VOICES-14]; + + fc=(((speed_khz<<9L)+(temp>>1L))/temp); + GF1OutW(SET_FREQUENCY,fc<<1); +} + + +static void UltraSetLoopMode(UBYTE mode) +{ + UBYTE data; + UBYTE vmode; + + /* set/reset the rollover bit as per user request */ + + vmode=GF1InB(GET_VOLUME_CONTROL); + + if(mode&USE_ROLLOVER) + vmode|=VC_ROLLOVER; + else + vmode&=~VC_ROLLOVER; + + GF1OutB(SET_VOLUME_CONTROL,vmode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,vmode); + + data=GF1InB(GET_CONTROL); + + data&=~(VC_WAVE_IRQ|VC_BI_LOOP|VC_LOOP_ENABLE); /* isolate the bits */ + mode&=VC_WAVE_IRQ|VC_BI_LOOP|VC_LOOP_ENABLE; /* no bad bits passed in */ + data|=mode; /* turn on proper bits ... */ + + GF1OutB(SET_CONTROL,data); + gf1_delay(); + GF1OutB(SET_CONTROL,data); +} + + +static ULONG UltraReadVoice(void) +{ + UWORD count_low,count_high; + ULONG acc; + UBYTE mode; + + /* Get the high & low portion of the accumulator */ + count_high = GF1InW(GET_ACC_HIGH); + count_low = GF1InW(GET_ACC_LOW); + + /* convert from UltraSound's format to a physical address */ + + mode=GF1InB(GET_CONTROL); + + acc=make_physical_address(count_low,count_high,mode); + acc&=0xfffffL; /* Only 20 bits please ... */ + + return(acc); +} + + + +static void UltraSetVoice(ULONG location) +{ + ULONG phys_loc; + UBYTE data; + + data=GF1InB(GET_CONTROL); + + phys_loc=(data&VC_DATA_TYPE)?convert_to_16bit(location):location; + + /* First set accumulator to beginning of data */ + GF1OutW(SET_ACC_HIGH,ADDR_HIGH(phys_loc)); + GF1OutW(SET_ACC_LOW,ADDR_LOW(phys_loc)); +} + + + +static UBYTE UltraPrimeVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode) +{ + ULONG phys_start,phys_end; + ULONG phys_begin; + ULONG temp; + UBYTE vmode; + + /* if start is greater than end, flip 'em and turn on */ + /* decrementing addresses */ + if(start>end){ + temp=start; + start=end; + end=temp; + mode|=VC_DIRECT; + } + + /* if 16 bit data, must convert addresses */ + if(mode&VC_DATA_TYPE){ + phys_begin = convert_to_16bit(begin); + phys_start = convert_to_16bit(start); + phys_end = convert_to_16bit(end); + } + else{ + phys_begin = begin; + phys_start = start; + phys_end = end; + } + + /* set/reset the rollover bit as per user request */ + vmode=GF1InB(GET_VOLUME_CONTROL); + + if(mode&USE_ROLLOVER) + vmode |= VC_ROLLOVER; + else + vmode &= ~VC_ROLLOVER; + + GF1OutB(SET_VOLUME_CONTROL,vmode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,vmode); + + /* First set accumulator to beginning of data */ + GF1OutW(SET_ACC_LOW,ADDR_LOW(phys_begin)); + GF1OutW(SET_ACC_HIGH,ADDR_HIGH(phys_begin)); + + /* Set start loop address of buffer */ + GF1OutW(SET_START_HIGH,ADDR_HIGH(phys_start)); + GF1OutW(SET_START_LOW,ADDR_LOW(phys_start)); + + /* Set end address of buffer */ + GF1OutW(SET_END_HIGH,ADDR_HIGH(phys_end)); + GF1OutW(SET_END_LOW,ADDR_LOW(phys_end)); + return(mode); +} + + +static void UltraGoVoice(UBYTE mode) +{ + mode&=~(VOICE_STOPPED|STOP_VOICE); /* turn 'stop' bits off ... */ + + /* NOTE: no irq's from the voice ... */ + + GF1OutB(SET_CONTROL,mode); + gf1_delay(); + GF1OutB(SET_CONTROL,mode); +} + + +/********************************************************************** + * + * This function will start playing a wave out of DRAM. It assumes + * the playback rate, volume & balance have been set up before ... + * + *********************************************************************/ + +static void UltraStartVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode) +{ + mode=UltraPrimeVoice(begin,start,end,mode); + UltraGoVoice(mode); +} + + + +/*************************************************************** + * This function will stop a given voices output. Note that a delay + * is necessary after the stop is issued to ensure the self- + * modifying bits aren't a problem. + ***************************************************************/ + +static void UltraStopVoice(void) +{ + UBYTE data; + + /* turn off the roll over bit first ... */ + + data=GF1InB(GET_VOLUME_CONTROL); + data&=~VC_ROLLOVER; + + GF1OutB(SET_VOLUME_CONTROL,data); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,data); + + /* Now stop the voice */ + + data=GF1InB(GET_CONTROL); + data&=~VC_WAVE_IRQ; /* disable irq's & stop voice .. */ + data|=VOICE_STOPPED|STOP_VOICE; + + GF1OutB(SET_CONTROL,data); /* turn it off */ + gf1_delay(); + GF1OutB(SET_CONTROL,data); +} + + +static int UltraVoiceStopped(void) +{ + return(GF1InB(GET_CONTROL) & (VOICE_STOPPED|STOP_VOICE)); +} + + +static void UltraSetBalance(UBYTE pan) +{ + GF1OutB(SET_BALANCE,pan&0xf); +} + + +static void UltraSetVolume(UWORD volume) +{ + GF1OutW(SET_VOLUME,volume<<4); +} + + +static UWORD UltraReadVolume(void) +{ + return(GF1InW(GET_VOLUME)>>4); +} + + +static void UltraStopVolume(void) +{ + UBYTE vmode; + + vmode=GF1InB(GET_VOLUME_CONTROL); + vmode|=(VOLUME_STOPPED|STOP_VOLUME); + + GF1OutB(SET_VOLUME_CONTROL,vmode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,vmode); +} + + +static void UltraRampVolume(UWORD start,UWORD end,UBYTE rate,UBYTE mode) +{ + UWORD begin; + UBYTE vmode; + + if(start==end) return; +/********************************************************************* + * If the start volume is greater than the end volume, flip them and + * turn on decreasing volume. Note that the GF1 requires that the + * programmed start volume MUST be less than or equal to the end + * volume. + *********************************************************************/ + /* Don't let bad bits thru ... */ + mode&=~(VL_IRQ_PENDING|VC_ROLLOVER|STOP_VOLUME|VOLUME_STOPPED); + + begin = start; + + if(start>end){ + /* flip start & end if decreasing numbers ... */ + start = end; + end = begin; + mode |= VC_DIRECT; /* decreasing volumes */ + } + + /* looping below 64 or greater that 4032 can cause strange things */ + if(start<64) start=64; + if(end>4032) end=4032; + + GF1OutB(SET_VOLUME_RATE,rate); + GF1OutB(SET_VOLUME_START,start>>4); + GF1OutB(SET_VOLUME_END,end>>4); + + /* Also MUST set the current volume to the start volume ... */ + UltraSetVolume(begin); + + vmode=GF1InB(GET_VOLUME_CONTROL); + + if(vmode&VC_ROLLOVER) mode|=VC_ROLLOVER; + + /* start 'er up !!! */ + GF1OutB(SET_VOLUME_CONTROL,mode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,mode); +} + + +static void UltraVectorVolume(UWORD end,UBYTE rate,UBYTE mode) +{ + UltraStopVolume(); + UltraRampVolume(UltraReadVolume(),end,rate,mode); +} + + +static int UltraVolumeStopped(void) +{ + return(GF1InB(GET_VOLUME_CONTROL) & (VOLUME_STOPPED|STOP_VOLUME)); +} + + +static UWORD vol_rates[19]={ +23,24,26,28,29,31,32,34,36,37,39,40,42,44,45,47,49,50,52 +}; + + +static UBYTE UltraCalcRate(UWORD start,UWORD end,ULONG mil_secs) +{ + ULONG gap,mic_secs; + UWORD i,range,increment; + UBYTE rate_val; + UWORD value; + + gap = (start>end) ? (start-end) : (end-start); + mic_secs = (mil_secs * 1000L)/gap; + +/* OK. We now have the # of microseconds for each update to go from */ +/* A to B in X milliseconds. See what the best fit is in the table */ + + range = 4; + value = vol_rates[GUS_VOICES-14]; + + for(i=0;i<3;i++){ + if(mic_secs>1)) / mic_secs); + } + + rate_val=range<<6; + rate_val|=(increment&0x3F); + return(rate_val); +} + + +static UWORD _gf1_volumes[512] = { + 0x0000, + 0x0700, 0x07ff, 0x0880, 0x08ff, 0x0940, 0x0980, 0x09c0, 0x09ff, 0x0a20, + 0x0a40, 0x0a60, 0x0a80, 0x0aa0, 0x0ac0, 0x0ae0, 0x0aff, 0x0b10, 0x0b20, + 0x0b30, 0x0b40, 0x0b50, 0x0b60, 0x0b70, 0x0b80, 0x0b90, 0x0ba0, 0x0bb0, + 0x0bc0, 0x0bd0, 0x0be0, 0x0bf0, 0x0bff, 0x0c08, 0x0c10, 0x0c18, 0x0c20, + 0x0c28, 0x0c30, 0x0c38, 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68, + 0x0c70, 0x0c78, 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0, + 0x0cb8, 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8, + 0x0cff, 0x0d04, 0x0d08, 0x0d0c, 0x0d10, 0x0d14, 0x0d18, 0x0d1c, 0x0d20, + 0x0d24, 0x0d28, 0x0d2c, 0x0d30, 0x0d34, 0x0d38, 0x0d3c, 0x0d40, 0x0d44, + 0x0d48, 0x0d4c, 0x0d50, 0x0d54, 0x0d58, 0x0d5c, 0x0d60, 0x0d64, 0x0d68, + 0x0d6c, 0x0d70, 0x0d74, 0x0d78, 0x0d7c, 0x0d80, 0x0d84, 0x0d88, 0x0d8c, + 0x0d90, 0x0d94, 0x0d98, 0x0d9c, 0x0da0, 0x0da4, 0x0da8, 0x0dac, 0x0db0, + 0x0db4, 0x0db8, 0x0dbc, 0x0dc0, 0x0dc4, 0x0dc8, 0x0dcc, 0x0dd0, 0x0dd4, + 0x0dd8, 0x0ddc, 0x0de0, 0x0de4, 0x0de8, 0x0dec, 0x0df0, 0x0df4, 0x0df8, + 0x0dfc, 0x0dff, 0x0e02, 0x0e04, 0x0e06, 0x0e08, 0x0e0a, 0x0e0c, 0x0e0e, + 0x0e10, 0x0e12, 0x0e14, 0x0e16, 0x0e18, 0x0e1a, 0x0e1c, 0x0e1e, 0x0e20, + 0x0e22, 0x0e24, 0x0e26, 0x0e28, 0x0e2a, 0x0e2c, 0x0e2e, 0x0e30, 0x0e32, + 0x0e34, 0x0e36, 0x0e38, 0x0e3a, 0x0e3c, 0x0e3e, 0x0e40, 0x0e42, 0x0e44, + 0x0e46, 0x0e48, 0x0e4a, 0x0e4c, 0x0e4e, 0x0e50, 0x0e52, 0x0e54, 0x0e56, + 0x0e58, 0x0e5a, 0x0e5c, 0x0e5e, 0x0e60, 0x0e62, 0x0e64, 0x0e66, 0x0e68, + 0x0e6a, 0x0e6c, 0x0e6e, 0x0e70, 0x0e72, 0x0e74, 0x0e76, 0x0e78, 0x0e7a, + 0x0e7c, 0x0e7e, 0x0e80, 0x0e82, 0x0e84, 0x0e86, 0x0e88, 0x0e8a, 0x0e8c, + 0x0e8e, 0x0e90, 0x0e92, 0x0e94, 0x0e96, 0x0e98, 0x0e9a, 0x0e9c, 0x0e9e, + 0x0ea0, 0x0ea2, 0x0ea4, 0x0ea6, 0x0ea8, 0x0eaa, 0x0eac, 0x0eae, 0x0eb0, + 0x0eb2, 0x0eb4, 0x0eb6, 0x0eb8, 0x0eba, 0x0ebc, 0x0ebe, 0x0ec0, 0x0ec2, + 0x0ec4, 0x0ec6, 0x0ec8, 0x0eca, 0x0ecc, 0x0ece, 0x0ed0, 0x0ed2, 0x0ed4, + 0x0ed6, 0x0ed8, 0x0eda, 0x0edc, 0x0ede, 0x0ee0, 0x0ee2, 0x0ee4, 0x0ee6, + 0x0ee8, 0x0eea, 0x0eec, 0x0eee, 0x0ef0, 0x0ef2, 0x0ef4, 0x0ef6, 0x0ef8, + 0x0efa, 0x0efc, 0x0efe, 0x0eff, 0x0f01, 0x0f02, 0x0f03, 0x0f04, 0x0f05, + 0x0f06, 0x0f07, 0x0f08, 0x0f09, 0x0f0a, 0x0f0b, 0x0f0c, 0x0f0d, 0x0f0e, + 0x0f0f, 0x0f10, 0x0f11, 0x0f12, 0x0f13, 0x0f14, 0x0f15, 0x0f16, 0x0f17, + 0x0f18, 0x0f19, 0x0f1a, 0x0f1b, 0x0f1c, 0x0f1d, 0x0f1e, 0x0f1f, 0x0f20, + 0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29, + 0x0f2a, 0x0f2b, 0x0f2c, 0x0f2d, 0x0f2e, 0x0f2f, 0x0f30, 0x0f31, 0x0f32, + 0x0f33, 0x0f34, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3a, 0x0f3b, + 0x0f3c, 0x0f3d, 0x0f3e, 0x0f3f, 0x0f40, 0x0f41, 0x0f42, 0x0f43, 0x0f44, + 0x0f45, 0x0f46, 0x0f47, 0x0f48, 0x0f49, 0x0f4a, 0x0f4b, 0x0f4c, 0x0f4d, + 0x0f4e, 0x0f4f, 0x0f50, 0x0f51, 0x0f52, 0x0f53, 0x0f54, 0x0f55, 0x0f56, + 0x0f57, 0x0f58, 0x0f59, 0x0f5a, 0x0f5b, 0x0f5c, 0x0f5d, 0x0f5e, 0x0f5f, + 0x0f60, 0x0f61, 0x0f62, 0x0f63, 0x0f64, 0x0f65, 0x0f66, 0x0f67, 0x0f68, + 0x0f69, 0x0f6a, 0x0f6b, 0x0f6c, 0x0f6d, 0x0f6e, 0x0f6f, 0x0f70, 0x0f71, + 0x0f72, 0x0f73, 0x0f74, 0x0f75, 0x0f76, 0x0f77, 0x0f78, 0x0f79, 0x0f7a, + 0x0f7b, 0x0f7c, 0x0f7d, 0x0f7e, 0x0f7f, 0x0f80, 0x0f81, 0x0f82, 0x0f83, + 0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c, + 0x0f8d, 0x0f8e, 0x0f8f, 0x0f90, 0x0f91, 0x0f92, 0x0f93, 0x0f94, 0x0f95, + 0x0f96, 0x0f97, 0x0f98, 0x0f99, 0x0f9a, 0x0f9b, 0x0f9c, 0x0f9d, 0x0f9e, + 0x0f9f, 0x0fa0, 0x0fa1, 0x0fa2, 0x0fa3, 0x0fa4, 0x0fa5, 0x0fa6, 0x0fa7, + 0x0fa8, 0x0fa9, 0x0faa, 0x0fab, 0x0fac, 0x0fad, 0x0fae, 0x0faf, 0x0fb0, + 0x0fb1, 0x0fb2, 0x0fb3, 0x0fb4, 0x0fb5, 0x0fb6, 0x0fb7, 0x0fb8, 0x0fb9, + 0x0fba, 0x0fbb, 0x0fbc, 0x0fbd, 0x0fbe, 0x0fbf, 0x0fc0, 0x0fc1, 0x0fc2, + 0x0fc3, 0x0fc4, 0x0fc5, 0x0fc6, 0x0fc7, 0x0fc8, 0x0fc9, 0x0fca, 0x0fcb, + 0x0fcc, 0x0fcd, 0x0fce, 0x0fcf, 0x0fd0, 0x0fd1, 0x0fd2, 0x0fd3, 0x0fd4, + 0x0fd5, 0x0fd6, 0x0fd7, 0x0fd8, 0x0fd9, 0x0fda, 0x0fdb, 0x0fdc, 0x0fdd, + 0x0fde, 0x0fdf, 0x0fe0, 0x0fe1, 0x0fe2, 0x0fe3, 0x0fe4, 0x0fe5, 0x0fe6, + 0x0fe7, 0x0fe8, 0x0fe9, 0x0fea, 0x0feb, 0x0fec, 0x0fed, 0x0fee, 0x0fef, + 0x0ff0, 0x0ff1, 0x0ff2, 0x0ff3, 0x0ff4, 0x0ff5, 0x0ff6, 0x0ff7, 0x0ff8, + 0x0ff9, 0x0ffa, 0x0ffb, 0x0ffc, 0x0ffd, 0x0ffe, 0x0fff +}; + + +static void UltraSetLinearVolume(UWORD index) +{ + UltraSetVolume(_gf1_volumes[index]); +} + + +static void UltraRampLinearVolume(UWORD start_idx,UWORD end_idx,ULONG msecs,UBYTE mode) +{ + UWORD start,end; + UBYTE rate; + + /* Ramp from start to end in x milliseconds */ + + start = _gf1_volumes[start_idx]; + end = _gf1_volumes[end_idx]; + + /* calculate a rate to get from start to end in msec milliseconds .. */ + rate=UltraCalcRate(start,end,msecs); + UltraRampVolume(start,end,rate,mode); +} + + +static void UltraVectorLinearVolume(UWORD end_idx,UBYTE rate,UBYTE mode) +{ + UltraStopVolume(); + UltraRampVolume(UltraReadVolume(),_gf1_volumes[end_idx],rate,mode); +} + + +static void UltraStartTimer(UBYTE timer,UBYTE time) +{ + UBYTE temp; + + if (timer == 1){ + GUS_TIMER_CTRL |= 0x04; + GUS_TIMER_MASK |= 0x01; + temp = TIMER1; + } + else{ + GUS_TIMER_CTRL |= 0x08; + GUS_TIMER_MASK |= 0x02; + temp = TIMER2; + } + +/* ENTER_CRITICAL; */ + + time = 256 - time; + + GF1OutB(temp,time); /* set timer speed */ + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); /* enable timer interrupt on gf1 */ + outportb(GF1_TIMER_CTRL,0x04); /* select timer stuff */ + outportb(GF1_TIMER_DATA,GUS_TIMER_MASK); /* start the timers */ + +/* LEAVE_CRITICAL; */ +} + + +static void UltraStopTimer(int timer) +{ + if (timer == 1){ + GUS_TIMER_CTRL &= ~0x04; + GUS_TIMER_MASK &= ~0x01; + } + else{ + GUS_TIMER_CTRL &= ~0x08; + GUS_TIMER_MASK &= ~0x02; + } + +/* ENTER_CRITICAL; */ + + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); /* disable timer interrupts */ + outportb(GF1_TIMER_CTRL,0x04); /* select timer stuff */ + outportb(GF1_TIMER_DATA,GUS_TIMER_MASK | 0x80); + +/* LEAVE_CRITICAL; */ +} + + +static BOOL UltraTimerStopped(UBYTE timer) +{ + UBYTE value; + UBYTE temp; + + if (timer == 1) + temp = 0x40; + else + temp = 0x20; + + value = (inportb(GF1_TIMER_CTRL)) & temp; + + return(value); +} + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> The actual GUS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + + +static ULONG Ultra[MAXSAMPLEHANDLES]; +static ULONG Ultrs[MAXSAMPLEHANDLES]; + +/* Ultra[] holds the sample dram adresses + of the samples of a module */ + +typedef struct{ + UBYTE kick; /* =1 -> sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + UBYTE vol; /* current volume */ + UBYTE pan; /* current panning position */ +} GHOLD; + +static GHOLD ghld[32]; + + +static UBYTE timeskip; +static UBYTE timecount; +static UBYTE GUS_BPM; + + +void UltraSetBPM(UBYTE bpm) +{ + /* The player routine has to be called (bpm*50)/125 times a second, + so the interval between calls takes 125/(bpm*50) seconds (amazing!). + + The Timer1 handler has a resolution of 80 microseconds. + + So the timer value to program: + + (125/(bpm*50)) / 80e-6 = 31250/bpm + */ + UWORD rate=31250/bpm; + + timeskip=0; + timecount=0; + + while(rate>255){ + rate>>=1; + timeskip++; + } + UltraStartTimer(1,rate); +} + + + +void GUS_Update(void) +{ + UBYTE t; + GHOLD *aud; + UWORD vol; + ULONG base,start,size,reppos,repend; + + UWORD curvol,bigvol=0,bigvoc=0; + + if(timecountkick){ + curvol=UltraReadVolume(); + if(bigvol<=curvol){ + bigvol=curvol; + bigvoc=t; + } + UltraVectorLinearVolume(0,0x3f,0); + } + } + +/* while(!UltraVolumeStopped(bigvoc)); */ + + for(t=0;tkick){ + aud->kick=0; + + base=Ultra[aud->handle]; + + start=aud->start; + reppos=aud->reppos; + repend=aud->repend; + size=aud->size; + + if(aud->flags&SF_16BITS){ + start<<=1; + reppos<<=1; + repend<<=1; + size<<=1; + } + + /* Stop current sample and start a new one */ + + UltraStopVoice(); + + UltraSetFrequency(aud->frq); + UltraVectorLinearVolume(6U*aud->vol,0x3f,0); + UltraSetBalance(aud->pan>>4); + + if(aud->flags&SF_LOOP){ + + /* Start a looping sample */ + + UltraStartVoice(base+start, + base+reppos, + base+repend,0x8|((aud->flags&SF_16BITS)?4:0)| + ((aud->flags&SF_BIDI)?16:0)); + } + else{ + + /* Start a one-shot sample */ + + UltraStartVoice(base+start, + base+start, + base+size+2,(aud->flags&SF_16BITS)?4:0); + } + } + else{ + UltraSetFrequency(aud->frq); + UltraVectorLinearVolume(6U*aud->vol,0x3f,0); + UltraSetBalance(aud->pan>>4); + } + } +} + + +SWORD GUS_Load(FILE *fp,ULONG length,ULONG loopstart,ULONG loopend,UWORD flags) +/* + callback routine for the MODLOAD module. +*/ +{ + int handle,t; + long p,l; + + SL_Init(fp,flags,flags|SF_SIGNED); + + /* Find empty slot to put sample address in */ + + for(handle=0;handle0){ + static UBYTE buffer[1024]; + long todo; + + todo=(l>1024) ? 1024 : l; + + SL_Load(buffer,todo); + + UltraPokeChunk(p,buffer,todo); + + p+=todo; + l-=todo; + } + + if(flags&SF_LOOP && !(flags&SF_BIDI)){ /* looping sample ? */ + + /* Anticlick for looping samples: + Copy the first bytes in the loop + beyond the end of the loop */ + + for(t=0;t<8;t++){ + UltraPoke(Ultra[handle]+loopend+t, + UltraPeek(Ultra[handle]+loopstart+t)); + } + } + else{ + + /* Anticlick for one-shot samples: + Zero the bytes beyond the end of the sample. + */ + + for(t=0;t<8;t++){ + UltraPoke(Ultra[handle]+length+t,0); + } + } + + return handle; +} + + + +void GUS_UnLoad(SWORD handle) +/* + callback routine to unload samples + + smp :sampleinfo of sample that is being freed +*/ +{ + UltraFree(Ultrs[handle],Ultra[handle]); + Ultra[handle]=0; +} + + + + +BOOL GUS_Init(void) +{ + ULONG p1,p2; + int irq; + + if(!(md_mode&DMODE_16BITS)){ + md_mode|=DMODE_16BITS; /* gus can't do 8 bit mixing */ + } + + if(!(md_mode&DMODE_STEREO)){ + md_mode|=DMODE_STEREO; /* gus can't do mono mixing */ + } + + if(!UltraDetect()){ + myerr="Couldn't detect gus, please check env. string"; + return 0; + } + + UltraOpen(14); + UltraTimer1Handler(GUS_Update); + + return 1; +} + + + +void GUS_Exit(void) +{ + UltraClose(); +} + + + +void GUS_PlayStart(void) +{ + int t; + for(t=0;t=size) return; + + if(flags&SF_LOOP){ + if(repend>size) repend=size; /* repend can't be bigger than size */ + } + + ghld[voice].flags=flags; + ghld[voice].handle=handle; + ghld[voice].start=start; + ghld[voice].size=size; + ghld[voice].reppos=reppos; + ghld[voice].repend=repend; + ghld[voice].kick=1; +} + +void GUS_Dummy(void) +{ +} + + +DRIVER drv_gus={ + NULL, + "Gravis Ultrasound", + "MikMod GUS Driver v2.1 (uses gus timer interrupt)", + GUS_IsThere, + GUS_Load, + GUS_UnLoad, + GUS_Init, + GUS_Exit, + GUS_PlayStart, + GUS_PlayStop, + GUS_Dummy, + GUS_VoiceSetVolume, + GUS_VoiceSetFrequency, + GUS_VoiceSetPanning, + GUS_VoicePlay +}; diff --git a/libs/oldmik/src/drv_nos.c b/libs/oldmik/src/drv_nos.c new file mode 100644 index 0000000..2b58030 --- /dev/null +++ b/libs/oldmik/src/drv_nos.c @@ -0,0 +1,96 @@ +/* + +Name: +DRV_NOS.C + +Description: +Mikmod driver for no output on any soundcard, monitor, keyboard, or whatever :) + +Portability: +All systems - All compilers + +*/ +#include +#include "mikmod.h" + + +BOOL NS_IsThere(void) +{ + return 1; +} + + +SWORD NS_SampleLoad(FILE *fp,ULONG s,ULONG a,ULONG b,UWORD f) +{ + return 1; +} + + +void NS_SampleUnload(SWORD h) +{ +} + + +BOOL NS_Init(void) +{ + return 1; +} + + +void NS_Exit(void) +{ +} + + +void NS_PlayStart(void) +{ +} + + +void NS_PlayStop(void) +{ +} + + +void NS_Update(void) +{ +} + + +void NS_VoiceSetVolume(UBYTE voice,UBYTE vol) +{ +} + + +void NS_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ +} + + +void NS_VoiceSetPanning(UBYTE voice,UBYTE pan) +{ +} + + +void NS_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ +} + + +DRIVER drv_nos={ + NULL, + "No Sound", + "MikMod Nosound Driver v2.0 - (c) Creative Silence", + NS_IsThere, + NS_SampleLoad, + NS_SampleUnload, + NS_Init, + NS_Exit, + NS_PlayStart, + NS_PlayStop, + NS_Update, + NS_VoiceSetVolume, + NS_VoiceSetFrequency, + NS_VoiceSetPanning, + NS_VoicePlay +}; diff --git a/libs/oldmik/src/drv_raw.c b/libs/oldmik/src/drv_raw.c new file mode 100644 index 0000000..c6b25d7 --- /dev/null +++ b/libs/oldmik/src/drv_raw.c @@ -0,0 +1,87 @@ +/* + +Name: +DRV_RAW.C + +Description: +Mikmod driver for output to a file called MUSIC.RAW + +!! DO NOT CALL MD_UPDATE FROM A INTERRUPT IF YOU USE THIS DRIVER !! + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: BC(y) +Linux: y + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include + +#include "mikmod.h" + +#define RAWBUFFERSIZE 8192 + +static FILE *rawout; + +static char RAW_DMABUF[RAWBUFFERSIZE]; + + +static BOOL RAW_IsThere(void) +{ + return 1; +} + + +static BOOL RAW_Init(void) +{ + if(!(rawout=fopen("music.raw","wb"))){ + myerr="Couldn't open output file 'music.raw'"; + return 0; + } + + if(!VC_Init()){ + fclose(rawout); + return 0; + } + + return 1; +} + + + +static void RAW_Exit(void) +{ + VC_Exit(); + fclose(rawout); +} + + +static void RAW_Update(void) +{ + VC_WriteBytes(RAW_DMABUF,RAWBUFFERSIZE); + fwrite(RAW_DMABUF,RAWBUFFERSIZE,1,rawout); +} + + +DRIVER drv_raw={ + NULL, + "music.raw file", + "MikMod music.raw file output driver v1.0", + RAW_IsThere, + VC_SampleLoad, + VC_SampleUnload, + RAW_Init, + RAW_Exit, + VC_PlayStart, + VC_PlayStop, + RAW_Update, + VC_VoiceSetVolume, + VC_VoiceSetFrequency, + VC_VoiceSetPanning, + VC_VoicePlay +}; diff --git a/libs/oldmik/src/drv_sb.c b/libs/oldmik/src/drv_sb.c new file mode 100644 index 0000000..6d85d47 --- /dev/null +++ b/libs/oldmik/src/drv_sb.c @@ -0,0 +1,511 @@ +/* + +Name: +DRV_SB.C + +Description: +Mikmod driver for output on Creative Labs Soundblasters & compatibles +(through DSP) + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include +#include +#include +#ifndef __DJGPP__ +#include +#endif + +#include "mikmod.h" +#include "mdma.h" +#include "mirq.h" + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SB stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +static UWORD sb_port; /* sb base port */ + +/* + Define some important SB i/o ports: +*/ + +#define MIXER_ADDRESS (sb_port+0x4) +#define MIXER_DATA (sb_port+0x5) +#define DSP_RESET (sb_port+0x6) +#define DSP_READ_DATA (sb_port+0xa) +#define DSP_WRITE_DATA (sb_port+0xc) +#define DSP_WRITE_STATUS (sb_port+0xc) +#define DSP_DATA_AVAIL (sb_port+0xe) + + +static void SB_MixerStereo(void) +/* + Enables stereo output for DSP versions 3.00 >= ver < 4.00 +*/ +{ + outportb(MIXER_ADDRESS,0xe); + outportb(MIXER_DATA,inportb(MIXER_DATA)|2); +} + + + +static void SB_MixerMono(void) +/* + Disables stereo output for DSP versions 3.00 >= ver < 4.00 +*/ +{ + outportb(MIXER_ADDRESS,0xe); + outportb(MIXER_DATA,inportb(MIXER_DATA)&0xfd); +} + + + +static BOOL SB_WaitDSPWrite(void) +/* + Waits until the DSP is ready to be written to. + + returns FALSE on timeout +*/ +{ + UWORD timeout=32767; + + while(timeout--){ + if(!(inportb(DSP_WRITE_STATUS)&0x80)) return 1; + } + return 0; +} + + + +static BOOL SB_WaitDSPRead(void) +/* + Waits until the DSP is ready to read from. + + returns FALSE on timeout +*/ +{ + UWORD timeout=32767; + + while(timeout--){ + if(inportb(DSP_DATA_AVAIL)&0x80) return 1; + } + return 0; +} + + + +static BOOL SB_WriteDSP(UBYTE data) +/* + Writes byte 'data' to the DSP. + + returns FALSE on timeout. +*/ +{ + if(!SB_WaitDSPWrite()) return 0; + outportb(DSP_WRITE_DATA,data); + return 1; +} + + + +static UWORD SB_ReadDSP(void) +/* + Reads a byte from the DSP. + + returns 0xffff on timeout. +*/ +{ + if(!SB_WaitDSPRead()) return 0xffff; + return(inportb(DSP_READ_DATA)); +} + + + +static void SB_SpeakerOn(void) +/* + Enables DAC speaker output. +*/ +{ + SB_WriteDSP(0xd1); +} + + + +static void SB_SpeakerOff(void) +/* + Disables DAC speaker output +*/ +{ + SB_WriteDSP(0xd3); +} + + + +static void SB_ResetDSP(void) +/* + Resets the DSP. +*/ +{ + int t; + /* reset the DSP by sending 1, (delay), then 0 */ + outportb(DSP_RESET,1); + for(t=0;t<8;t++) inportb(DSP_RESET); + outportb(DSP_RESET,0); +} + + + +static BOOL SB_Ping(void) +/* + Checks if a SB is present at the current baseport by + resetting the DSP and checking if it returned the value 0xaa. + + returns: TRUE => SB is present + FALSE => No SB detected +*/ +{ + SB_ResetDSP(); + return(SB_ReadDSP()==0xaa); +} + + + +static UWORD SB_GetDSPVersion(void) +/* + Gets SB-dsp version. returns 0xffff if dsp didn't respond. +*/ +{ + UWORD hi,lo; + + if(!SB_WriteDSP(0xe1)) return 0xffff; + + hi=SB_ReadDSP(); + lo=SB_ReadDSP(); + + return((hi<<8)|lo); +} + + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> The actual SB driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +static DMAMEM *SB_DMAMEM; +static signed char *SB_DMABUF; + +static UBYTE SB_TIMECONSTANT; + +static UBYTE PIC1MSK; +static UBYTE PIC2MSK; + +static UWORD sb_int; /* interrupt vector that belongs to sb_irq */ +static UWORD sb_ver; /* DSP version number */ +static UBYTE sb_irq; /* sb irq */ +static UBYTE sb_lodma; /* 8 bit dma channel (1.0/2.0/pro) */ +static UBYTE sb_hidma; /* 16 bit dma channel (16/16asp) */ +static UBYTE sb_dma; /* current dma channel */ + + +static BOOL SB_IsThere(void) +{ + char *envptr,c; + static char *endptr; + + sb_port =0xffff; + sb_irq =0xff; + sb_lodma=0xff; + sb_hidma=0xff; + + if((envptr=getenv("BLASTER"))==NULL) return 0; + + while(1){ + + /* skip whitespace */ + + do c=*(envptr++); while(c==' ' || c=='\t'); + + /* reached end of string? -> exit */ + + if(c==0) break; + + switch(c){ + + case 'a': + case 'A': + sb_port=strtol(envptr,&endptr,16); + break; + + case 'i': + case 'I': + sb_irq=strtol(envptr,&endptr,10); + break; + + case 'd': + case 'D': + sb_lodma=strtol(envptr,&endptr,10); + break; + + case 'h': + case 'H': + sb_hidma=strtol(envptr,&endptr,10); + break; + + default: + strtol(envptr,&endptr,16); + break; + } + envptr=endptr; + } + + if(sb_port==0xffff || sb_irq==0xff || sb_lodma==0xff) return 0; + + /* determine interrupt vector */ + + sb_int = (sb_irq>7) ? sb_irq+104 : sb_irq+8; + + if(!SB_Ping()) return 0; + + /* get dsp version. */ + + if((sb_ver=SB_GetDSPVersion())==0xffff) return 0; + + return 1; +} + + +static void interrupt newhandler(MIRQARGS) +{ + if(sb_ver<0x200){ + SB_WriteDSP(0x14); + SB_WriteDSP(0xff); + SB_WriteDSP(0xfe); + } + + if(md_mode & DMODE_16BITS) + inportb(sb_port+0xf); + else + inportb(DSP_DATA_AVAIL); + + MIrq_EOI(sb_irq); +} + + +static PVI oldhandler; + + +static BOOL SB_Init(void) +{ + ULONG t; + + if(!SB_IsThere()){ + myerr="No such hardware detected, check your 'BLASTER' env. variable"; + return 0; + } + +/* printf("SB version %x\n",sb_ver); */ +/* if(sb_ver>0x200) sb_ver=0x200; */ + + if(sb_ver>=0x400 && sb_hidma==0xff){ + myerr="High-dma setting in 'BLASTER' variable is required for SB-16"; + return 0; + } + + if(sb_ver<0x400 && md_mode&DMODE_16BITS){ + /* DSP versions below 4.00 can't do 16 bit sound. */ + md_mode&=~DMODE_16BITS; + } + + if(sb_ver<0x300 && md_mode&DMODE_STEREO){ + /* DSP versions below 3.00 can't do stereo sound. */ + md_mode&=~DMODE_STEREO; + } + + /* Use low dma channel for 8 bit, high dma for 16 bit */ + + sb_dma=(md_mode & DMODE_16BITS) ? sb_hidma : sb_lodma; + + if(sb_ver<0x400){ + + t=md_mixfreq; + if(md_mode & DMODE_STEREO) t<<=1; + + SB_TIMECONSTANT=256-(1000000L/t); + + if(sb_ver<0x201){ + if(SB_TIMECONSTANT>210) SB_TIMECONSTANT=210; + } + else{ + if(SB_TIMECONSTANT>233) SB_TIMECONSTANT=233; + } + + md_mixfreq=1000000L/(256-SB_TIMECONSTANT); + if(md_mode & DMODE_STEREO) md_mixfreq>>=1; + } + + if(!VC_Init()) return 0; + + SB_DMAMEM=MDma_AllocMem(md_dmabufsize); + + if(SB_DMAMEM==NULL){ + VC_Exit(); + myerr="Couldn't allocate page-contiguous dma-buffer"; + return 0; + } + + SB_DMABUF=(char *)MDma_GetPtr(SB_DMAMEM); + + oldhandler=MIrq_SetHandler(sb_irq,newhandler); + return 1; +} + + + +static void SB_Exit(void) +{ + MIrq_SetHandler(sb_irq,oldhandler); + MDma_FreeMem(SB_DMAMEM); + VC_Exit(); +} + + +static UWORD last=0; +static UWORD curr=0; + + +static void SB_Update(void) +{ + UWORD todo,index; + + curr=(md_dmabufsize-MDma_Todo(sb_dma))&0xfffc; + + if(curr==last) return; + + if(curr>last){ + todo=curr-last; index=last; + last+=VC_WriteBytes(&SB_DMABUF[index],todo); + MDma_Commit(SB_DMAMEM,index,todo); + if(last>=md_dmabufsize) last=0; + } + else{ + todo=md_dmabufsize-last; + VC_WriteBytes(&SB_DMABUF[last],todo); + MDma_Commit(SB_DMAMEM,last,todo); + last=VC_WriteBytes(SB_DMABUF,curr); + MDma_Commit(SB_DMAMEM,0,curr); + } +} + + + + +static void SB_PlayStart(void) +{ + VC_PlayStart(); + + MIrq_OnOff(sb_irq,1); + + if(sb_ver>=0x300 && sb_ver<0x400){ + if(md_mode & DMODE_STEREO){ + SB_MixerStereo(); + } + else{ + SB_MixerMono(); + } + } + + /* clear the dma buffer */ + + VC_SilenceBytes(SB_DMABUF,md_dmabufsize); + MDma_Commit(SB_DMAMEM,0,md_dmabufsize); + + if(!MDma_Start(sb_dma,SB_DMAMEM,md_dmabufsize,INDEF_WRITE)){ + return; + } + + if(sb_ver<0x400){ + SB_SpeakerOn(); + + SB_WriteDSP(0x40); + SB_WriteDSP(SB_TIMECONSTANT); + + if(sb_ver<0x200){ + SB_WriteDSP(0x14); + SB_WriteDSP(0xff); + SB_WriteDSP(0xfe); + } + else if(sb_ver==0x200){ + SB_WriteDSP(0x48); + SB_WriteDSP(0xff); + SB_WriteDSP(0xfe); + SB_WriteDSP(0x1c); + } + else{ + SB_WriteDSP(0x48); + SB_WriteDSP(0xff); + SB_WriteDSP(0xfe); + SB_WriteDSP(0x90); + } + } + else{ + SB_WriteDSP(0x41); + + SB_WriteDSP(md_mixfreq>>8); + SB_WriteDSP(md_mixfreq&0xff); + + if(md_mode & DMODE_16BITS){ + SB_WriteDSP(0xb6); + SB_WriteDSP((md_mode & DMODE_STEREO) ? 0x30 : 0x10); + } + else{ + SB_WriteDSP(0xc6); + SB_WriteDSP((md_mode & DMODE_STEREO) ? 0x20 : 0x00); + } + + SB_WriteDSP(0xff); + SB_WriteDSP(0xef); + } +} + + +static void SB_PlayStop(void) +{ + VC_PlayStop(); + SB_SpeakerOff(); + SB_ResetDSP(); + SB_ResetDSP(); + MDma_Stop(sb_dma); + MIrq_OnOff(sb_irq,0); +} + + +DRIVER drv_sb={ + NULL, + "Soundblaster & compatibles", + "MikMod Soundblaster Driver v2.1 for 1.0 / 2.0 / Pro / 16", + SB_IsThere, + VC_SampleLoad, + VC_SampleUnload, + SB_Init, + SB_Exit, + SB_PlayStart, + SB_PlayStop, + SB_Update, + VC_VoiceSetVolume, + VC_VoiceSetFrequency, + VC_VoiceSetPanning, + VC_VoicePlay +}; diff --git a/libs/oldmik/src/drv_ss.c b/libs/oldmik/src/drv_ss.c new file mode 100644 index 0000000..4d0ff26 --- /dev/null +++ b/libs/oldmik/src/drv_ss.c @@ -0,0 +1,714 @@ +/* + +Name: +DRV_SS.C + +Description: +Mikmod driver for output on Ensoniq Soundscape / Soundscape ELITE + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include +#include +#include +#include + +#include "mikmod.h" +#include "mdma.h" +#include "mirq.h" + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel SS stuff <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +/* Ensoniq gate-array chip defines ... */ + +#define ODIE 0 /* ODIE gate array */ +#define OPUS 1 /* OPUS gate array */ +#define MMIC 2 /* MiMIC gate array */ + +/* relevant direct register defines - offsets from base address */ +#define GA_HOSTCTL_OFF 2 /* host port ctrl/stat reg */ +#define GA_ADDR_OFF 4 /* indirect address reg */ +#define GA_DATA_OFF 5 /* indirect data reg */ +#define GA_CODEC_OFF 8 /* for some boards CoDec is fixed from base */ + +/* relevant indirect register defines */ +#define GA_DMAB_REG 3 /* DMA chan B assign reg */ +#define GA_INTCFG_REG 4 /* interrupt configuration reg */ +#define GA_DMACFG_REG 5 /* DMA configuration reg */ +#define GA_CDCFG_REG 6 /* CD-ROM/CoDec config reg */ +#define GA_HMCTL_REG 9 /* host master control reg */ + + +/* AD-1848 or compatible CoDec defines ... */ +/* relevant direct register defines - offsets from base */ +#define CD_ADDR_OFF 0 /* indirect address reg */ +#define CD_DATA_OFF 1 /* indirect data reg */ +#define CD_STATUS_OFF 2 /* status register */ + +/* relevant indirect register defines */ +#define CD_ADCL_REG 0 /* left DAC input control reg */ +#define CD_ADCR_REG 1 /* right DAC input control reg */ +#define CD_CDAUXL_REG 2 /* left DAC output control reg */ +#define CD_CDAUXR_REG 3 /* right DAC output control reg */ +#define CD_DACL_REG 6 /* left DAC output control reg */ +#define CD_DACR_REG 7 /* right DAC output control reg */ +#define CD_FORMAT_REG 8 /* clock and data format reg */ +#define CD_CONFIG_REG 9 /* interface config register */ +#define CD_PINCTL_REG 10 /* external pin control reg */ +#define CD_UCOUNT_REG 14 /* upper count reg */ +#define CD_LCOUNT_REG 15 /* lower count reg */ +#define CD_XFORMAT_REG 28 /* extended format reg - 1845 record */ +#define CD_XUCOUNT_REG 30 /* extended upper count reg - 1845 record */ +#define CD_XLCOUNT_REG 31 /* extended lower count reg - 1845 record */ + +#define CD_MODE_CHANGE 0x40 /* mode change mask for addr reg */ + +/**************************************************************************** +hardware config info ... +****************************************************************************/ + +static UWORD BasePort; /* Gate Array/MPU-401 base port */ +static UWORD MidiIrq; /* the MPU-401 IRQ */ +static UWORD WavePort; /* the AD-1848 base port */ +static UWORD WaveIrq; /* the PCM IRQ */ +static UWORD DmaChan; /* the PCM DMA channel */ + +/**************************************************************************** +all kinds of stuff ... +****************************************************************************/ + +static UWORD Windx; /* Wave IRQ index - for reg writes */ +static UWORD Mindx; /* MIDI IRQ index - for reg writes */ + +static UBYTE IcType; /* the Ensoniq chip type */ +static UBYTE CdCfgSav; /* gate array register save area */ +static UBYTE DmaCfgSav; /* gate array register save area */ +static UBYTE IntCfgSav; /* gate array register save area */ + +static UWORD const SsIrqs[4] = { 9, 5, 7, 10 }; /* Soundscape IRQs */ +static UWORD const RsIrqs[4] = { 9, 7, 5, 15 }; /* an older IRQ set */ +static UWORD const *Irqs; /* pointer to one of the IRQ sets */ + +static UBYTE DacSavL; /* DAC left volume save */ +static UBYTE DacSavR; /* DAC right volume save */ +static UBYTE CdxSavL; /* CD/Aux left volume save */ +static UBYTE CdxSavR; /* CD/Aux right volume save */ +static UBYTE AdcSavL; /* ADC left volume save */ +static UBYTE AdcSavR; /* ADC right volume save */ + +static DMAMEM *SS_DMAMEM; +static char *SS_DMABUF; + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> The actual SS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + + + +UBYTE GaRead(UWORD rnum) +/* + This function is used to read the indirect addressed registers in the + Ensoniq Soundscape gate array. + + INPUTS: + rnum - the numner of the indirect register to be read + + RETURNS: + the contents of the indirect register are returned +*/ +{ + outportb(BasePort + GA_ADDR_OFF, rnum); + return inportb(BasePort + GA_DATA_OFF); +} + + + +void GaWrite(UWORD rnum,UBYTE value) +/* + This function is used to write the indirect addressed registers in the + Ensoniq Soundscape gate array. + + INPUTS: + rnum - the numner of the indirect register to be read + value - the byte value to be written to the indirect register + + RETURNS: + Nothing +*/ +{ + outportb(BasePort + GA_ADDR_OFF, rnum); + outportb(BasePort + GA_DATA_OFF, value); +} + + +UBYTE CdRead(UWORD rnum) +/* + This function is used to read the indirect addressed registers in the + AD-1848 or compatible CoDec. It will preserve the special function bits + in the upper-nibble of the indirect address register. + + INPUTS: + rnum - the numner of the indirect register to be read + + RETURNS: + the contents of the indirect register are returned + +*/ +{ + outportb(WavePort + CD_ADDR_OFF, + (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum); + return inportb(WavePort+CD_DATA_OFF); +} + + + +void CdWrite(UWORD rnum,UBYTE value) +/* + This function is used to write the indirect addressed registers in the + Ad-1848 or compatible CoDec. It will preserve the special function bits + in the upper-nibble of the indirect address register. + + INPUTS: + rnum - the numner of the indirect register to be read + value - the byte value to be written to the indirect register + + RETURNS: + Nothing +*/ +{ + outportb(WavePort + CD_ADDR_OFF, + (inportb(WavePort + CD_ADDR_OFF) & 0xf0) | rnum); + outportb(WavePort + CD_DATA_OFF, value); +} + + + + +void SetDacVol(UBYTE lvol,UBYTE rvol) +/* + This function sets the left and right DAC output level in the CoDec. + + INPUTS: + lvol - left volume, 0-127 + rvol - right volume, 0-127 + + RETURNS: + Nothing + +*/ +{ + CdWrite(CD_DACL_REG, ~(lvol >> 1) & 0x3f); + CdWrite(CD_DACR_REG, ~(rvol >> 1) & 0x3f); +} + + + +void SetCdRomVol(UBYTE lvol,UBYTE rvol) +/* + This function sets the left and right CD-ROM output level in the CoDec. + + INPUTS: + lvol - left volume, 0-127 + rvol - right volume, 0-127 + + RETURNS: + Nothing +*/ +{ + CdWrite(CD_CDAUXL_REG, ~(lvol >> 2) & 0x1f); + CdWrite(CD_CDAUXR_REG, ~(rvol >> 2) & 0x1f); +} + + +void SetAdcVol(UBYTE lvol,UBYTE rvol) +/* + This function sets the left and right ADC input level in the CoDec. + + INPUTS: + lvol - left volume, 0-127 + rvol - right volume, 0-127 + + RETURNS: + Nothing + +*/ +{ + CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0xf0) | (lvol & 0x7f) >> 3); + CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0xf0) | (rvol & 0x7f) >> 3); +} + + +void StopCoDec(void) +{ + UWORD i; + + CdWrite(CD_CONFIG_REG,CdRead(CD_CONFIG_REG)&0xfc); + + /* Let the CoDec receive its last DACK(s). The DMAC must not be */ + /* masked while the CoDec has DRQs pending. */ +/* for(i=0; i<256; ++i ) + if(!(inportb(DmacRegP->status) & (0x10 << DmaChan))) break; +*/ +} + + + +BOOL GetConfigEntry(char *entry, char *dest, FILE *fp) +/* + This function parses a file (SNDSCAPE.INI) for a left-hand string and, + if found, writes its associated right-hand value to a destination buffer. + This function is case-insensitive. + + INPUTS: + fp - a file pointer to the open SNDSCAPE.INI config file + dst - the destination buffer pointer + lhp - a pointer to the right-hand string + + RETURNS: + 1 - if successful + 0 - if the right-hand string is not found or has no equate +*/ +{ + char static str[83]; + char static tokstr[33]; + char *p; + + /* make a local copy of the entry, upper-case it */ + strcpy(tokstr, entry); + strupr(tokstr); + + /* rewind the file and try to find it ... */ + rewind(fp); + + for( ;; ) { + /* get the next string from the file */ + + fgets(str, 83, fp); + if(feof(fp)) return 0; + + /* properly terminate the string */ + for( p = str; *p != '\0'; ++p ) { + if( *p == ' ' || *p == '\t' || *p == 0x0a || *p == 0x0d ) { + *p = '\0'; + break; + } + } + + /* see if it's an 'equate' string; if so, zero the '=' */ + if( !(p = strchr(str, '=')) ) continue; + *p = '\0'; + + /* upper-case the current string and test it */ + strupr(str); + if( strcmp(str, tokstr) ) + continue; + + /* it's our string - copy the right-hand value to buffer */ + for( p = str + strlen(str) + 1; (*dest++ = *p++) != '\0'; ); + break; + } + return 1; +} + + +static BOOL SS_IsThere(void) +{ + static char str[78]; + char *envptr; + FILE *fp; + UBYTE tmp; + + if((envptr=getenv("SNDSCAPE"))==NULL) return 0; + + strcpy(str, envptr); + if( str[strlen(str) - 1] == '\\' ) + str[strlen(str) - 1] = '\0'; + + strcat(str, "\\SNDSCAPE.INI"); + + if(!(fp=fopen(str, "r"))) return 0; + + /* read all of the necessary config info ... */ + if(!GetConfigEntry("Product",str,fp)){ + fclose(fp); + return 0; + } + + /* if an old product name is read, set the IRQs accordingly */ + strupr(str); + if(strstr(str,"SOUNDFX") || strstr(str,"MEDIA_FX")) + Irqs = RsIrqs; + else + Irqs = SsIrqs; + + if(!GetConfigEntry("Port", str, fp)){ + fclose(fp); + return 0; + } + + BasePort=strtol(str,NULL,16); + + if(!GetConfigEntry("WavePort",str,fp)){ + fclose(fp); + return 0; + } + + WavePort=strtol(str,NULL,16); + + if(!GetConfigEntry("IRQ",str,fp)){ + fclose(fp); + return 0; + } + + MidiIrq=strtol(str,NULL,10); + if(MidiIrq==2) MidiIrq = 9; + + if(!GetConfigEntry("SBIRQ",str,fp)){ + fclose(fp); + return 0; + } + + WaveIrq=strtol(str,NULL,10); + if(WaveIrq==2) WaveIrq=9; + + if(!GetConfigEntry("DMA",str,fp)){ + fclose(fp); + return 0; + } + + DmaChan=strtol(str,NULL,10); + + fclose(fp); + + /* see if Soundscape is there by reading HW ... */ + if((inportb(BasePort+GA_HOSTCTL_OFF)&0x78) != 0x00) return 0; + if((inportb(BasePort+GA_ADDR_OFF)&0xf0)==0xf0) return 0; + + outportb(BasePort+GA_ADDR_OFF,0xf5); + + tmp=inportb(BasePort+GA_ADDR_OFF); + + if((tmp & 0xf0)==0xf0) return 0; + if((tmp & 0x0f)!=0x05) return 0; + + /* formulate the chip ID */ + if( (tmp & 0x80) != 0x00 ) + IcType = MMIC; + else if((tmp & 0x70) != 0x00) + IcType = OPUS; + else + IcType = ODIE; + + /* now do a quick check to make sure the CoDec is there too */ + if((inportb(WavePort)&0x80)!=0x00) return 0; + return 1; +} + + +#ifdef NEVER + +static void interrupt newhandler(void) +{ + if(WaveIrq==7){ + outportb(0x20,0x0b); + inportb(0x21); + if(!(inportb(0x20)&0x80)) return; + } + else if(WaveIrq==15){ + outportb(0xa0,0x0b); + inportb(0xa1); + if(!(inportb(0xa0)&0x80)) return; + } + + interruptcount++; + + /* if the CoDec is interrupting clear the AD-1848 interrupt */ + if(inportb(WavePort+CD_STATUS_OFF)&0x01) + outportb(WavePort+CD_STATUS_OFF,0x00); + + MIrq_EOI(WaveIrq); +} + + +static PVI oldhandler; + +#endif + + +static UWORD codecfreqs[14]={ + 5512, 6615, 8000, 9600,11025,16000,18900, + 22050,27428,32000,33075,37800,44100,48000 +}; + + +static UWORD codecformats[14]={ + 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, 0x05, + 0x07, 0x04, 0x06, 0x0d, 0x09, 0x0b, 0x0c +}; + + +static UBYTE codecformat; + + +static BOOL SS_Init(void) +{ + int t; + + if(!SS_IsThere()){ + myerr="No such hardware detected, check your 'SNDSCAPE' env. variable"; + return 0; + } + + printf("Ensoniq Soundscape at port 0x%x, irq %d, dma %d\n",WavePort,WaveIrq,DmaChan); + + /* find closest codec frequency */ + + for(t=0;t<14;t++){ + if(t==13 || md_mixfreq<=codecfreqs[t]){ + md_mixfreq=codecfreqs[t]; + break; + } + } + + codecformat=codecformats[t]; + if(md_mode & DMODE_STEREO) codecformat|=0x10; + if(md_mode & DMODE_16BITS) codecformat|=0x40; + + if(!VC_Init()) return 0; + + SS_DMAMEM=MDma_AllocMem(md_dmabufsize); + + if(SS_DMAMEM==NULL){ + VC_Exit(); + myerr="Couldn't allocate page-contiguous dma-buffer"; + return 0; + } + + SS_DMABUF=(char *)MDma_GetPtr(SS_DMAMEM); + + /* In case the CoDec is running, stop it */ + StopCoDec(); + + /* Clear possible CoDec and SoundBlaster emulation interrupts */ + outportb(WavePort+CD_STATUS_OFF,0x00); + inportb(0x22e); + + /* If necessary, save some regs, do some resource re-routing */ + if( IcType != MMIC) { + + /* derive the MIDI and Wave IRQ indices (0-3) for reg writes */ + for( Mindx = 0; Mindx < 4; ++Mindx ) + if( MidiIrq == *(Irqs + Mindx) ) + break; + for( Windx = 0; Windx < 4; ++Windx ) + if( WaveIrq == *(Irqs + Windx) ) + break; + + /* setup the CoDec DMA polarity */ + GaWrite(GA_DMACFG_REG, 0x50); + + /* give the CoDec control of the DMA and Wave IRQ resources */ + CdCfgSav = GaRead(GA_CDCFG_REG); + GaWrite(GA_CDCFG_REG, 0x89 | (DmaChan << 4) | (Windx << 1)); + + /* pull the Sound Blaster emulation off of those resources */ + DmaCfgSav = GaRead(GA_DMAB_REG); + GaWrite(GA_DMAB_REG, 0x20); + IntCfgSav = GaRead(GA_INTCFG_REG); + GaWrite(GA_INTCFG_REG, 0xf0 | (Mindx << 2) | Mindx); + } + + /* Save all volumes that we might use, init some levels */ + DacSavL = CdRead(CD_DACL_REG); + DacSavR = CdRead(CD_DACR_REG); + CdxSavL = CdRead(CD_CDAUXL_REG); + CdxSavR = CdRead(CD_CDAUXL_REG); + AdcSavL = CdRead(CD_ADCL_REG); + AdcSavR = CdRead(CD_ADCR_REG); + + SetDacVol(127, 127); + SetAdcVol(96, 96); + + /* Select the mic/line input to the record mux; */ + /* if not ODIE, set the mic gain bit too */ + CdWrite(CD_ADCL_REG, (CdRead(CD_ADCL_REG) & 0x3f) | + (IcType == ODIE ? 0x80 : 0xa0)); + CdWrite(CD_ADCR_REG, (CdRead(CD_ADCR_REG) & 0x3f) | + (IcType == ODIE ? 0x80 : 0xa0)); + + /* Put the CoDec into mode change state */ + outportb(WavePort + CD_ADDR_OFF, 0x40); + + /* Setup CoDec mode - single DMA chan, AutoCal on */ + CdWrite(CD_CONFIG_REG, 0x0c); + +#ifdef NEVER + /* enable the CoDec interrupt pin */ + CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02); + oldhandler=MIrq_SetHandler(WaveIrq,newhandler); + MIrq_OnOff(WaveIrq,1); +#else + /* disable the interrupt for mikmod */ + CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd); +#endif + return 1; +} + + + +static void SS_Exit(void) +{ + /* in case the CoDec is running, stop it */ + StopCoDec(); + + /* mask the PC DMA Controller */ +/* outportb(DmacRegP->mask, 0x04 | DmaChan); */ + +#ifdef NEVER + /* disable the CoDec interrupt pin */ + CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd); + MIrq_OnOff(WaveIrq,0); + MIrq_SetHandler(WaveIrq,oldhandler); +#endif + + /* restore all volumes ... */ + CdWrite(CD_DACL_REG, DacSavL); + CdWrite(CD_DACR_REG, DacSavR); + CdWrite(CD_CDAUXL_REG, CdxSavL); + CdWrite(CD_CDAUXL_REG, CdxSavR); + CdWrite(CD_ADCL_REG, AdcSavL); + CdWrite(CD_ADCR_REG, AdcSavR); + + /* if necessary, restore gate array resource registers */ + if(IcType!=MMIC){ + GaWrite(GA_INTCFG_REG, IntCfgSav); + GaWrite(GA_DMAB_REG, DmaCfgSav); + GaWrite(GA_CDCFG_REG, CdCfgSav); + } + + MDma_FreeMem(SS_DMAMEM); + VC_Exit(); +} + + +static UWORD last=0; +static UWORD curr=0; + + +static void SS_Update(void) +{ + UWORD todo,index; + + curr=(md_dmabufsize-MDma_Todo(DmaChan))&0xfffc; + + if(curr>=md_dmabufsize) return; + if(curr==last) return; + + if(curr>last){ + todo=curr-last; index=last; + last+=VC_WriteBytes(&SS_DMABUF[index],todo); + MDma_Commit(SS_DMAMEM,index,todo); + if(last>=md_dmabufsize) last=0; + } + else{ + todo=md_dmabufsize-last; + VC_WriteBytes(&SS_DMABUF[last],todo); + MDma_Commit(SS_DMAMEM,last,todo); + last=VC_WriteBytes(SS_DMABUF,curr); + MDma_Commit(SS_DMAMEM,0,curr); + } +} + + +static void SS_PlayStart(void) +{ + int direction=0; + long i; + UWORD tmp; + + VC_PlayStart(); + + /* make sure the the CoDec is in mode change state */ + outportb(WavePort + CD_ADDR_OFF, 0x40); + + /* and write the format register */ + CdWrite(CD_FORMAT_REG, codecformat); + + /* if not using ODIE and recording, setup extended format register */ + if( IcType != ODIE && direction ) + CdWrite(CD_XFORMAT_REG, codecformat & 0x70); + + /* delay for internal re-synch */ + for( i = 0; i < 200000UL; ++i ) + inportb(BasePort + GA_ADDR_OFF); + + /* clear the dma buffer */ + + VC_SilenceBytes(SS_DMABUF,md_dmabufsize); + MDma_Commit(SS_DMAMEM,0,md_dmabufsize); + + /* Write the CoDec interrupt count - sample frames per half-buffer. */ + /* If not using ODIE and recording, use extended count regs */ + tmp = md_dmabufsize; + if(md_mode&DMODE_STEREO) tmp>>=1; + if(md_mode&DMODE_16BITS) tmp>>=1; + tmp--; + + if( IcType != ODIE && direction ) { + CdWrite(CD_XLCOUNT_REG, tmp); + CdWrite(CD_XUCOUNT_REG, tmp >> 8); + } + else { + CdWrite(CD_LCOUNT_REG, tmp); + CdWrite(CD_UCOUNT_REG, tmp >> 8); + } + + if(!MDma_Start(DmaChan,SS_DMAMEM,md_dmabufsize,INDEF_WRITE)){ + return; + } + + /* disable mode change state and start the CoDec */ + outportb(WavePort + CD_ADDR_OFF, 0x00); + CdWrite(CD_CONFIG_REG, direction ? 0x02 : 0x01); +} + + +static void SS_PlayStop(void) +{ + StopCoDec(); + VC_PlayStop(); +} + + +DRIVER drv_ss={ + NULL, + "Ensoniq Soundscape", + "MikMod Ensoniq Soundscape Driver v0.0 - Thanks to CyberCerus", + SS_IsThere, + VC_SampleLoad, + VC_SampleUnload, + SS_Init, + SS_Exit, + SS_PlayStart, + SS_PlayStop, + SS_Update, + VC_VoiceSetVolume, + VC_VoiceSetFrequency, + VC_VoiceSetPanning, + VC_VoicePlay +}; diff --git a/libs/oldmik/src/load_m15.c b/libs/oldmik/src/load_m15.c new file mode 100644 index 0000000..6186a03 --- /dev/null +++ b/libs/oldmik/src/load_m15.c @@ -0,0 +1,323 @@ +/* + +Name: +LOAD_M15.C + +Description: +15 instrument MOD loader + +Portability: +All systems - all compilers (hopefully) + +*/ +#include +#include +#include +#include +#include "mikmod.h" + +/************************************************************************* +*************************************************************************/ + + +typedef struct MSAMPINFO{ /* sample header as it appears in a module */ + char samplename[22]; + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + + +typedef struct MODULEHEADER{ /* verbatim module header */ + char songname[20]; /* the songname.. */ + MSAMPINFO samples[15]; /* all sampleinfo */ + UBYTE songlength; /* number of patterns used */ + UBYTE magic1; /* should be 127 */ + UBYTE positions[128]; /* which pattern to play at pos */ +} MODULEHEADER; + + +typedef struct MODNOTE{ + UBYTE a,b,c,d; +} MODNOTE; + + +/************************************************************************* +*************************************************************************/ + +static MODULEHEADER *mh; /* raw as-is module header */ +static MODNOTE *patbuf; + + +static BOOL LoadModuleHeader(MODULEHEADER *mh) +{ + int t; + + _mm_read_str(mh->songname,20,modfp); + + for(t=0;t<15;t++){ + MSAMPINFO *s= &mh->samples[t]; + _mm_read_str(s->samplename,22,modfp); + s->length =_mm_read_M_UWORD(modfp); + s->finetune =_mm_read_UBYTE(modfp); + s->volume =_mm_read_UBYTE(modfp); + s->reppos =_mm_read_M_UWORD(modfp); + s->replen =_mm_read_M_UWORD(modfp); + } + + mh->songlength =_mm_read_UBYTE(modfp); + mh->magic1 =_mm_read_UBYTE(modfp); /* should be 127 */ + _mm_read_UBYTES(mh->positions,128,modfp); + + return(!feof(modfp)); +} + + + +BOOL M15_Test(void) +{ + int t; + MODULEHEADER mh; + + if(!LoadModuleHeader(&mh)) return 0; + + for(t=0;t<15;t++){ + + /* all finetunes should be zero */ + if(mh.samples[t].finetune!=0) return 0; + + /* all volumes should be <=64 */ + if(mh.samples[t].volume>64) return 0; + } + if(mh.magic1>127) return 0; /* and magic1 should be <128 */ + + return 1; +} + + +BOOL M15_Init(void) +{ + patbuf=NULL; + if(!(mh=(MODULEHEADER *)MyCalloc(1,sizeof(MODULEHEADER)))) return 0; + return 1; +} + + +void M15_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(patbuf!=NULL) free(patbuf); +} + + +/* + +Old (amiga) noteinfo: + + _____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + + +*/ + + +UWORD M15_npertab[60]={ + +/* -> Tuning 0 */ + + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906, + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113, + 107,101,95,90,85,80,75,71,67,63,60,56 +}; + + +void M15_ConvertNote(MODNOTE *n) +{ + UBYTE instrument,effect,effdat,note; + UWORD period; + + /* extract the various information from the 4 bytes that + make up a single note */ + + instrument=(n->a&0x10)|(n->c>>4); + period=(((UWORD)n->a&0xf)<<8)+n->b; + effect=n->c&0xf; + effdat=n->d; + + /* Convert the period to a note number */ + + note=0; + if(period!=0){ + for(note=0;note<60;note++){ + if(period>=M15_npertab[note]) break; + } + note++; + if(note==61) note=0; + } + + if(instrument!=0){ + UniInstrument(instrument-1); + } + + if(note!=0){ + UniNote(note+23); + } + + UniPTEffect(effect,effdat); +} + + + +UBYTE *M15_ConvertTrack(MODNOTE *n) +{ + int t; + + UniReset(); + for(t=0;t<64;t++){ + M15_ConvertNote(n); + UniNewline(); + n+=of.numchn; + } + return UniDup(); +} + + + +BOOL M15_LoadPatterns(void) +/* + Loads all patterns of a modfile and converts them into the + 3 byte format. +*/ +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + /* Allocate temporary buffer for loading + and converting the patterns */ + + if(!(patbuf=(MODNOTE *)MyCalloc(64U*of.numchn,sizeof(MODNOTE)))) return 0; + + for(t=0;tsongname,20); /* make a cstr of songname */ + of.numpos=mh->songlength; /* copy the songlength */ + memcpy(of.positions,mh->positions,128); /* copy the position array */ + + /* Count the number of patterns */ + + of.numpat=0; + + for(t=0;t<128;t++){ /* <-- BUGFIX... have to check ALL positions */ + if(of.positions[t] > of.numpat){ + of.numpat=of.positions[t]; + } + } + of.numpat++; + of.numtrk=of.numpat*of.numchn; + + /* Finally, init the sampleinfo structures */ + + of.numins=15; + if(!AllocInstruments()) return 0; + + s=mh->samples; /* init source pointer */ + d=of.instruments; /* init dest pointer */ + + for(t=0;tnumsmp=1; + if(!AllocSamples(d)) return 0; + + q=d->samples; + + /* convert the samplename */ + + d->insname=DupStr(s->samplename,22); + + /* init the sampleinfo variables and + convert the size pointers to longword format */ + + q->c2spd=finetune[s->finetune&0xf]; + q->volume=s->volume; + q->loopstart=s->reppos; + q->loopend=q->loopstart+(s->replen<<1); + q->length=s->length<<1; + q->seekpos=0; + + q->flags=SF_SIGNED; + if(s->replen>1) q->flags|=SF_LOOP; + + /* fix replen if repend>length */ + + if(q->loopend>q->length) q->loopend=q->length; + + s++; /* point to next source sampleinfo */ + d++; /* point to next destiny sampleinfo */ + } + + if(!M15_LoadPatterns()) return 0; + return 1; +} + + + +LOADER load_m15={ + NULL, + "15-instrument module", + "Portable MOD-15 loader v0.1", + M15_Init, + M15_Test, + M15_Load, + M15_Cleanup +}; diff --git a/libs/oldmik/src/load_mod.c b/libs/oldmik/src/load_mod.c new file mode 100644 index 0000000..f8698f1 --- /dev/null +++ b/libs/oldmik/src/load_mod.c @@ -0,0 +1,358 @@ +/* + +Name: +LOAD_MOD.C + +Description: +Generic MOD loader + +Portability: +All systems - all compilers (hopefully) + +*/ +#include +#include +#include +#include +#include "mikmod.h" + +/************************************************************************* +*************************************************************************/ + + +typedef struct MSAMPINFO{ /* sample header as it appears in a module */ + char samplename[22]; + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + + +typedef struct MODULEHEADER{ /* verbatim module header */ + char songname[20]; /* the songname.. */ + MSAMPINFO samples[31]; /* all sampleinfo */ + UBYTE songlength; /* number of patterns used */ + UBYTE magic1; /* should be 127 */ + UBYTE positions[128]; /* which pattern to play at pos */ + UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */ +} MODULEHEADER; + +#define MODULEHEADERSIZE 1084 + + +typedef struct MODTYPE{ /* struct to identify type of module */ + char id[5]; + UBYTE channels; + char *name; +} MODTYPE; + + +typedef struct MODNOTE{ + UBYTE a,b,c,d; +} MODNOTE; + + +/************************************************************************* +*************************************************************************/ + + +char protracker[]="Protracker"; +char startracker[]="Startracker"; +char fasttracker[]="Fasttracker"; +char ins15tracker[]="15-instrument"; +char oktalyzer[]="Oktalyzer"; +char taketracker[]="TakeTracker"; + + +MODTYPE modtypes[]={ + "M.K.",4,protracker, /* protracker 4 channel */ + "M!K!",4,protracker, /* protracker 4 channel */ + "FLT4",4,startracker, /* startracker 4 channel */ + "4CHN",4,fasttracker, /* fasttracker 4 channel */ + "6CHN",6,fasttracker, /* fasttracker 6 channel */ + "8CHN",8,fasttracker, /* fasttracker 8 channel */ + "CD81",8,oktalyzer, /* atari oktalyzer 8 channel */ + "OKTA",8,oktalyzer, /* atari oktalyzer 8 channel */ + "16CN",16,taketracker, /* taketracker 16 channel */ + "32CN",32,taketracker, /* taketracker 32 channel */ + " ",4,ins15tracker /* 15-instrument 4 channel */ +}; + +static MODULEHEADER *mh; /* raw as-is module header */ +static MODNOTE *patbuf; + + +BOOL MOD_Test(void) +{ + int t; + + char id[4]; + + _mm_fseek(modfp,MODULEHEADERSIZE-4,SEEK_SET); + if(!fread(id,4,1,modfp)) return 0; + + /* find out which ID string */ + + for(t=0;t<10;t++){ + if(!memcmp(id,modtypes[t].id,4)) return 1; + } + + return 0; +} + + +BOOL MOD_Init(void) +{ + patbuf=NULL; + if(!(mh=(MODULEHEADER *)MyCalloc(1,sizeof(MODULEHEADER)))) return 0; + return 1; +} + + +void MOD_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(patbuf!=NULL) free(patbuf); +} + + +/* + +Old (amiga) noteinfo: + + _____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + + +*/ + + +UWORD npertab[60]={ + +/* -> Tuning 0 */ + + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906, + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113, + 107,101,95,90,85,80,75,71,67,63,60,56 +}; + + +void ConvertNote(MODNOTE *n) +{ + UBYTE instrument,effect,effdat,note; + UWORD period; + + /* extract the various information from the 4 bytes that + make up a single note */ + + instrument=(n->a&0x10)|(n->c>>4); + period=(((UWORD)n->a&0xf)<<8)+n->b; + effect=n->c&0xf; + effdat=n->d; + + /* Convert the period to a note number */ + + note=0; + if(period!=0){ + for(note=0;note<60;note++){ + if(period>=npertab[note]) break; + } + note++; + if(note==61) note=0; + } + + if(instrument!=0){ + UniInstrument(instrument-1); + } + + if(note!=0){ + UniNote(note+23); + } + + UniPTEffect(effect,effdat); +} + + +UBYTE *ConvertTrack(MODNOTE *n) +{ + int t; + + UniReset(); + for(t=0;t<64;t++){ + ConvertNote(n); + UniNewline(); + n+=of.numchn; + } + return UniDup(); +} + + +BOOL ML_LoadPatterns(void) +/* + Loads all patterns of a modfile and converts them into the + 3 byte format. +*/ +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + /* Allocate temporary buffer for loading + and converting the patterns */ + + if(!(patbuf=(MODNOTE *)MyCalloc(64U*of.numchn,sizeof(MODNOTE)))) return 0; + + for(t=0;tsongname,20,modfp); + + for(t=0;t<31;t++){ + s=&mh->samples[t]; + _mm_read_str(s->samplename,22,modfp); + s->length =_mm_read_M_UWORD(modfp); + s->finetune =_mm_read_UBYTE(modfp); + s->volume =_mm_read_UBYTE(modfp); + s->reppos =_mm_read_M_UWORD(modfp); + s->replen =_mm_read_M_UWORD(modfp); + } + + mh->songlength =_mm_read_UBYTE(modfp); + mh->magic1 =_mm_read_UBYTE(modfp); + + _mm_read_UBYTES(mh->positions,128,modfp); + _mm_read_UBYTES(mh->magic2,4,modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + /* find out which ID string */ + + for(modtype=0;modtype<10;modtype++){ + if(!memcmp(mh->magic2,modtypes[modtype].id,4)) break; + } + + if(modtype==10){ + + /* unknown modtype */ + myerr=ERROR_NOT_A_MODULE; + return 0; + } + + /* set module variables */ + + of.initspeed=6; + of.inittempo=125; + of.numchn=modtypes[modtype].channels; /* get number of channels */ + of.modtype=strdup(modtypes[modtype].name); /* get ascii type of mod */ + of.songname=DupStr(mh->songname,20); /* make a cstr of songname */ + of.numpos=mh->songlength; /* copy the songlength */ + memcpy(of.positions,mh->positions,128); /* copy the position array */ + + /* Count the number of patterns */ + + of.numpat=0; + + for(t=0;t<128;t++){ /* <-- BUGFIX... have to check ALL positions */ + if(of.positions[t] > of.numpat){ + of.numpat=of.positions[t]; + } + } + of.numpat++; + of.numtrk=of.numpat*of.numchn; + + /* Finally, init the sampleinfo structures */ + + of.numins=31; + + if(!AllocInstruments()) return 0; + + s=mh->samples; /* init source pointer */ + d=of.instruments; /* init dest pointer */ + + for(t=0;tnumsmp=1; + if(!AllocSamples(d)) return 0; + + q=d->samples; + + /* convert the samplename */ + + d->insname=DupStr(s->samplename,22); + + /* init the sampleinfo variables and + convert the size pointers to longword format */ + + q->c2spd=finetune[s->finetune&0xf]; + q->volume=s->volume; + q->loopstart=(ULONG)s->reppos<<1; + q->loopend=q->loopstart+((ULONG)s->replen<<1); + q->length=(ULONG)s->length<<1; + q->seekpos=0; + + q->flags=SF_SIGNED; + if(s->replen>1) q->flags|=SF_LOOP; + + /* fix replen if repend>length */ + + if(q->loopend>q->length) q->loopend=q->length; + + s++; /* point to next source sampleinfo */ + d++; /* point to next destiny sampleinfo */ + } + + if(!ML_LoadPatterns()) return 0; + return 1; +} + + + +LOADER load_mod={ + NULL, + "Standard module", + "Portable MOD loader v0.11", + MOD_Init, + MOD_Test, + MOD_Load, + MOD_Cleanup +}; diff --git a/libs/oldmik/src/load_mtm.c b/libs/oldmik/src/load_mtm.c new file mode 100644 index 0000000..b7ad052 --- /dev/null +++ b/libs/oldmik/src/load_mtm.c @@ -0,0 +1,285 @@ +/* + +Name: +LOAD_MTM.C + +Description: +MTM module loader + +Portability: +All systems - all compilers (hopefully) + +*/ +#include +#include +#include +#include +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + + +typedef struct MTMSAMPLE{ + char samplename[22]; + ULONG length; + ULONG reppos; + ULONG repend; + UBYTE finetune; + UBYTE volume; + UBYTE attribute; +} MTMSAMPLE; + + + +typedef struct MTMHEADER{ + UBYTE id[3]; /* MTM file marker */ + UBYTE version; /* upper major, lower nibble minor version number */ + char songname[20]; /* ASCIIZ songname */ + UWORD numtracks; /* number of tracks saved */ + UBYTE lastpattern; /* last pattern number saved */ + UBYTE lastorder; /* last order number to play (songlength-1) */ + UWORD commentsize; /* length of comment field */ + UBYTE numsamples; /* number of samples saved */ + UBYTE attribute; /* attribute byte (unused) */ + UBYTE beatspertrack; /* */ + UBYTE numchannels; /* number of channels used */ + UBYTE panpos[32]; /* voice pan positions */ +} MTMHEADER; + + +typedef struct MTMNOTE{ + UBYTE a,b,c; +} MTMNOTE; + + +/************************************************************************** +**************************************************************************/ + + + +static MTMHEADER *mh; +MTMNOTE *mtmtrk; +UWORD pat[32]; + +char MTM_Version[]="MTM"; + + + +BOOL MTM_Test(void) +{ + char id[3]; + if(!fread(id,3,1,modfp)) return 0; + if(!memcmp(id,"MTM",3)) return 1; + return 0; +} + + +BOOL MTM_Init(void) +{ + mtmtrk=NULL; + mh=NULL; + + if(!(mtmtrk=(MTMNOTE *)MyCalloc(64,sizeof(MTMNOTE)))) return 0; + if(!(mh=(MTMHEADER *)MyCalloc(1,sizeof(MTMHEADER)))) return 0; + + return 1; +} + + +void MTM_Cleanup(void) +{ + if(mtmtrk!=NULL) free(mtmtrk); + if(mh!=NULL) free(mh); +} + + + +UBYTE *MTM_Convert(void) +{ + int t; + UBYTE a,b,c,inst,note,eff,dat; + + UniReset(); + for(t=0;t<64;t++){ + + a=mtmtrk[t].a; + b=mtmtrk[t].b; + c=mtmtrk[t].c; + + inst=((a&0x3)<<4)|(b>>4); + note=a>>2; + + eff=b&0xf; + dat=c; + + + if(inst!=0){ + UniInstrument(inst-1); + } + + if(note!=0){ + UniNote(note+24); + } + + /* mtm bug bugfix: when the effect is volslide, + slide-up _always_ overrides slide-dn. */ + + if(eff==0xa && dat&0xf0) dat&=0xf0; + + UniPTEffect(eff,dat); + UniNewline(); + } + return UniDup(); +} + + +BOOL MTM_Load(void) +{ + MTMSAMPLE s; + INSTRUMENT *d; + SAMPLE *q; + + int t,u; + + /* try to read module header */ + + _mm_read_UBYTES(mh->id,3,modfp); + mh->version =_mm_read_UBYTE(modfp); + _mm_read_str(mh->songname,20,modfp); + mh->numtracks =_mm_read_I_UWORD(modfp); + mh->lastpattern =_mm_read_UBYTE(modfp); + mh->lastorder =_mm_read_UBYTE(modfp); + mh->commentsize =_mm_read_I_UWORD(modfp); + mh->numsamples =_mm_read_UBYTE(modfp); + mh->attribute =_mm_read_UBYTE(modfp); + mh->beatspertrack=_mm_read_UBYTE(modfp); + mh->numchannels =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->panpos,32,modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + + of.initspeed=6; + of.inittempo=125; + of.modtype=strdup(MTM_Version); + of.numchn=mh->numchannels; + of.numtrk=mh->numtracks+1; /* get number of channels */ + of.songname=DupStr(mh->songname,20); /* make a cstr of songname */ + of.numpos=mh->lastorder+1; /* copy the songlength */ + of.numpat=mh->lastpattern+1; + for(t=0;t<32;t++) of.panning[t]=mh->panpos[t]<<4; + + of.numins=mh->numsamples; + if(!AllocInstruments()) return 0; + + d=of.instruments; + + for(t=0;tnumsmp=1; + if(!AllocSamples(d)) return 0; + q=d->samples; + + /* try to read sample info */ + + _mm_read_str(s.samplename,22,modfp); + s.length =_mm_read_I_ULONG(modfp); + s.reppos =_mm_read_I_ULONG(modfp); + s.repend =_mm_read_I_ULONG(modfp); + s.finetune =_mm_read_UBYTE(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.attribute =_mm_read_UBYTE(modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_SAMPLEINFO; + return 0; + } + + d->insname=DupStr(s.samplename,22); + q->seekpos=0; + q->c2spd=finetune[s.finetune]; + q->length=s.length; + q->loopstart=s.reppos; + q->loopend=s.repend; + q->volume=s.volume; + + q->flags=0; + + if(s.repend-s.reppos>2) q->flags|=SF_LOOP; /* <- 1.00 bugfix */ + + if(s.attribute&1){ + + /* If the sample is 16-bits, convert the length + and replen byte-values into sample-values */ + + q->flags|=SF_16BITS; + q->length>>=1; + q->loopstart>>=1; + q->loopend>>=1; + } + + d++; + } + + _mm_read_UBYTES(of.positions,128,modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + of.tracks[0]=MTM_Convert(); /* track 0 is empty */ + + for(t=1;tcommentsize)) return 0; + + return 1; +} + + + +LOADER load_mtm={ + NULL, + "MTM", + "Portable MTM loader v0.1", + MTM_Init, + MTM_Test, + MTM_Load, + MTM_Cleanup +}; + diff --git a/libs/oldmik/src/load_s3m.c b/libs/oldmik/src/load_s3m.c new file mode 100644 index 0000000..07425ec --- /dev/null +++ b/libs/oldmik/src/load_s3m.c @@ -0,0 +1,570 @@ +/* + +Name: +LOAD_S3M.C + +Description: +Screamtracker (S3M) module loader + +Portability: +All systems - all compilers (hopefully) + +*/ +#include +#include +#include +#include +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + +typedef struct S3MNOTE{ + UBYTE note,ins,vol,cmd,inf; +} S3MNOTE; + +typedef S3MNOTE S3MTRACK[64]; + + +/* Raw S3M header struct: */ + +typedef struct S3MHEADER{ + char songname[28]; + char t1a; + char type; + UBYTE unused1[2]; + UWORD ordnum; + UWORD insnum; + UWORD patnum; + UWORD flags; + UWORD tracker; + UWORD fileformat; + char scrm[4]; + UBYTE mastervol; + UBYTE initspeed; + UBYTE inittempo; + UBYTE mastermult; + UBYTE ultraclick; + UBYTE pantable; + UBYTE unused2[8]; + UWORD special; + UBYTE channels[32]; +} S3MHEADER; + + +/* Raw S3M sampleinfo struct: */ + +typedef struct S3MSAMPLE{ + UBYTE type; + char filename[12]; + UBYTE memsegh; + UWORD memsegl; + ULONG length; + ULONG loopbeg; + ULONG loopend; + UBYTE volume; + UBYTE dsk; + UBYTE pack; + UBYTE flags; + ULONG c2spd; + UBYTE unused[12]; + char sampname[28]; + char scrs[4]; +} S3MSAMPLE; + + +/************************************************************************** +**************************************************************************/ + + + +S3MNOTE *s3mbuf; /* pointer to a complete S3M pattern */ +UWORD *paraptr; /* parapointer array (see S3M docs) */ +static S3MHEADER *mh; +UBYTE remap[32]; + + +char S3M_Version[]="Screamtracker 3.xx"; + + + +BOOL S3M_Test(void) +{ + char id[4]; + _mm_fseek(modfp,0x2c,SEEK_SET); + if(!fread(id,4,1,modfp)) return 0; + if(!memcmp(id,"SCRM",4)) return 1; + return 0; +} + +BOOL S3M_Init(void) +{ + s3mbuf=NULL; + paraptr=NULL; + + if(!(s3mbuf=(S3MNOTE *)MyMalloc(16*64*sizeof(S3MNOTE)))) return 0; + if(!(mh=(S3MHEADER *)MyCalloc(1,sizeof(S3MHEADER)))) return 0; + + return 1; +} + +void S3M_Cleanup(void) +{ + if(s3mbuf!=NULL) free(s3mbuf); + if(paraptr!=NULL) free(paraptr); + if(mh!=NULL) free(mh); +} + + + + +BOOL S3M_ReadPattern(void) +{ + int row=0,flag,ch; + S3MNOTE *n; + S3MNOTE dummy; + + /* clear pattern data */ + + memset(s3mbuf,255,16*64*sizeof(S3MNOTE)); + + while(row<64){ + + flag=fgetc(modfp); + + if(flag==EOF){ + myerr="Error loading pattern"; + return 0; + } + + if(flag){ + + ch=flag&31; + + if(mh->channels[ch]<16){ + n=&s3mbuf[(64U*remap[ch])+row]; + } + else{ + n=&dummy; + } + + if(flag&32){ + n->note=fgetc(modfp); + n->ins=fgetc(modfp); + } + + if(flag&64){ + n->vol=fgetc(modfp); + } + + if(flag&128){ + n->cmd=fgetc(modfp); + n->inf=fgetc(modfp); + } + } + else row++; + } + return 1; +} + + + +UBYTE *S3M_ConvertTrack(S3MNOTE *tr) +{ + int t; + + UBYTE note,ins,vol,cmd,inf,lo,hi; + + UniReset(); + for(t=0;t<64;t++){ + + note=tr[t].note; + ins=tr[t].ins; + vol=tr[t].vol; + cmd=tr[t].cmd; + inf=tr[t].inf; + lo=inf&0xf; + hi=inf>>4; + + + if(ins!=0 && ins!=255){ + UniInstrument(ins-1); + } + + if(note!=255){ + if(note==254) UniPTEffect(0xc,0); /* <- note off command */ + else UniNote(((note>>4)*12)+(note&0xf)); /* <- normal note */ + } + + if(vol<255){ + UniPTEffect(0xc,vol); +/* UniWrite(UNI_S3MVOLUME); */ +/* UniWrite(vol); */ + } + + if(cmd!=255){ + switch(cmd){ + + case 1: /* Axx set speed to xx */ + UniWrite(UNI_S3MEFFECTA); + UniWrite(inf); + break; + + case 2: /* Bxx position jump */ + UniPTEffect(0xb,inf); + break; + + case 3: /* Cxx patternbreak to row xx */ + UniPTEffect(0xd,inf); + break; + + case 4: /* Dxy volumeslide */ + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 5: /* Exy toneslide down */ + UniWrite(UNI_S3MEFFECTE); + UniWrite(inf); + break; + + case 6: /* Fxy toneslide up */ + UniWrite(UNI_S3MEFFECTF); + UniWrite(inf); + break; + + case 7: /* Gxx Tone portamento,speed xx */ + UniPTEffect(0x3,inf); + break; + + case 8: /* Hxy vibrato */ + UniPTEffect(0x4,inf); + break; + + case 9: /* Ixy tremor, ontime x, offtime y */ + UniWrite(UNI_S3MEFFECTI); + UniWrite(inf); + break; + + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + + case 0xb: /* Kxy Dual command H00 & Dxy */ + UniPTEffect(0x4,0); + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 0xc: /* Lxy Dual command G00 & Dxy */ + UniPTEffect(0x3,0); + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 0xf: /* Oxx set sampleoffset xx00h */ + UniPTEffect(0x9,inf); + break; + + case 0x11: /* Qxy Retrig (+volumeslide) */ + UniWrite(UNI_S3MEFFECTQ); + UniWrite(inf); + break; + + case 0x12: /* Rxy tremolo speed x, depth y */ + UniPTEffect(0x6,inf); + break; + + case 0x13: /* Sxx special commands */ + switch(hi){ + + case 0: /* S0x set filter */ + UniPTEffect(0xe,0x00|lo); + break; + + case 1: /* S1x set glissando control */ + UniPTEffect(0xe,0x30|lo); + break; + + case 2: /* S2x set finetune */ + UniPTEffect(0xe,0x50|lo); + break; + + case 3: /* S3x set vibrato waveform */ + UniPTEffect(0xe,0x40|lo); + break; + + case 4: /* S4x set tremolo waveform */ + UniPTEffect(0xe,0x70|lo); + break; + + case 8: /* S8x set panning position */ + UniPTEffect(0xe,0x80|lo); + break; + + case 0xb: /* SBx pattern loop */ + UniPTEffect(0xe,0x60|lo); + break; + + case 0xc: /* SCx notecut */ + UniPTEffect(0xe,0xC0|lo); + break; + + case 0xd: /* SDx notedelay */ + UniPTEffect(0xe,0xD0|lo); + break; + + case 0xe: /* SDx patterndelay */ + UniPTEffect(0xe,0xE0|lo); + break; + } + break; + + case 0x14: /* Txx tempo */ + if(inf>0x20){ + UniWrite(UNI_S3MEFFECTT); + UniWrite(inf); + } + break; + + case 0x18: /* Xxx amiga command 8xx */ + UniPTEffect(0x8,inf); + break; + } + } + + UniNewline(); + } + return UniDup(); +} + + + + +BOOL S3M_Load(void) +{ + int t,u,track=0; + INSTRUMENT *d; + SAMPLE *q; + UBYTE isused[16]; + UBYTE pan[32]; + + /* try to read module header */ + + _mm_read_str(mh->songname,28,modfp); + mh->t1a =_mm_read_UBYTE(modfp); + mh->type =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->unused1,2,modfp); + mh->ordnum =_mm_read_I_UWORD(modfp); + mh->insnum =_mm_read_I_UWORD(modfp); + mh->patnum =_mm_read_I_UWORD(modfp); + mh->flags =_mm_read_I_UWORD(modfp); + mh->tracker =_mm_read_I_UWORD(modfp); + mh->fileformat =_mm_read_I_UWORD(modfp); + _mm_read_str(mh->scrm,4,modfp); + + mh->mastervol =_mm_read_UBYTE(modfp); + mh->initspeed =_mm_read_UBYTE(modfp); + mh->inittempo =_mm_read_UBYTE(modfp); + mh->mastermult =_mm_read_UBYTE(modfp); + mh->ultraclick =_mm_read_UBYTE(modfp); + mh->pantable =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->unused2,8,modfp); + mh->special =_mm_read_I_UWORD(modfp); + _mm_read_UBYTES(mh->channels,32,modfp); + + if(feof(modfp)){ + myerr="Error loading header"; + return 0; + } + + /* set module variables */ + + of.modtype=strdup(S3M_Version); + of.songname=DupStr(mh->songname,28); /* make a cstr of songname */ + of.numpat=mh->patnum; + of.numins=mh->insnum; + of.initspeed=mh->initspeed; + of.inittempo=mh->inittempo; + + /* count the number of channels used */ + + of.numchn=0; + +/* for(t=0;t<32;t++) printf("%2.2x ",mh->channels[t]); +*/ + for(t=0;t<32;t++) remap[t]=0; + for(t=0;t<16;t++) isused[t]=0; + + /* set a flag for each channel (1 out of of 16) thats being used: */ + + for(t=0;t<32;t++){ + if(mh->channels[t]<16){ + isused[mh->channels[t]]=1; + } + } + + /* give each of them a different number */ + + for(t=0;t<16;t++){ + if(isused[t]){ + isused[t]=of.numchn; + of.numchn++; + } + } + + /* build the remap array */ + + for(t=0;t<32;t++){ + if(mh->channels[t]<16){ + remap[t]=isused[mh->channels[t]]; + } + } + + /* set panning positions */ + + for(t=0;t<32;t++){ + if(mh->channels[t]<16){ + if(mh->channels[t]<8){ + of.panning[remap[t]]=0x30; + } + else{ + of.panning[remap[t]]=0xc0; + } + } + } + + of.numtrk=of.numpat*of.numchn; + +/* printf("Uses %d channels\n",of.numchn); +*/ + /* read the order data */ + + _mm_read_UBYTES(of.positions,mh->ordnum,modfp); + + of.numpos=0; + for(t=0;tordnum;t++){ + of.positions[of.numpos]=of.positions[t]; + if(of.positions[t]<254) of.numpos++; + } + + if((paraptr=(UWORD *)MyMalloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0; + + /* read the instrument+pattern parapointers */ + + _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp); + +/* printf("pantab %d\n",mh->pantable); +*/ + if(mh->pantable==252){ + + /* read the panning table */ + + _mm_read_UBYTES(pan,32,modfp); + + /* set panning positions according to panning table (new for st3.2) */ + + for(t=0;t<32;t++){ + if((pan[t]&0x20) && mh->channels[t]<16){ + of.panning[remap[t]]=(pan[t]&0xf)<<4; + } + } + } + + /* now is a good time to check if the header was too short :) */ + + if(feof(modfp)){ + myerr="Error loading header"; + return 0; + } + + if(!AllocInstruments()) return 0; + + d=of.instruments; + + for(t=0;tnumsmp=1; + if(!AllocSamples(d)) return 0; + q=d->samples; + + /* seek to instrument position */ + + _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET); + + /* and load sample info */ + + s.type =_mm_read_UBYTE(modfp); + _mm_read_str(s.filename,12,modfp); + s.memsegh =_mm_read_UBYTE(modfp); + s.memsegl =_mm_read_I_UWORD(modfp); + s.length =_mm_read_I_ULONG(modfp); + s.loopbeg =_mm_read_I_ULONG(modfp); + s.loopend =_mm_read_I_ULONG(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.dsk =_mm_read_UBYTE(modfp); + s.pack =_mm_read_UBYTE(modfp); + s.flags =_mm_read_UBYTE(modfp); + s.c2spd =_mm_read_I_ULONG(modfp); + _mm_read_UBYTES(s.unused,12,modfp); + _mm_read_str(s.sampname,28,modfp); + _mm_read_str(s.scrs,4,modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + d->insname=DupStr(s.sampname,28); + q->c2spd=s.c2spd; + q->length=s.length; + q->loopstart=s.loopbeg; + q->loopend=s.loopend; + q->volume=s.volume; + q->seekpos=(((long)s.memsegh)<<16|s.memsegl)<<4; + + q->flags=0; + + if(s.flags&1) q->flags|=SF_LOOP; + if(s.flags&4) q->flags|=SF_16BITS; + if(mh->fileformat==1) q->flags|=SF_SIGNED; + + /* DON'T load sample if it doesn't have the SCRS tag */ + + if(memcmp(s.scrs,"SCRS",4)!=0) q->length=0; + +/* printf("%s\n",d->insname); +*/ + d++; + } + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0;t +#include +#include +#include +#include "mikmod.h" + +typedef struct STMNOTE{ + UBYTE note,insvol,volcmd,cmdinf; +} STMNOTE; + + +/* Raw STM sampleinfo struct: */ + +typedef struct STMSAMPLE{ + char filename[12]; /* Can't have long comments - just filename comments :) */ + char unused; /* 0x00 */ + UBYTE instdisk; /* Instrument disk */ + UWORD reserved; /* ISA in memory when in ST 2 */ + UWORD length; /* Sample length */ + UWORD loopbeg; /* Loop start point */ + UWORD loopend; /* Loop end point */ + UBYTE volume; /* Volume */ + UBYTE reserved2; /* More reserved crap */ + UWORD c2spd; /* Good old c2spd */ + UBYTE reserved3[4]; /* Yet more of PSi's reserved crap */ + UWORD isa; /* Internal Segment Address -> */ + /* contrary to the tech specs, this is NOT actually */ + /* written to the stm file. */ +} STMSAMPLE; + +/* Raw STM header struct: */ + +typedef struct STMHEADER{ + char songname[20]; + char trackername[8]; /* !SCREAM! for ST 2.xx */ + char unused; /* 0x1A */ + char filetype; /* 1=song, 2=module (only 2 is supported, of course) :) */ + char ver_major; /* Like 2 */ + char ver_minor; /* "ditto" */ + UBYTE inittempo; /* initspeed= stm inittempo>>4 */ + UBYTE numpat; /* number of patterns */ + UBYTE globalvol; /* <- WoW! a RiGHT TRiANGLE =8*) */ + UBYTE reserved[13]; /* More of PSi's internal crap */ + STMSAMPLE sample[31]; /* STM sample data */ + UBYTE patorder[128]; /* Docs say 64 - actually 128 */ +} STMHEADER; + + +static STMNOTE *stmbuf; +static STMHEADER *mh; + +char STM_Version[]="Screamtracker 2"; + + + +BOOL STM_Test(void) +{ + char str[9],filetype; + + _mm_fseek(modfp,21,SEEK_SET); + fread(str,1,9,modfp); + fread(&filetype,1,1,modfp); + if(!memcmp(str,"!SCREAM!",8) || (filetype!=2)) /* STM Module = filetype 2 */ + return 0; + return 1; +} + + + +BOOL STM_Init(void) +{ + stmbuf=NULL; + if(!(mh=(STMHEADER *)MyCalloc(1,sizeof(STMHEADER)))) return 0; + return 1; +} + +void STM_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(stmbuf!=NULL) free(stmbuf); +} + + + +void STM_ConvertNote(STMNOTE *n) +{ + UBYTE note,ins,vol,cmd,inf; + + /* extract the various information from the 4 bytes that + make up a single note */ + + note=n->note; + ins=n->insvol>>3; + vol=(n->insvol&7)+(n->volcmd>>1); + cmd=n->volcmd&15; + inf=n->cmdinf; + + if(ins!=0 && ins<32){ + UniInstrument(ins-1); + } + + /* special values of [SBYTE0] are handled here -> */ + /* we have no idea if these strange values will ever be encountered */ + /* but it appears as though stms sound correct. */ + if(note==254 || note==252) UniPTEffect(0xc,0); /* <- note off command (???) */ + else + /* if note < 251, then all three bytes are stored in the file */ + if(note<251) UniNote((((note>>4)+2)*12)+(note&0xf)); /* <- normal note and up the octave by two */ + + if(vol<65){ + UniPTEffect(0xc,vol); + } + + if(cmd!=255){ + switch(cmd){ + + case 1: /* Axx set speed to xx and add 0x1c to fix StoOoPiD STM 2.x */ + UniPTEffect(0xf,inf>>4); + break; + + case 2: /* Bxx position jump */ + UniPTEffect(0xb,inf); + break; + + case 3: /* Cxx patternbreak to row xx */ + UniPTEffect(0xd,inf); + break; + + case 4: /* Dxy volumeslide */ + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 5: /* Exy toneslide down */ + UniWrite(UNI_S3MEFFECTE); + UniWrite(inf); + break; + + case 6: /* Fxy toneslide up */ + UniWrite(UNI_S3MEFFECTF); + UniWrite(inf); + break; + + case 7: /* Gxx Tone portamento,speed xx */ + UniPTEffect(0x3,inf); + break; + + case 8: /* Hxy vibrato */ + UniPTEffect(0x4,inf); + break; + + case 9: /* Ixy tremor, ontime x, offtime y */ + UniWrite(UNI_S3MEFFECTI); + UniWrite(inf); + break; + + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + + case 0xb: /* Kxy Dual command H00 & Dxy */ + UniPTEffect(0x4,0); + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 0xc: /* Lxy Dual command G00 & Dxy */ + UniPTEffect(0x3,0); + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + /* Support all these above, since ST2 can LOAD these values */ + /* but can actually only play up to J - and J is only */ + /* half-way implemented in ST2 */ + + case 0x18: /* Xxx amiga command 8xx - What the hell, support panning. :) */ + UniPTEffect(0x8,inf); + break; + } + } + +} + + +UBYTE *STM_ConvertTrack(STMNOTE *n) +{ + int t; + + UniReset(); + for(t=0;t<64;t++) + { STM_ConvertNote(n); + UniNewline(); + n+=of.numchn; + } + return UniDup(); +} + + + + +BOOL STM_LoadPatterns(void) +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + /* Allocate temporary buffer for loading + and converting the patterns */ + + if(!(stmbuf=(STMNOTE *)MyCalloc(64U*of.numchn,sizeof(STMNOTE)))) return 0; + + for(t=0;tsongname,20,modfp); + _mm_read_str(mh->trackername,8,modfp); + mh->unused =_mm_read_UBYTE(modfp); + mh->filetype =_mm_read_UBYTE(modfp); + mh->ver_major =_mm_read_UBYTE(modfp); + mh->ver_minor =_mm_read_UBYTE(modfp); + mh->inittempo =_mm_read_UBYTE(modfp); + mh->numpat =_mm_read_UBYTE(modfp); + mh->globalvol =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->reserved,13,modfp); + + for(t=0;t<31;t++){ + STMSAMPLE *s=&mh->sample[t]; /* STM sample data */ + _mm_read_str(s->filename,12,modfp); + s->unused =_mm_read_UBYTE(modfp); + s->instdisk =_mm_read_UBYTE(modfp); + s->reserved =_mm_read_I_UWORD(modfp); + s->length =_mm_read_I_UWORD(modfp); + s->loopbeg =_mm_read_I_UWORD(modfp); + s->loopend =_mm_read_I_UWORD(modfp); + s->volume =_mm_read_UBYTE(modfp); + s->reserved2=_mm_read_UBYTE(modfp); + s->c2spd =_mm_read_I_UWORD(modfp); + _mm_read_UBYTES(s->reserved3,4,modfp); + s->isa =_mm_read_I_UWORD(modfp); + } + _mm_read_UBYTES(mh->patorder,128,modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + + of.modtype=strdup(STM_Version); + of.songname=DupStr(mh->songname,20); /* make a cstr of songname */ + + of.numpat=mh->numpat; + + of.initspeed=6; /* Always this */ + + /* STM 2.x tempo has always been fucked... The default of 96 */ + /* is actually 124, so we add 1ch to the initial value of 60h */ + + /* MikMak: No it's not.. STM tempo is UNI speed << 4 */ + + of.inittempo=125; /* mh->inittempo+0x1c; */ + of.initspeed=mh->inittempo>>4; + of.numchn=4; /* get number of channels */ + + t=0; + while(mh->patorder[t]!=99){ /* 99 terminates the patorder list */ + of.positions[t]=mh->patorder[t]; + t++; + } + of.numpos=--t; + of.numtrk=of.numpat*of.numchn; + + /* Finally, init the sampleinfo structures */ + + of.numins=31; /* always this */ + + if(!AllocInstruments()) return 0; + if(!STM_LoadPatterns()) return 0; + + d=of.instruments; + + MikMod_ISA=ftell(modfp); + MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; + + for(t=0;tnumsmp=1; + if(!AllocSamples(d)) return 0; + q=d->samples; + + /* load sample info */ + + d->insname=DupStr(mh->sample[t].filename,12); + q->c2spd=mh->sample[t].c2spd; + q->volume=mh->sample[t].volume; + q->length=mh->sample[t].length; + if (!mh->sample[t].volume || q->length==1 ) q->length = 0; /* if vol = 0 or length = 1, then no sample */ + q->loopstart=mh->sample[t].loopbeg; + q->loopend=mh->sample[t].loopend; + q->seekpos=MikMod_ISA; + + MikMod_ISA+=q->length; + + MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; + + /* Once again, contrary to the STM specs, all the sample data is */ + /* actually SIGNED! Sheesh */ + + q->flags=SF_SIGNED; + + if(mh->sample[t].loopend>0 && mh->sample[t].loopend!=0xffff) q->flags|=SF_LOOP; + + /* fix replen if repend>length */ + + if(q->loopend>q->length) q->loopend=q->length; + + d++; + } + + return 1; +} + + +LOADER load_stm={ + NULL, + "STM", + "Portable STM Loader - V 1.2 - A Coding Nightmare by Rao and Air Richter of HaRDCoDE", + STM_Init, + STM_Test, + STM_Load, + STM_Cleanup +}; diff --git a/libs/oldmik/src/load_ult.c b/libs/oldmik/src/load_ult.c new file mode 100644 index 0000000..a60325a --- /dev/null +++ b/libs/oldmik/src/load_ult.c @@ -0,0 +1,313 @@ +/* + +Name: +LOAD_ULT.C + +Description: +Ultratracker (ULT) module loader + +Portability: +All systems - all compilers (hopefully) + +*/ +#include +#include +#include +#include +#include "mikmod.h" + +#define ULTS_16BITS 4 +#define ULTS_LOOP 8 +#define ULTS_REVERSE 16 + + +/* Raw ULT header struct: */ + +typedef struct ULTHEADER{ + char id[15]; + char songtitle[32]; + UBYTE reserved; +} ULTHEADER; + + +/* Raw ULT sampleinfo struct: */ + +typedef struct ULTSAMPLE{ + char samplename[32]; + char dosname[12]; + SLONG loopstart; + SLONG loopend; + SLONG sizestart; + SLONG sizeend; + UBYTE volume; + UBYTE flags; + SWORD finetune; +} ULTSAMPLE; + + +typedef struct ULTEVENT{ + UBYTE note,sample,eff,dat1,dat2; +} ULTEVENT; + + +char *ULT_Version[]={ + "Ultra Tracker V1.3", + "Ultra Tracker V1.4", + "Ultra Tracker V1.5", + "Ultra Tracker V1.6" +}; + + +BOOL ULT_Test(void) +{ + char id[15]; + + if(!fread(&id,15,1,modfp)) return 0; + return(!strncmp(id,"MAS_UTrack_V00",14)); +} + + +BOOL ULT_Init(void) +{ + return 1; +} + + +void ULT_Cleanup(void) +{ +} + +ULTEVENT ev; + + + +int ReadUltEvent(ULTEVENT *event) +{ + UBYTE flag,rep=1; + + flag=_mm_read_UBYTE(modfp); + + if(flag==0xfc){ + fread(&rep,1,1,modfp); + event->note =_mm_read_UBYTE(modfp); + } + else{ + event->note=flag; + } + + event->sample =_mm_read_UBYTE(modfp); + event->eff =_mm_read_UBYTE(modfp); + event->dat1 =_mm_read_UBYTE(modfp); + event->dat2 =_mm_read_UBYTE(modfp); + + return rep; +} + + + + +BOOL ULT_Load(void) +{ + int t,u,tracks=0; + INSTRUMENT *d; + SAMPLE *q; + ULTSAMPLE s; + ULTHEADER mh; + UBYTE nos,noc,nop; + + /* try to read module header */ + + _mm_read_str(mh.id,15,modfp); + _mm_read_str(mh.songtitle,32,modfp); + mh.reserved=_mm_read_UBYTE(modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + if(mh.id[14]<'1' || mh.id[14]>'4'){ + printf("This version is not yet supported\n"); + return 0; + } + + of.modtype=strdup(ULT_Version[mh.id[14]-'1']); + of.initspeed=6; + of.inittempo=125; + + /* read songtext */ + + if(!ReadComment((UWORD)mh.reserved*32)) return 0; + + nos=_mm_read_UBYTE(modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + of.songname=DupStr(mh.songtitle,32); + of.numins=nos; + + if(!AllocInstruments()) return 0; + + d=of.instruments; + + for(t=0;tnumsmp=1; + if(!AllocSamples(d)) return 0; + q=d->samples; + + /* try to read sample info */ + + _mm_read_str(s.samplename,32,modfp); + _mm_read_str(s.dosname,12,modfp); + s.loopstart =_mm_read_I_ULONG(modfp); + s.loopend =_mm_read_I_ULONG(modfp); + s.sizestart =_mm_read_I_ULONG(modfp); + s.sizeend =_mm_read_I_ULONG(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.flags =_mm_read_UBYTE(modfp); + s.finetune =_mm_read_I_SWORD(modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_SAMPLEINFO; + return 0; + } + + d->insname=DupStr(s.samplename,32); + + q->seekpos=0; + + q->c2spd=8363; + + if(mh.id[14]>='4'){ + _mm_read_I_UWORD(modfp); /* read 1.6 extra info(??) word */ + q->c2spd=s.finetune; + } + + q->length=s.sizeend-s.sizestart; + q->volume=s.volume>>2; + q->loopstart=s.loopstart; + q->loopend=s.loopend; + + q->flags=SF_SIGNED; + + if(s.flags&ULTS_LOOP){ + q->flags|=SF_LOOP; + } + + if(s.flags&ULTS_16BITS){ + q->flags|=SF_16BITS; + q->loopstart>>=1; + q->loopend>>=1; + } + +/* printf("Sample %d %s length %ld\n",t,d->samplename,d->length); */ + d++; + } + + _mm_read_UBYTES(of.positions,256,modfp); + + for(t=0;t<256;t++){ + if(of.positions[t]==255) break; + } + of.numpos=t; + + noc=_mm_read_UBYTE(modfp); + nop=_mm_read_UBYTE(modfp); + + of.numchn=noc+1; + of.numpat=nop+1; + of.numtrk=of.numchn*of.numpat; + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(u=0;u='3'){ + for(t=0;t>4; + + + /* + ULT panning effect fixed by Alexander Kerkhove : + */ + + + if(eff==0xc) UniPTEffect(eff,ev.dat2>>2); + else if(eff==0xb) UniPTEffect(8,ev.dat2*0xf); + else UniPTEffect(eff,ev.dat2); + + eff=ev.eff&0xf; + + if(eff==0xc) UniPTEffect(eff,ev.dat1>>2); + else if(eff==0xb) UniPTEffect(8,ev.dat1*0xf); + else UniPTEffect(eff,ev.dat1); + + UniNewline(); + done++; + } + } +/* printf("----------------"); */ + + if(!(of.tracks[t]=UniDup())) return 0; + } + +/* printf("%d channels %d patterns\n",of.numchn,of.numpat); */ +/* printf("Song %32.32s: There's %d samples\n",mh.songtitle,nos); */ + return 1; +} + + + +LOADER load_ult={ + NULL, + "ULT", + "Portable ULT loader v0.1", + ULT_Init, + ULT_Test, + ULT_Load, + ULT_Cleanup +}; diff --git a/libs/oldmik/src/load_uni.c b/libs/oldmik/src/load_uni.c new file mode 100644 index 0000000..27b7407 --- /dev/null +++ b/libs/oldmik/src/load_uni.c @@ -0,0 +1,190 @@ +/* + +Name: +LOAD_UNI.C + +Description: +UNIMOD (mikmod's internal format) module loader. + +Portability: +All systems - all compilers (hopefully) + +*/ +#include +#include +#include +#include "mikmod.h" + + +BOOL UNI_Test(void) +{ + char id[4]; + if(!fread(id,4,1,modfp)) return 0; + if(!memcmp(id,"UN05",4)) return 1; + return 0; +} + + +BOOL UNI_Init(void) +{ + return 1; +} + + +void UNI_Cleanup(void) +{ + ; +} + + +char *StrRead(void) +{ + char *s; + UWORD len; + + len=_mm_read_I_UWORD(modfp); + if(!len) return NULL; + + s=(char *)malloc(len+1); + fread(s,len,1,modfp); + s[len]=0; + + return s; +} + + +UBYTE *TrkRead(void) +{ + UBYTE *t; + UWORD len; + + len=_mm_read_I_UWORD(modfp); + t=(UBYTE *)malloc(len); + fread(t,len,1,modfp); + return t; +} + + + +BOOL UNI_Load(void) +{ + int t,u; + + _mm_fseek(modfp,4,SEEK_SET); + + /* try to read module header */ + + of.numchn =_mm_read_UBYTE(modfp); + of.numpos =_mm_read_I_UWORD(modfp); + of.reppos =_mm_read_I_UWORD(modfp); + of.numpat =_mm_read_I_UWORD(modfp); + of.numtrk =_mm_read_I_UWORD(modfp); + of.numins =_mm_read_I_UWORD(modfp); + of.initspeed=_mm_read_UBYTE(modfp); + of.inittempo=_mm_read_UBYTE(modfp); + _mm_read_UBYTES(of.positions,256,modfp); + _mm_read_UBYTES(of.panning,32,modfp); + of.flags =_mm_read_UBYTE(modfp); + + if(feof(modfp)){ + myerr=ERROR_LOADING_HEADER; + return 0; + } + + of.songname=StrRead(); + of.modtype=StrRead(); + of.comment=StrRead(); /* <- new since UN01 */ + +/* printf("Song: %s\nModty: %s\n",of.songname,of.modtype); +*/ + + if(!AllocInstruments()) return 0; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + /* Read sampleinfos */ + + for(t=0;tnumsmp=_mm_read_UBYTE(modfp); + _mm_read_UBYTES(i->samplenumber,96,modfp); + + i->volflg=_mm_read_UBYTE(modfp); + i->volpts=_mm_read_UBYTE(modfp); + i->volsus=_mm_read_UBYTE(modfp); + i->volbeg=_mm_read_UBYTE(modfp); + i->volend=_mm_read_UBYTE(modfp); + + for(u=0;u<12;u++){ + i->volenv[u].pos=_mm_read_I_SWORD(modfp); + i->volenv[u].val=_mm_read_I_SWORD(modfp); + } + + i->panflg=_mm_read_UBYTE(modfp); + i->panpts=_mm_read_UBYTE(modfp); + i->pansus=_mm_read_UBYTE(modfp); + i->panbeg=_mm_read_UBYTE(modfp); + i->panend=_mm_read_UBYTE(modfp); + + for(u=0;u<12;u++){ + i->panenv[u].pos=_mm_read_I_SWORD(modfp); + i->panenv[u].val=_mm_read_I_SWORD(modfp); + } + + i->vibtype =_mm_read_UBYTE(modfp); + i->vibsweep =_mm_read_UBYTE(modfp); + i->vibdepth =_mm_read_UBYTE(modfp); + i->vibrate =_mm_read_UBYTE(modfp); + + i->volfade =_mm_read_I_UWORD(modfp); + i->insname =StrRead(); + +/* printf("Ins: %s\n",i->insname); +*/ + if(!AllocSamples(i)) return 0; + + for(u=0;unumsmp;u++){ + + SAMPLE *s=&i->samples[u]; + + s->c2spd = _mm_read_I_UWORD(modfp); + s->transpose= _mm_read_SBYTE(modfp); + s->volume = _mm_read_UBYTE(modfp); + s->panning = _mm_read_UBYTE(modfp); + s->length = _mm_read_I_ULONG(modfp); + s->loopstart= _mm_read_I_ULONG(modfp); + s->loopend = _mm_read_I_ULONG(modfp); + s->flags = _mm_read_I_UWORD(modfp); + s->seekpos = 0; + + s->samplename=StrRead(); + } + } + + /* Read patterns */ + + _mm_read_I_UWORDS(of.pattrows,of.numpat,modfp); + _mm_read_I_UWORDS(of.patterns,of.numpat*of.numchn,modfp); + + /* Read tracks */ + + for(t=0;t +#include +#include +#include +#include +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + + +typedef struct XMHEADER{ + char id[17]; /* ID text: 'Extended module: ' */ + char songname[21]; /* Module name, padded with zeroes and 0x1a at the end */ + char trackername[20]; /* Tracker name */ + UWORD version; /* (word) Version number, hi-byte major and low-byte minor */ + ULONG headersize; /* Header size */ + UWORD songlength; /* (word) Song length (in patten order table) */ + UWORD restart; /* (word) Restart position */ + UWORD numchn; /* (word) Number of channels (2,4,6,8,10,...,32) */ + UWORD numpat; /* (word) Number of patterns (max 256) */ + UWORD numins; /* (word) Number of instruments (max 128) */ + UWORD flags; /* (word) Flags: bit 0: 0 = Amiga frequency table (see below) 1 = Linear frequency table */ + UWORD tempo; /* (word) Default tempo */ + UWORD bpm; /* (word) Default BPM */ + UBYTE orders[256]; /* (byte) Pattern order table */ +} XMHEADER; + + +typedef struct XMINSTHEADER{ + ULONG size; /* (dword) Instrument size */ + char name[22]; /* (char) Instrument name */ + UBYTE type; /* (byte) Instrument type (always 0) */ + UWORD numsmp; /* (word) Number of samples in instrument */ + ULONG ssize; /* */ +} XMINSTHEADER; + + +typedef struct XMPATCHHEADER{ + UBYTE what[96]; /* (byte) Sample number for all notes */ + UBYTE volenv[48]; /* (byte) Points for volume envelope */ + UBYTE panenv[48]; /* (byte) Points for panning envelope */ + UBYTE volpts; /* (byte) Number of volume points */ + UBYTE panpts; /* (byte) Number of panning points */ + UBYTE volsus; /* (byte) Volume sustain point */ + UBYTE volbeg; /* (byte) Volume loop start point */ + UBYTE volend; /* (byte) Volume loop end point */ + UBYTE pansus; /* (byte) Panning sustain point */ + UBYTE panbeg; /* (byte) Panning loop start point */ + UBYTE panend; /* (byte) Panning loop end point */ + UBYTE volflg; /* (byte) Volume type: bit 0: On; 1: Sustain; 2: Loop */ + UBYTE panflg; /* (byte) Panning type: bit 0: On; 1: Sustain; 2: Loop */ + UBYTE vibflg; /* (byte) Vibrato type */ + UBYTE vibsweep; /* (byte) Vibrato sweep */ + UBYTE vibdepth; /* (byte) Vibrato depth */ + UBYTE vibrate; /* (byte) Vibrato rate */ + UWORD volfade; /* (word) Volume fadeout */ + UWORD reserved[11]; /* (word) Reserved */ +} XMPATCHHEADER; + + +typedef struct XMWAVHEADER{ + ULONG length; /* (dword) Sample length */ + ULONG loopstart; /* (dword) Sample loop start */ + ULONG looplength; /* (dword) Sample loop length */ + UBYTE volume; /* (byte) Volume */ + SBYTE finetune; /* (byte) Finetune (signed byte -128..+127) */ + UBYTE type; /* (byte) Type: Bit 0-1: 0 = No loop, 1 = Forward loop, */ +/* 2 = Ping-pong loop; */ +/* 4: 16-bit sampledata */ + UBYTE panning; /* (byte) Panning (0-255) */ + SBYTE relnote; /* (byte) Relative note number (signed byte) */ + UBYTE reserved; /* (byte) Reserved */ + char samplename[22]; /* (char) Sample name */ +} XMWAVHEADER; + + +typedef struct XMPATHEADER{ + ULONG size; /* (dword) Pattern header length */ + UBYTE packing; /* (byte) Packing type (always 0) */ + UWORD numrows; /* (word) Number of rows in pattern (1..256) */ + UWORD packsize; /* (word) Packed patterndata size */ +} XMPATHEADER; + +typedef struct MTMNOTE{ + UBYTE a,b,c; +} MTMNOTE; + + +typedef struct XMNOTE{ + UBYTE note,ins,vol,eff,dat; +}XMNOTE; + +XMNOTE *xmpat; + +/************************************************************************** +**************************************************************************/ + + + +static XMHEADER *mh; + +char XM_Version[]="XM"; + + + +BOOL XM_Test(void) +{ + char id[17]; + if(!fread(id,17,1,modfp)) return 0; + if(!memcmp(id,"Extended Module: ",17)) return 1; + return 0; +} + + +BOOL XM_Init(void) +{ + mh=NULL; + if(!(mh=(XMHEADER *)MyCalloc(1,sizeof(XMHEADER)))) return 0; + return 1; +} + + +void XM_Cleanup(void) +{ + if(mh!=NULL) free(mh); +} + + +void XM_ReadNote(XMNOTE *n) +{ + UBYTE cmp; + memset(n,0,sizeof(XMNOTE)); + + cmp=fgetc(modfp); + + if(cmp&0x80){ + if(cmp&1) n->note=fgetc(modfp); + if(cmp&2) n->ins=fgetc(modfp); + if(cmp&4) n->vol=fgetc(modfp); + if(cmp&8) n->eff=fgetc(modfp); + if(cmp&16) n->dat=fgetc(modfp); + } + else{ + n->note=cmp; + n->ins=fgetc(modfp); + n->vol=fgetc(modfp); + n->eff=fgetc(modfp); + n->dat=fgetc(modfp); + } +} + + +UBYTE *XM_Convert(XMNOTE *xmtrack,UWORD rows) +{ + int t; + UBYTE note,ins,vol,eff,dat; + + UniReset(); + + for(t=0;tnote; + ins=xmtrack->ins; + vol=xmtrack->vol; + eff=xmtrack->eff; + dat=xmtrack->dat; + + if(note!=0) UniNote(note-1); + + if(ins!=0) UniInstrument(ins-1); + +/* printf("Vol:%d\n",vol); */ + + switch(vol>>4){ + + case 0x6: /* volslide down */ + if(vol&0xf){ + UniWrite(UNI_XMEFFECTA); + UniWrite(vol&0xf); + } + break; + + case 0x7: /* volslide up */ + if(vol&0xf){ + UniWrite(UNI_XMEFFECTA); + UniWrite(vol<<4); + } + break; + + /* volume-row fine volume slide is compatible with protracker + EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as + opposed to 'take the last sliding value'. + */ + + case 0x8: /* finevol down */ + UniPTEffect(0xe,0xb0 | (vol&0xf)); + break; + + case 0x9: /* finevol up */ + UniPTEffect(0xe,0xa0 | (vol&0xf)); + break; + + case 0xa: /* set vibrato speed */ + UniPTEffect(0x4,vol<<4); + break; + + case 0xb: /* vibrato */ + UniPTEffect(0x4,vol&0xf); + break; + + case 0xc: /* set panning */ + UniPTEffect(0x8,vol<<4); + break; + + case 0xd: /* panning slide left */ + /* only slide when data nibble not zero: */ + + if(vol&0xf){ + UniWrite(UNI_XMEFFECTP); + UniWrite(vol&0xf); + } + break; + + case 0xe: /* panning slide right */ + /* only slide when data nibble not zero: */ + + if(vol&0xf){ + UniWrite(UNI_XMEFFECTP); + UniWrite(vol<<4); + } + break; + + case 0xf: /* tone porta */ + UniPTEffect(0x3,vol<<4); + break; + + default: + if(vol>=0x10 && vol<=0x50){ + UniPTEffect(0xc,vol-0x10); + } + } + +/* if(eff>0xf) printf("Effect %d",eff); */ + + switch(eff){ + + case 'G'-55: /* G - set global volume */ + if(dat>64) dat=64; + UniWrite(UNI_XMEFFECTG); + UniWrite(dat); + break; + + case 'H'-55: /* H - global volume slide */ + UniWrite(UNI_XMEFFECTH); + UniWrite(dat); + break; + + case 'K'-55: /* K - keyoff */ + UniNote(96); + break; + + case 'L'-55: /* L - set envelope position */ + break; + + case 'P'-55: /* P - panning slide */ + UniWrite(UNI_XMEFFECTP); + UniWrite(dat); + break; + + case 'R'-55: /* R - multi retrig note */ + UniWrite(UNI_S3MEFFECTQ); + UniWrite(dat); + break; + + case 'T'-55: /* T - Tremor !! (== S3M effect I) */ + UniWrite(UNI_S3MEFFECTI); + UniWrite(dat); + break; + + case 'X'-55: + if((dat>>4)==1){ /* X1 extra fine porta up */ + + + } + else{ /* X2 extra fine porta down */ + + } + break; + + default: + if(eff==0xa){ + UniWrite(UNI_XMEFFECTA); + UniWrite(dat); + } + else if(eff<=0xf) UniPTEffect(eff,dat); + break; + } + + UniNewline(); + xmtrack++; + } + return UniDup(); +} + + + +BOOL XM_Load(void) +{ + INSTRUMENT *d; + SAMPLE *q; + int t,u,v,p,numtrk; + long next; + + /* try to read module header */ + + _mm_read_str(mh->id,17,modfp); + _mm_read_str(mh->songname,21,modfp); + _mm_read_str(mh->trackername,20,modfp); + mh->version =_mm_read_I_UWORD(modfp); + mh->headersize =_mm_read_I_ULONG(modfp); + mh->songlength =_mm_read_I_UWORD(modfp); + mh->restart =_mm_read_I_UWORD(modfp); + mh->numchn =_mm_read_I_UWORD(modfp); + mh->numpat =_mm_read_I_UWORD(modfp); + mh->numins =_mm_read_I_UWORD(modfp); + mh->flags =_mm_read_I_UWORD(modfp); + mh->tempo =_mm_read_I_UWORD(modfp); + mh->bpm =_mm_read_I_UWORD(modfp); + _mm_read_UBYTES(mh->orders,256,modfp); + + if(feof(modfp)){ + myerr = ERROR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + + of.initspeed=mh->tempo; + of.inittempo=mh->bpm; + of.modtype=DupStr(mh->trackername,20); + of.numchn=mh->numchn; + of.numpat=mh->numpat; + of.numtrk=(UWORD)of.numpat*of.numchn; /* get number of channels */ + of.songname=DupStr(mh->songname,20); /* make a cstr of songname */ + of.numpos=mh->songlength; /* copy the songlength */ + of.reppos=mh->restart; + of.numins=mh->numins; + of.flags|=UF_XMPERIODS; + if(mh->flags&1) of.flags|=UF_LINEAR; + + memcpy(of.positions,mh->orders,256); + +/* + WHY THIS CODE HERE?? I CAN'T REMEMBER! + + of.numpat=0; + for(t=0;tof.numpat) of.numpat=of.positions[t]; + } + of.numpat++; +*/ + +/* printf("Modtype :%s\n",of.modtype); + printf("Version :%x\n",mh->version); + printf("Song :%s\n",of.songname); + printf("Speed :%d,%d\n",of.initspeed,of.inittempo); + printf("Channels:%d\n",of.numchn); + printf("Numins :%d\n",mh->numins); +*/ + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + numtrk=0; + for(t=0;tnumpat;t++){ + XMPATHEADER ph; + +/* printf("Reading pattern %d\n",t); */ + + ph.size =_mm_read_I_ULONG(modfp); + ph.packing =_mm_read_UBYTE(modfp); + ph.numrows =_mm_read_I_UWORD(modfp); + ph.packsize =_mm_read_I_UWORD(modfp); + +/* printf("headln: %ld\n",ph.size); */ +/* printf("numrows: %d\n",ph.numrows); */ +/* printf("packsize:%d\n",ph.packsize); */ + + of.pattrows[t]=ph.numrows; + + /* + Gr8.. when packsize is 0, don't try to load a pattern.. it's empty. + This bug was discovered thanks to Khyron's module.. + */ + + if(!(xmpat=(XMNOTE *)MyCalloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) return 0; + + if(ph.packsize>0){ + for(u=0;uinsname=DupStr(ih.name,22); + d->numsmp=ih.numsmp; + + if(!AllocSamples(d)) return 0; + + if(ih.numsmp>0){ + XMPATCHHEADER pth; + XMWAVHEADER wh; + + _mm_read_UBYTES (pth.what, 96, modfp); + _mm_read_UBYTES (pth.volenv, 48, modfp); + _mm_read_UBYTES (pth.panenv, 48, modfp); + pth.volpts =_mm_read_UBYTE(modfp); + pth.panpts =_mm_read_UBYTE(modfp); + pth.volsus =_mm_read_UBYTE(modfp); + pth.volbeg =_mm_read_UBYTE(modfp); + pth.volend =_mm_read_UBYTE(modfp); + pth.pansus =_mm_read_UBYTE(modfp); + pth.panbeg =_mm_read_UBYTE(modfp); + pth.panend =_mm_read_UBYTE(modfp); + pth.volflg =_mm_read_UBYTE(modfp); + pth.panflg =_mm_read_UBYTE(modfp); + pth.vibflg =_mm_read_UBYTE(modfp); + pth.vibsweep =_mm_read_UBYTE(modfp); + pth.vibdepth =_mm_read_UBYTE(modfp); + pth.vibrate =_mm_read_UBYTE(modfp); + pth.volfade =_mm_read_I_UWORD(modfp); + _mm_read_I_UWORDS(pth.reserved, 11, modfp); + + memcpy(d->samplenumber,pth.what,96); + + d->volfade=pth.volfade; + +/* printf("Volfade %x\n",d->volfade); */ + + memcpy(d->volenv,pth.volenv,24); + d->volflg=pth.volflg; + d->volsus=pth.volsus; + d->volbeg=pth.volbeg; + d->volend=pth.volend; + d->volpts=pth.volpts; + +/* printf("volume points : %d\n" + "volflg : %d\n" + "volbeg : %d\n" + "volend : %d\n" + "volsus : %d\n", + d->volpts, + d->volflg, + d->volbeg, + d->volend, + d->volsus); +*/ + /* scale volume envelope: */ + + for(p=0;p<12;p++){ + d->volenv[p].val<<=2; +/* printf("%d,%d,",d->volenv[p].pos,d->volenv[p].val); */ + } + + memcpy(d->panenv,pth.panenv,24); + d->panflg=pth.panflg; + d->pansus=pth.pansus; + d->panbeg=pth.panbeg; + d->panend=pth.panend; + d->panpts=pth.panpts; + +/* printf("Panning points : %d\n" + "panflg : %d\n" + "panbeg : %d\n" + "panend : %d\n" + "pansus : %d\n", + d->panpts, + d->panflg, + d->panbeg, + d->panend, + d->pansus); +*/ + /* scale panning envelope: */ + + for(p=0;p<12;p++){ + d->panenv[p].val<<=2; +/* printf("%d,%d,",d->panenv[p].pos,d->panenv[p].val); */ + } + +/* for(u=0;u<256;u++){ */ +/* printf("%2.2x ",fgetc(modfp)); */ +/* } */ + + next=0; + + for(u=0;usamples[u]; + + wh.length =_mm_read_I_ULONG (modfp); + wh.loopstart =_mm_read_I_ULONG (modfp); + wh.looplength =_mm_read_I_ULONG (modfp); + wh.volume =_mm_read_UBYTE (modfp); + wh.finetune =_mm_read_SBYTE (modfp); + wh.type =_mm_read_UBYTE (modfp); + wh.panning =_mm_read_UBYTE (modfp); + wh.relnote =_mm_read_SBYTE (modfp); + wh.reserved =_mm_read_UBYTE (modfp); + _mm_read_str(wh.samplename, 22, modfp); + +/* printf("wav %d:%22.22s\n",u,wh.samplename); */ + + q->samplename =DupStr(wh.samplename,22); + q->length =wh.length; + q->loopstart =wh.loopstart; + q->loopend =wh.loopstart+wh.looplength; + q->volume =wh.volume; + q->c2spd =wh.finetune+128; + q->transpose =wh.relnote; + q->panning =wh.panning; + q->seekpos =next; + + if(wh.type&0x10){ + q->length>>=1; + q->loopstart>>=1; + q->loopend>>=1; + } + + next+=wh.length; + +/* printf("Type %u\n",wh.type); */ +/* printf("Trans %d\n",wh.relnote); */ + + q->flags|=SF_OWNPAN; + if(wh.type&0x3) q->flags|=SF_LOOP; + if(wh.type&0x2) q->flags|=SF_BIDI; + + if(wh.type&0x10) q->flags|=SF_16BITS; + q->flags|=SF_DELTA; + q->flags|=SF_SIGNED; + } + + for(u=0;usamples[u].seekpos+=_mm_ftell(modfp); + + _mm_fseek(modfp,next,SEEK_CUR); + } + + d++; + } + + + return 1; +} + + +LOADER load_xm={ + NULL, + "XM", + "Portable XM loader v0.4 - for your ears only / MikMak", + XM_Init, + XM_Test, + XM_Load, + XM_Cleanup +}; diff --git a/libs/oldmik/src/mdma.c b/libs/oldmik/src/mdma.c new file mode 100644 index 0000000..aac6cc9 --- /dev/null +++ b/libs/oldmik/src/mdma.c @@ -0,0 +1,515 @@ +/* + +Name: +MDMA.C + +Description: +DMA routines + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include +#include "mdma.h" + + +/* DMA Controler #1 (8-bit controller) */ +#define DMA1_STAT 0x08 /* read status register */ +#define DMA1_WCMD 0x08 /* write command register */ +#define DMA1_WREQ 0x09 /* write request register */ +#define DMA1_SNGL 0x0A /* write single bit register */ +#define DMA1_MODE 0x0B /* write mode register */ +#define DMA1_CLRFF 0x0C /* clear byte ptr flip/flop */ +#define DMA1_MCLR 0x0D /* master clear register */ +#define DMA1_CLRM 0x0E /* clear mask register */ +#define DMA1_WRTALL 0x0F /* write all mask register */ + +/* DMA Controler #2 (16-bit controller) */ +#define DMA2_STAT 0xD0 /* read status register */ +#define DMA2_WCMD 0xD0 /* write command register */ +#define DMA2_WREQ 0xD2 /* write request register */ +#define DMA2_SNGL 0xD4 /* write single bit register */ +#define DMA2_MODE 0xD6 /* write mode register */ +#define DMA2_CLRFF 0xD8 /* clear byte ptr flip/flop */ +#define DMA2_MCLR 0xDA /* master clear register */ +#define DMA2_CLRM 0xDC /* clear mask register */ +#define DMA2_WRTALL 0xDE /* write all mask register */ + +#define DMA0_ADDR 0x00 /* chan 0 base adddress */ +#define DMA0_CNT 0x01 /* chan 0 base count */ +#define DMA1_ADDR 0x02 /* chan 1 base adddress */ +#define DMA1_CNT 0x03 /* chan 1 base count */ +#define DMA2_ADDR 0x04 /* chan 2 base adddress */ +#define DMA2_CNT 0x05 /* chan 2 base count */ +#define DMA3_ADDR 0x06 /* chan 3 base adddress */ +#define DMA3_CNT 0x07 /* chan 3 base count */ +#define DMA4_ADDR 0xC0 /* chan 4 base adddress */ +#define DMA4_CNT 0xC2 /* chan 4 base count */ +#define DMA5_ADDR 0xC4 /* chan 5 base adddress */ +#define DMA5_CNT 0xC6 /* chan 5 base count */ +#define DMA6_ADDR 0xC8 /* chan 6 base adddress */ +#define DMA6_CNT 0xCA /* chan 6 base count */ +#define DMA7_ADDR 0xCC /* chan 7 base adddress */ +#define DMA7_CNT 0xCE /* chan 7 base count */ + +#define DMA0_PAGE 0x87 /* chan 0 page register (refresh)*/ +#define DMA1_PAGE 0x83 /* chan 1 page register */ +#define DMA2_PAGE 0x81 /* chan 2 page register */ +#define DMA3_PAGE 0x82 /* chan 3 page register */ +#define DMA4_PAGE 0x8F /* chan 4 page register (unuseable)*/ +#define DMA5_PAGE 0x8B /* chan 5 page register */ +#define DMA6_PAGE 0x89 /* chan 6 page register */ +#define DMA7_PAGE 0x8A /* chan 7 page register */ + +#define MAX_DMA 8 + +#define DMA_DECREMENT 0x20 /* mask to make DMA hardware go backwards */ + +typedef struct { + UBYTE dma_disable; /* bits to disable dma channel */ + UBYTE dma_enable; /* bits to enable dma channel */ + UWORD page; /* page port location */ + UWORD addr; /* addr port location */ + UWORD count; /* count port location */ + UWORD single; /* single mode port location */ + UWORD mode; /* mode port location */ + UWORD clear_ff; /* clear flip-flop port location */ + UBYTE write; /* bits for write transfer */ + UBYTE read; /* bits for read transfer */ +} DMA_ENTRY; + +/* Variables needed ... */ + +static DMA_ENTRY mydma[MAX_DMA] = { + +/* DMA channel 0 */ + {0x04,0x00,DMA0_PAGE,DMA0_ADDR,DMA0_CNT, + DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x48,0x44}, + +/* DMA channel 1 */ + {0x05,0x01,DMA1_PAGE,DMA1_ADDR,DMA1_CNT, + DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x49,0x45}, + +/* DMA channel 2 */ + {0x06,0x02,DMA2_PAGE,DMA2_ADDR,DMA2_CNT, + DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4A,0x46}, + +/* DMA channel 3 */ + {0x07,0x03,DMA3_PAGE,DMA3_ADDR,DMA3_CNT, + DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4B,0x47}, + +/* DMA channel 4 */ + {0x04,0x00,DMA4_PAGE,DMA4_ADDR,DMA4_CNT, + DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x48,0x44}, + +/* DMA channel 5 */ + {0x05,0x01,DMA5_PAGE,DMA5_ADDR,DMA5_CNT, + DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x49,0x45}, + +/* DMA channel 6 */ + {0x06,0x02,DMA6_PAGE,DMA6_ADDR,DMA6_CNT, + DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4A,0x46}, + +/* DMA channel 7 */ + {0x07,0x03,DMA7_PAGE,DMA7_ADDR,DMA7_CNT, + DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4B,0x47}, +}; + + +/* + +Each specialised DMA code part should provide the following things: + +In MDMA.H: + + - a DMAMEM typedef, which should contain all the data that the + routines need for maintaining/allocating/freeing dma memory. + + +In MDMA.C: + + - 2 macros ENTER_CRITICAL and LEAVE_CRITICAL + + - A function 'static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)' + which should perform the actual dma-memory allocation. It should + use DMAMEM *dm to store all it's information. + + - A function 'static void MDma_FreeMem0(DMAMEM *dm)' to free the memory + + - A function 'static ULONG MDma_GetLinearPtr(DMAMEM *dm)' which should + return the linear 20 bits pointer to the actual dmabuffer.. this + function is used by MDma_Start + + - A function 'void *MDma_GetPtr(DMAMEM *dm)' which should return a pointer + to the dmabuffer. If the dma memory can't be accessed directly it should + return a pointer to a FAKE dma buffer (DJGPP!!) + + - A function 'void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)'. This + function will be called each time a routine wrote something to the + dmabuffer (returned by MDma_GetPtr()). In the case of a FAKE dmabuffer + this routine should take care of copying the data from the fake buffer to + the real dma memory ('count' bytes from byteoffset 'index'). + +*/ + + + +#ifdef __WATCOMC__ + +/**************************************************************************** +********************* Watcom C specialised DMA code: ************************ +****************************************************************************/ + +#define ENTER_CRITICAL IRQ_PUSH_OFF() +extern void IRQ_PUSH_OFF (void); +#pragma aux IRQ_PUSH_OFF = \ + "pushfd", \ + "cli" \ + modify [esp]; + +#define LEAVE_CRITICAL IRQ_POP() +extern void IRQ_POP (void); +#pragma aux IRQ_POP = \ + "popfd" \ + modify [esp]; + + +static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size) +/* + Allocates a dma buffer of 'size' bytes. + returns FALSE if failed. +*/ +{ + static union REGS r; + ULONG p; + + /* allocate TWICE the size of the requested dma buffer.. + this fixes the 'page-crossing' bug of previous versions */ + + r.x.eax = 0x0100; /* DPMI allocate DOS memory */ + r.x.ebx = ((size*2) + 15) >> 4; /* Number of paragraphs requested */ + + int386 (0x31, &r, &r); + + if( r.x.cflag ) return 0; /* failed */ + + dm->raw_selector=r.x.edx; + + /* convert the segment into a linear address */ + + p=(r.x.eax&0xffff)<<4; + + /* if the first half of the allocated memory crosses a page + boundary, return the second half which is then guaranteed to + be page-continuous */ + + if( (p>>16) != ((p+size-1)>>16) ) p+=size; + + dm->continuous=(void *)p; + + return 1; +} + + +static void MDma_FreeMem0(DMAMEM *dm) +{ + static union REGS r; + r.x.eax = 0x0101; /* DPMI free DOS memory */ + r.x.edx = dm->raw_selector; /* base selector */ + int386 (0x31, &r, &r); +} + + +static ULONG MDma_GetLinearPtr(DMAMEM *dm) +{ + return (ULONG)dm->continuous; +} + + +void *MDma_GetPtr(DMAMEM *dm) +{ + return(dm->continuous); +} + + +void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count) +{ + /* This function doesnt do anything here (WATCOM C + can access dma memory directly) */ +} + + +#elif defined(__DJGPP__) +/**************************************************************************** +*********************** DJGPP specialised DMA code: ************************* +****************************************************************************/ +#define ENTER_CRITICAL __asm__( "pushf \n\t cli" ) +#define LEAVE_CRITICAL __asm__( "popf \n\t" ) +#include + +static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size) +/* + Allocates a dma buffer of 'size' bytes - one in the code segment and + one in the lower 1 Mb physical mem. + It doesn't check if the dma mem is page-continuous, and can only be + used to allocate exactly 1 block. +*/ +{ + dm->raw.size = (size + 15) >> 4; + if (_go32_dpmi_allocate_dos_memory(&(dm->raw))) + return 0; + dm->continuous = (void *) malloc(size); + return 1; +} + + + +static void MDma_FreeMem0(DMAMEM *dm) +{ + _go32_dpmi_free_dos_memory(&(dm->raw)); + free(dm->continuous); +} + +static ULONG MDma_GetLinearPtr(DMAMEM *dm) +{ + return (ULONG) dm->raw.rm_segment << 4; +} + + +void *MDma_GetPtr(DMAMEM *dm) +{ + return(dm->continuous); +} + +void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count) +{ + char *src = &(((UBYTE*)dm->continuous)[index]); + ULONG dest = 16 * dm->raw.rm_segment + (ULONG) index; + _farsetsel(_go32_conventional_mem_selector()); + while(count--) { + _farnspokeb(dest++, *(src++)); + } +} + +#else + +/**************************************************************************** +********************* Borland C specialised DMA code: *********************** +****************************************************************************/ + +#define ENTER_CRITICAL asm{ pushf; cli } +#define LEAVE_CRITICAL asm{ popf } + +#define LPTR(ptr) (((ULONG)FP_SEG(ptr)<<4)+FP_OFF(ptr)) +#define NPTR(ptr) MK_FP(FP_SEG(p)+(FP_OFF(p)>>4),FP_OFF(p)&15) + + +static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size) +/* + Allocates a dma buffer of 'size' bytes. + returns FALSE if failed. +*/ +{ + char huge *p; + ULONG s; + + /* allocate TWICE the size of the requested dma buffer.. + so we can always get a page-contiguous dma buffer */ + + if((dm->raw=malloc((ULONG)size*2))==NULL) return 0; + + p=(char huge *)dm->raw; + s=LPTR(p); + + /* if the first half of the allocated memory crosses a page + boundary, return the second half which is then guaranteed to + be page-continuous */ + + if( (s>>16) != ((s+size-1)>>16) ) p+=size; + + /* put the page-continuous pointer into DMAMEM */ + + dm->continuous=NPTR(p); + + return 1; +} + + +static void MDma_FreeMem0(DMAMEM *dm) +{ + free(dm->raw); +} + + +static ULONG MDma_GetLinearPtr(DMAMEM *dm) +{ + return LPTR(dm->continuous); +} + + +void *MDma_GetPtr(DMAMEM *dm) +{ + return(dm->continuous); +} + +#pragma argsused + +void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count) +{ + /* This function doesnt do anything here (BORLAND C + can access dma memory directly) */ +} + +#endif + + +/**************************************************************************** +************************* General DMA code: ********************************* +****************************************************************************/ + + +DMAMEM *MDma_AllocMem(UWORD size) +{ + DMAMEM *p; + + /* allocate dma memory structure */ + + if(!(p=(DMAMEM *)malloc(sizeof(DMAMEM)))) return NULL; + + /* allocate dma memory */ + + if(!MDma_AllocMem0(p,size)){ + + /* didn't succeed? -> free everything & return NULL */ + + free(p); + return NULL; + } + + return p; +} + + +void MDma_FreeMem(DMAMEM *p) +{ + MDma_FreeMem0(p); + free(p); +} + + +int MDma_Start(int channel,DMAMEM *dm,UWORD size,int type) +{ + DMA_ENTRY *tdma; + ULONG s_20bit,e_20bit; + UWORD spage,saddr,tcount; + UWORD epage,eaddr; + UBYTE cur_mode; + + tdma=&mydma[channel]; /* point to this dma data */ + + /* Convert the pc address to a 20 bit physical + address that the DMA controller needs */ + + s_20bit = MDma_GetLinearPtr(dm); + + e_20bit = s_20bit + size - 1; + spage = s_20bit>>16; + epage = e_20bit>>16; + + if(spage != epage) return 0; + + if(channel>=4){ + /* if 16-bit xfer, then addr,count & size are divided by 2 */ + s_20bit = s_20bit >> 1; + e_20bit = e_20bit >> 1; + size = size >> 1; + } + + saddr=s_20bit&0xffff; + + tcount = size-1; + + switch (type){ + + case READ_DMA: + cur_mode = tdma->read; + break; + + case WRITE_DMA: + cur_mode = tdma->write; + break; + + case INDEF_READ: + cur_mode = tdma->read | 0x10; /* turn on auto init */ + break; + + case INDEF_WRITE: + cur_mode = tdma->write | 0x10; /* turn on auto init */ + break; + } + + ENTER_CRITICAL; + outportb(tdma->single,tdma->dma_disable); /* disable channel */ + outportb(tdma->mode,cur_mode); /* set mode */ + outportb(tdma->clear_ff,0); /* clear f/f */ + outportb(tdma->addr,saddr&0xff); /* LSB */ + outportb(tdma->addr,saddr>>8); /* MSB */ + outportb(tdma->page,spage); /* page # */ + outportb(tdma->clear_ff,0); /* clear f/f */ + outportb(tdma->count,tcount&0x0ff); /* LSB count */ + outportb(tdma->count,tcount>>8); /* MSB count */ + outportb(tdma->single,tdma->dma_enable); /* enable */ + LEAVE_CRITICAL; + + return 1; +} + + +void MDma_Stop(int channel) +{ + DMA_ENTRY *tdma; + tdma=&mydma[channel]; /* point to this dma data */ + outportb(tdma->single,tdma->dma_disable); /* disable chan */ +} + + +UWORD MDma_Todo(int channel) +{ + UWORD creg; + UWORD val1,val2; + + DMA_ENTRY *tdma=&mydma[channel]; + + creg=tdma->count; + + ENTER_CRITICAL; + + outportb(tdma->clear_ff,0xff); + + redo: + val1=inportb(creg); + val1|=inportb(creg)<<8; + val2=inportb(creg); + val2|=inportb(creg)<<8; + + val1-=val2; + if((SWORD)val1>64) goto redo; + if((SWORD)val1<-64) goto redo; + + LEAVE_CRITICAL; + + if(channel>3) val2<<=1; + + return val2; +} diff --git a/libs/oldmik/src/mdma.h b/libs/oldmik/src/mdma.h new file mode 100644 index 0000000..893d359 --- /dev/null +++ b/libs/oldmik/src/mdma.h @@ -0,0 +1,42 @@ +#ifndef MDMA_H +#define MDMA_H + +#include "mtypes.h" + +#define READ_DMA 0 +#define WRITE_DMA 1 +#define INDEF_READ 2 +#define INDEF_WRITE 3 + +#ifdef __WATCOMC__ + +typedef struct{ + void *continuous; /* the pointer to a page-continous dma buffer */ + UWORD raw_selector; /* the raw allocated dma selector */ +} DMAMEM; + +#elif defined(__DJGPP__) + +typedef struct{ + void *continuous; /* the pointer to a page-continous dma buffer */ + _go32_dpmi_seginfo raw; /* points to the memory that was allocated */ +} DMAMEM; + +#else + +typedef struct{ + void *continuous; /* the pointer to a page-continous dma buffer */ + void *raw; /* points to the memory that was allocated */ +} DMAMEM; + +#endif + +DMAMEM *MDma_AllocMem(UWORD size); +void MDma_FreeMem(DMAMEM *dm); +int MDma_Start(int channel,DMAMEM *dm,UWORD size,int type); +void MDma_Stop(int channel); +void *MDma_GetPtr(DMAMEM *dm); +void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count); +UWORD MDma_Todo(int channel); + +#endif diff --git a/libs/oldmik/src/mdriver.c b/libs/oldmik/src/mdriver.c new file mode 100644 index 0000000..d7c5de9 --- /dev/null +++ b/libs/oldmik/src/mdriver.c @@ -0,0 +1,264 @@ +/* + +Name: +MDRIVER.C + +Description: +These routines are used to access the available soundcard drivers. + +Portability: +All systems - all compilers + +*/ +#include +#include +#include "mikmod.h" + +DRIVER *firstdriver=NULL,*md_driver; + +UWORD md_device =0; +UWORD md_mixfreq =44100; +UWORD md_mode =0; +UWORD md_dmabufsize =8192; +UBYTE md_numchn =0; +UBYTE md_bpm =125; + +static void dummyplay(void) +{ +} + +void (*md_player)(void)=dummyplay; + +static FILE *sl_fp; +static SWORD sl_old; +static UWORD sl_infmt; +static UWORD sl_outfmt; +static SWORD sl_buffer[1024]; + +static BOOL isplaying=0; + + +void SL_Init(FILE *fp,UWORD infmt,UWORD outfmt) +{ + sl_old=0; + sl_fp=fp; + sl_infmt=infmt; + sl_outfmt=outfmt; +} + + +void SL_Exit(void) +{ +} + + +void SL_Load(void *buffer,ULONG length) +{ + SBYTE *bptr=(SBYTE *)buffer; + SWORD *wptr=(SWORD *)buffer; + UWORD stodo; + int t; + + /* compute number of samples to load */ + if(sl_outfmt & SF_16BITS) length>>=1; + + while(length){ + + stodo=(length<1024) ? length : 1024; + + if(sl_infmt&SF_16BITS){ + fread(sl_buffer,sizeof(SWORD),stodo,sl_fp); +#ifdef MM_BIG_ENDIAN + if(!(sl_infmt&SF_BIG_ENDIAN)) + swab((char *)sl_buffer,(char *)sl_buffer,stodo<<1); +#else + /* assume machine is little endian by default */ + if(sl_infmt&SF_BIG_ENDIAN) + swab((char *)sl_buffer,(char *)sl_buffer,stodo<<1); +#endif + } + else{ + SBYTE *s; + SWORD *d; + + fread(sl_buffer,sizeof(SBYTE),stodo,sl_fp); + + s=(SBYTE *)sl_buffer; + d=sl_buffer; + s+=stodo; + d+=stodo; + + for(t=0;t>8; + } + + length-=stodo; + } +} + + +void MD_InfoDriver(void) +{ + int t; + DRIVER *l; + + /* list all registered devicedrivers: */ + + for(t=1,l=firstdriver; l!=NULL; l=l->next, t++){ + printf("%d. %s\n",t,l->Version); + } +} + + +void MD_RegisterDriver(DRIVER *drv) +{ + if(firstdriver==NULL){ + firstdriver=drv; + drv->next=NULL; + } + else{ + drv->next=firstdriver; + firstdriver=drv; + } +} + + +SWORD MD_SampleLoad(FILE *fp,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ + SWORD result=md_driver->SampleLoad(fp,size,reppos,repend,flags); + SL_Exit(); + return result; +} + + +void MD_SampleUnLoad(SWORD handle) +{ + md_driver->SampleUnLoad(handle); +} + + +BOOL MD_Init(void) +{ + UWORD t; + + /* if md_device==0, try to find a device number */ + + if(md_device==0){ + + for(t=1,md_driver=firstdriver; md_driver!=NULL; md_driver=md_driver->next, t++){ + if(md_driver->IsPresent()) break; + } + + if(md_driver==NULL){ + myerr="You don't have any of the supported sound-devices"; + return 0; + } + + md_device=t; + } + + /* if n>0 use that driver */ + + for(t=1,md_driver=firstdriver; md_driver!=NULL && t!=md_device; md_driver=md_driver->next, t++); + + if(md_driver==NULL){ + myerr="Device number out of range"; + return 0; + } + + return(md_driver->Init()); +} + + +void MD_Exit(void) +{ + md_driver->Exit(); +} + + +void MD_PlayStart(void) +{ + /* safety valve, prevents entering + playstart twice: */ + + if(isplaying) return; + md_driver->PlayStart(); + isplaying=1; +} + + +void MD_PlayStop(void) +{ + /* safety valve, prevents calling playStop when playstart + hasn't been called: */ + + if(isplaying){ + isplaying=0; + md_driver->PlayStop(); + } +} + + +void MD_SetBPM(UBYTE bpm) +{ + md_bpm=bpm; +} + + +void MD_RegisterPlayer(void (*player)(void)) +{ + md_player=player; +} + + +void MD_Update(void) +{ + if(isplaying) md_driver->Update(); +} + + +void MD_VoiceSetVolume(UBYTE voice,UBYTE vol) +{ + md_driver->VoiceSetVolume(voice,vol); +} + + +void MD_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ + md_driver->VoiceSetFrequency(voice,frq); +} + + +void MD_VoiceSetPanning(UBYTE voice,ULONG pan) +{ + md_driver->VoiceSetPanning(voice,pan); +} + + +void MD_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ + md_driver->VoicePlay(voice,handle,start,size,reppos,repend,flags); +} diff --git a/libs/oldmik/src/mikmod.h b/libs/oldmik/src/mikmod.h new file mode 100644 index 0000000..0111ccb --- /dev/null +++ b/libs/oldmik/src/mikmod.h @@ -0,0 +1,580 @@ +#ifndef MIKMOD_H +#define MIKMOD_H + +#include +#include "mtypes.h" /* include atomic mikmod types */ + + +#define mikbanner \ +"=======================================================================\n" \ +"MIKMOD v2.10 - Portable version - Programmed by MikMak of HaRDCoDE '95\n" \ +"=======================================================================\n" \ +"This program is SHAREWARE - Read MIKMOD.TXT for more info\n" \ +"E-Mail : mikmak@stack.urc.tue.nl\n" + + +/* + error variables: + =============== +*/ + +extern char *ERROR_ALLOC_STRUCT; +extern char *ERROR_LOADING_PATTERN; +extern char *ERROR_LOADING_TRACK; +extern char *ERROR_LOADING_HEADER; +extern char *ERROR_NOT_A_MODULE; +extern char *ERROR_LOADING_SAMPLEINFO; +extern char *ERROR_OUT_OF_HANDLES; +extern char *ERROR_SAMPLE_TOO_BIG; +extern char *myerr; + + + +#define _mm_rewind(x) _mm_fseek(x,0,SEEK_SET) +int _mm_fseek(FILE *stream,long offset,int whence); +void _mm_setiobase(long iobase); +long _mm_ftell(FILE *stream); + + +extern SBYTE _mm_read_SBYTE (FILE *fp); +extern UBYTE _mm_read_UBYTE (FILE *fp); + +extern SWORD _mm_read_M_SWORD (FILE *fp); +extern SWORD _mm_read_I_SWORD (FILE *fp); + +extern UWORD _mm_read_M_UWORD(FILE *fp); +extern UWORD _mm_read_I_UWORD(FILE *fp); + +extern SLONG _mm_read_M_SLONG (FILE *fp); +extern SLONG _mm_read_I_SLONG (FILE *fp); + +extern ULONG _mm_read_M_ULONG(FILE *fp); +extern ULONG _mm_read_I_ULONG(FILE *fp); + +extern int _mm_read_str(char *str, int size, FILE *fp); + +extern int _mm_read_SBYTES (SBYTE *buffer, int number, FILE *fp); +extern int _mm_read_UBYTES (UBYTE *buffer, int number, FILE *fp); + +extern int _mm_read_M_SWORDS (SWORD *buffer, int number, FILE *fp); +extern int _mm_read_I_SWORDS (SWORD *buffer, int number, FILE *fp); + +extern int _mm_read_M_UWORDS (UWORD *buffer, int number, FILE *fp); +extern int _mm_read_I_UWORDS (UWORD *buffer, int number, FILE *fp); + +extern int _mm_read_M_SLONGS (SLONG *buffer, int number, FILE *fp); +extern int _mm_read_I_SLONGS (SLONG *buffer, int number, FILE *fp); + +extern int _mm_read_M_ULONGS (ULONG *buffer, int number, FILE *fp); +extern int _mm_read_I_ULONGS (ULONG *buffer, int number, FILE *fp); + + +extern void _mm_write_SBYTE (SBYTE data,FILE *fp); +extern void _mm_write_UBYTE (UBYTE data,FILE *fp); + +extern void _mm_write_M_SWORD (SWORD data,FILE *fp); +extern void _mm_write_I_SWORD (SWORD data,FILE *fp); + +extern void _mm_write_M_UWORD (UWORD data,FILE *fp); +extern void _mm_write_I_UWORD (UWORD data,FILE *fp); + +extern void _mm_write_M_SLONG (SLONG data,FILE *fp); +extern void _mm_write_I_SLONG (SLONG data,FILE *fp); + +extern void _mm_write_M_ULONG (ULONG data,FILE *fp); +extern void _mm_write_I_ULONG (ULONG data,FILE *fp); + +extern void _mm_write_SBYTES (SBYTE *data, int number,FILE *fp); +extern void _mm_write_UBYTES (UBYTE *data, int number,FILE *fp); + +extern void _mm_write_M_SWORDS (SWORD *data, int number,FILE *fp); +extern void _mm_write_I_SWORDS (SWORD *data, int number,FILE *fp); + +extern void _mm_write_M_UWORDS (UWORD *data, int number,FILE *fp); +extern void _mm_write_I_UWORDS (UWORD *data, int number,FILE *fp); + +extern void _mm_write_M_SLONGS (SLONG *data, int number,FILE *fp); +extern void _mm_write_I_SLONGS (SLONG *data, int number,FILE *fp); + +extern void _mm_write_M_ULONGS (ULONG *data, int number,FILE *fp); +extern void _mm_write_I_ULONGS (ULONG *data, int number,FILE *fp); + + +/************************************************************************** +****** Unitrack stuff: **************************************************** +**************************************************************************/ + +/* + prototypes: +*/ + +void UniSetRow(UBYTE *t); +UBYTE UniGetByte(void); +UBYTE *UniFindRow(UBYTE *t,UWORD row); +void UniReset(void); +void UniWrite(UBYTE data); +void UniNewline(void); +void UniInstrument(UBYTE ins); +void UniNote(UBYTE note); +void UniPTEffect(UBYTE eff,UBYTE dat); +UBYTE *UniDup(void); +void UniSkipOpcode(UBYTE op); +BOOL UniInit(void); +void UniCleanup(void); +UWORD TrkLen(UBYTE *t); +BOOL MyCmp(UBYTE *a,UBYTE *b,UWORD l); + +/* + all known effects: +*/ + +enum { + UNI_NOTE=1, + UNI_INSTRUMENT, + UNI_PTEFFECT0, + UNI_PTEFFECT1, + UNI_PTEFFECT2, + UNI_PTEFFECT3, + UNI_PTEFFECT4, + UNI_PTEFFECT5, + UNI_PTEFFECT6, + UNI_PTEFFECT7, + UNI_PTEFFECT8, + UNI_PTEFFECT9, + UNI_PTEFFECTA, + UNI_PTEFFECTB, + UNI_PTEFFECTC, + UNI_PTEFFECTD, + UNI_PTEFFECTE, + UNI_PTEFFECTF, + UNI_S3MEFFECTA, + UNI_S3MEFFECTD, + UNI_S3MEFFECTE, + UNI_S3MEFFECTF, + UNI_S3MEFFECTI, + UNI_S3MEFFECTQ, + UNI_S3MEFFECTT, + UNI_XMEFFECTA, + UNI_XMEFFECTG, + UNI_XMEFFECTH, + UNI_XMEFFECTP +}; + + +/************************************************************************** +****** mikmod types: ****************************************************** +**************************************************************************/ + + +/* + Sample format flags: +*/ + +#define SF_16BITS 1 +#define SF_SIGNED 2 +#define SF_DELTA 4 +#define SF_BIG_ENDIAN 8 +#define SF_LOOP 16 +#define SF_BIDI 32 +#define SF_OWNPAN 64 +#define SF_REVERSE 128 + + +/* + Envelope flags: +*/ + +#define EF_ON 1 +#define EF_SUSTAIN 2 +#define EF_LOOP 4 + + +/* + Unimod flags +*/ + +#define UF_XMPERIODS 1 /* if set use XM periods/finetuning */ +#define UF_LINEAR 2 /* if set use LINEAR periods */ + + +typedef struct ENVPT{ + SWORD pos; + SWORD val; +} ENVPT; + + +typedef struct SAMPLE{ + UWORD c2spd; /* finetune frequency */ + SBYTE transpose; /* transpose value */ + UBYTE volume; /* volume 0-64 */ + UBYTE panning; /* panning */ + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + UWORD flags; /* sample format */ + ULONG seekpos; /* seek position in file */ + char *samplename; /* name of the sample */ + SWORD handle; /* sample handle */ +} SAMPLE; + + +typedef struct INSTRUMENT{ + UBYTE numsmp; + UBYTE samplenumber[96]; + + UBYTE volflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE volpts; + UBYTE volsus; + UBYTE volbeg; + UBYTE volend; + ENVPT volenv[12]; + + UBYTE panflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE panpts; + UBYTE pansus; + UBYTE panbeg; + UBYTE panend; + ENVPT panenv[12]; + + UBYTE vibtype; + UBYTE vibsweep; + UBYTE vibdepth; + UBYTE vibrate; + + UWORD volfade; + char *insname; + SAMPLE *samples; +} INSTRUMENT; + + +/* + MikMod UNImod types: + ==================== +*/ + +typedef struct UNIMOD{ + UBYTE numchn; /* number of channels */ + UWORD numpos; /* number of positions in this song */ + UWORD reppos; /* restart position */ + UWORD numpat; /* number of patterns in this song */ + UWORD numtrk; /* number of tracks */ + UWORD numins; /* number of samples */ + UBYTE initspeed; /* */ + UBYTE inittempo; /* */ + UBYTE positions[256]; /* all positions */ + UBYTE panning[32]; /* 32 panning positions */ + UBYTE flags; /* */ + char *songname; /* name of the song */ + char *modtype; /* string type of module */ + char *comment; /* module comments */ + INSTRUMENT *instruments; /* all samples */ + UWORD *patterns; /* array of PATTERN */ + UWORD *pattrows; /* array of number of rows for each pattern */ + UBYTE **tracks; /* array of pointers to tracks */ +} UNIMOD; + + +/************************************************************************** +****** Loader stuff: ****************************************************** +**************************************************************************/ + +/* + loader structure: +*/ + +typedef struct LOADER{ + struct LOADER *next; + char *type; + char *version; + BOOL (*Init)(void); + BOOL (*Test)(void); + BOOL (*Load)(void); + void (*Cleanup)(void); +} LOADER; + + +/* + public loader variables: +*/ + +extern UNIMOD of; +extern FILE *modfp; +extern UWORD finetune[16]; + + +/* + main loader prototypes: +*/ + +void ML_InfoLoader(void); +void ML_RegisterLoader(LOADER *ldr); +UNIMOD *ML_LoadFP(FILE *fp); +UNIMOD *ML_LoadFN(char *filename); +void ML_Free(UNIMOD *mf); + + +/* + other loader prototypes: (used by the loader modules) +*/ + +BOOL InitTracks(void); +void AddTrack(UBYTE *tr); +BOOL ReadComment(UWORD len); +BOOL AllocPatterns(void); +BOOL AllocTracks(void); +BOOL AllocInstruments(void); +BOOL AllocSamples(INSTRUMENT *i); +char *DupStr(char *s,UWORD len); +void *MyMalloc(size_t size); +void *MyCalloc(size_t nitems,size_t size); + + +/* + Declare external loaders: +*/ +extern LOADER load_uni; +extern LOADER load_mod; +extern LOADER load_m15; +extern LOADER load_mtm; +extern LOADER load_s3m; +extern LOADER load_stm; +extern LOADER load_ult; +extern LOADER load_xm; + + +/************************************************************************** +****** Wavload stuff: ***************************************************** +**************************************************************************/ + +SAMPLE *MW_LoadWavFP(FILE *fp); +SAMPLE *MW_LoadWavFN(char *filename); +void MW_FreeWav(SAMPLE *si); + + +/************************************************************************** +****** Driver stuff: ****************************************************** +**************************************************************************/ + +/* + max. number of handles a driver has to provide. (not strict) +*/ + +#define MAXSAMPLEHANDLES 128 + + +/* + possible mixing mode bits: +*/ + +#define DMODE_STEREO 1 +#define DMODE_16BITS 2 +#define DMODE_INTERP 4 + + +/* + driver structure: +*/ + +typedef struct DRIVER{ + struct DRIVER *next; + char *Name; + char *Version; + BOOL (*IsPresent) (void); + SWORD (*SampleLoad) (FILE *fp,ULONG size,ULONG reppos,ULONG repend,UWORD flags); + void (*SampleUnLoad) (SWORD handle); + BOOL (*Init) (void); + void (*Exit) (void); + void (*PlayStart) (void); + void (*PlayStop) (void); + void (*Update) (void); + void (*VoiceSetVolume) (UBYTE voice,UBYTE vol); + void (*VoiceSetFrequency) (UBYTE voice,ULONG frq); + void (*VoiceSetPanning) (UBYTE voice,UBYTE pan); + void (*VoicePlay) (UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags); +} DRIVER; + + +/* + public driver variables: +*/ + +extern DRIVER *md_driver; +extern UWORD md_device; +extern UWORD md_mixfreq; +extern UWORD md_dmabufsize; +extern UWORD md_mode; +extern UBYTE md_numchn; +extern UBYTE md_bpm; +extern void (*md_player)(void); + +/* + main driver prototypes: +*/ + +void MD_InfoDriver(void); +void MD_RegisterDriver(DRIVER *drv); +void MD_RegisterPlayer(void (*plr)(void)); +SWORD MD_SampleLoad(FILE *fp,ULONG size,ULONG reppos,ULONG repend,UWORD flags); +void MD_SampleUnLoad(SWORD handle); +BOOL MD_Init(void); +void MD_Exit(void); +void MD_PlayStart(void); +void MD_PlayStop(void); +void MD_SetBPM(UBYTE bpm); +void MD_Update(void); +void MD_VoiceSetVolume(UBYTE voice,UBYTE ivol); +void MD_VoiceSetFrequency(UBYTE voice,ULONG frq); +void MD_VoiceSetPanning(UBYTE voice,ULONG pan); +void MD_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags); +void SL_Init(FILE *fp,UWORD infmt,UWORD outfmt); +void SL_Load(void *buffer,ULONG length); + +/* + Declare external drivers: +*/ + +extern DRIVER drv_gus; /* gravis ultrasound driver */ +extern DRIVER drv_sb; /* soundblaster DSP driver */ +extern DRIVER drv_ss; /* ensoniq soundscape driver */ +extern DRIVER drv_nos; /* nosound driver */ +extern DRIVER drv_raw; /* file output driver */ +extern DRIVER drv_w95; /* win95 driver */ +extern DRIVER drv_awe; /* experimental SB-AWE driver */ +extern DRIVER drv_vox; /* linux voxware driver */ +extern DRIVER drv_af; /* Dec Alpha AudioFile driver */ +extern DRIVER drv_sun; /* Sun driver */ +extern DRIVER drv_os2; /* Os2 driver */ +extern DRIVER drv_tim; /* timing driver */ + +/************************************************************************** +****** Player stuff: ****************************************************** +**************************************************************************/ + + +typedef struct ENVPR{ + UBYTE flg; /* envelope flag */ + UBYTE pts; /* number of envelope points */ + UBYTE sus; /* envelope sustain index */ + UBYTE beg; /* envelope loop begin */ + UBYTE end; /* envelope loop end */ + SWORD p; /* current envelope counter */ + UWORD a; /* envelope index a */ + UWORD b; /* envelope index b */ + ENVPT *env; /* envelope points */ +} ENVPR; + + +typedef struct AUDTMP{ + INSTRUMENT *i; + SAMPLE *s; + + UWORD fadevol; /* fading volume */ + + ENVPR venv; + ENVPR penv; + + UBYTE keyon; /* if true=key is pressed. */ + UBYTE kick; /* if true=sample has to be restarted */ + UBYTE sample; /* which sample number (0-31) */ + SWORD handle; /* which sample-handle */ + + ULONG start; /* The start byte index in the sample */ + + UBYTE panning; /* panning position */ + UBYTE pansspd; /* panslide speed */ + + SBYTE volume; /* amiga volume (0 t/m 64) to play the sample at */ + UWORD period; /* period to play the sample at */ + + /* You should not have to use the values + below in the player routine */ + + SBYTE transpose; + + UBYTE note; /* */ + + SWORD ownper; + SWORD ownvol; + + UBYTE *row; /* row currently playing on this channel */ + + SBYTE retrig; /* retrig value (0 means don't retrig) */ + UWORD c2spd; /* what finetune to use */ + + SBYTE tmpvolume; /* tmp volume */ + + UWORD tmpperiod; /* tmp period */ + UWORD wantedperiod; /* period to slide to (with effect 3 or 5) */ + + UWORD slidespeed; /* */ + UWORD portspeed; /* noteslide speed (toneportamento) */ + + UBYTE s3mtremor; /* s3m tremor (effect I) counter */ + UBYTE s3mtronof; /* s3m tremor ontime/offtime */ + + UBYTE s3mvolslide; /* last used volslide */ + + UBYTE s3mrtgspeed; /* last used retrig speed */ + UBYTE s3mrtgslide; /* last used retrig slide */ + + UBYTE glissando; /* glissando (0 means off) */ + UBYTE wavecontrol; /* */ + + SBYTE vibpos; /* current vibrato position */ + UBYTE vibspd; /* "" speed */ + UBYTE vibdepth; /* "" depth */ + + SBYTE trmpos; /* current tremolo position */ + UBYTE trmspd; /* "" speed */ + UBYTE trmdepth; /* "" depth */ + + UWORD soffset; /* last used sample-offset (effect 9) */ +} AUDTMP; + + +extern AUDTMP mp_audio[32]; /* max eight channels */ +extern UBYTE mp_bpm; /* beats-per-minute speed */ +extern UWORD mp_patpos; /* current row number (0-63) */ +extern SWORD mp_sngpos; /* current song position */ +extern UWORD mp_sngspd; /* current songspeed */ + +extern BOOL mp_loop; +extern BOOL mp_panning; +extern BOOL mp_extspd; +extern UBYTE mp_volume; + +/* + player prototypes: +*/ + +int MP_Ready(void); +void MP_NextPosition(void); +void MP_PrevPosition(void); +void MP_SetPosition(UWORD pos); +void MP_HandleTick(void); +void MP_Init(UNIMOD *m); + + +/************************************************************************** +****** Virtual channel stuff: ********************************************* +**************************************************************************/ + +BOOL VC_Init(void); +void VC_Exit(void); + +void VC_PlayStart(void); +void VC_PlayStop(void); + +SWORD VC_SampleLoad(FILE *fp,ULONG size,ULONG reppos,ULONG repend,UWORD flags); +void VC_SampleUnload(SWORD handle); + +void VC_WriteSamples(SBYTE *buf,UWORD todo); +UWORD VC_WriteBytes(SBYTE *buf,UWORD todo); +void VC_SilenceBytes(SBYTE *buf,UWORD todo); + +void VC_VoiceSetVolume(UBYTE voice,UBYTE vol); +void VC_VoiceSetFrequency(UBYTE voice,ULONG frq); +void VC_VoiceSetPanning(UBYTE voice,UBYTE pan); +void VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags); + +#endif diff --git a/libs/oldmik/src/mirq.c b/libs/oldmik/src/mirq.c new file mode 100644 index 0000000..96cbcf7 --- /dev/null +++ b/libs/oldmik/src/mirq.c @@ -0,0 +1,99 @@ +/* + +Name: +MDMA.C + +Description: +Some general purpose IRQ routines + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include "mirq.h" + +#define OCR1 0x20 /* 8259-1 Operation control register */ +#define IMR1 0x21 /* 8259-1 Mask register */ + +#define OCR2 0xA0 /* 8259-2 Operation control register */ +#define IMR2 0xA1 /* 8259-2 Mask register */ + + +BOOL MIrq_IsEnabled(UBYTE irqno) +/* + Returns true if the specified hardware irq is enabled. +*/ +{ + UBYTE imr=(irqno>7) ? IMR2 : IMR1; /* interrupt mask register */ + UBYTE msk=1<<(irqno&7); /* interrupt mask */ + return((inportb(imr) & msk) == 0); +} + + +BOOL MIrq_OnOff(UBYTE irqno,UBYTE onoff) +/* + Use to enable or disable the specified irq. +*/ +{ + UBYTE imr=(irqno>7) ? IMR2 : IMR1; /* interrupt mask register */ + UBYTE ocr=(irqno>7) ? OCR2 : OCR1; /* ocr */ + UBYTE msk=1<<(irqno&7); /* interrupt mask */ + UBYTE eoi=0x60|(irqno&7); /* specific end-of-interrupt */ + BOOL oldstate; + + /* save current setting of this irq */ + oldstate=((inportb(imr) & msk) == 0); + + if(onoff){ + outportb(imr,inportb(imr) & ~msk); + outportb(ocr,eoi); + if(irqno>7) MIrq_OnOff(2,1); + } + else{ + outportb(imr,inportb(imr) | msk); + } + + return oldstate; +} + + +void MIrq_EOI(UBYTE irqno) +/* + Clears the specified interrupt request at the interrupt controller. +*/ +{ + outportb(0x20,0x20); + if(irqno>7) outportb(0xa0,0x20); +} + + +PVI MIrq_SetHandler(UBYTE irqno,PVI handler) +{ +#ifdef __DJGPP__ + _go32_dpmi_seginfo seginfo; +#endif + PVI oldvect; + int vecno=(irqno>7) ? irqno+0x68 : irqno+0x8; +#ifdef __DJGPP__ + _go32_dpmi_get_protected_mode_interrupt_vector(vecno, &seginfo); + oldvect = seginfo.pm_offset; + seginfo.pm_offset = handler; + seginfo.pm_selector = _go32_my_cs(); + _go32_dpmi_allocate_iret_wrapper(&seginfo); + _go32_dpmi_set_protected_mode_interrupt_vector(vecno, &seginfo); +#else + oldvect=_dos_getvect(vecno); + _dos_setvect(vecno,handler); +#endif + return oldvect; +} diff --git a/libs/oldmik/src/mirq.h b/libs/oldmik/src/mirq.h new file mode 100644 index 0000000..7fcf97f --- /dev/null +++ b/libs/oldmik/src/mirq.h @@ -0,0 +1,35 @@ +#ifndef MIRQ_H +#define MIRQ_H + +#include "mtypes.h" + + +#ifdef __WATCOMC__ + #define MIRQARGS void + typedef void (interrupt far *PVI)(MIRQARGS); +#endif + +#ifdef __DJGPP__ + #define MIRQARGS void + typedef void (*PVI)(MIRQARGS); +#endif + +#ifdef __BORLANDC__ + + #ifdef __cplusplus + #define MIRQARGS ... + #else + #define MIRQARGS + #endif + + typedef void interrupt (far *PVI)(MIRQARGS); + +#endif + + +BOOL MIrq_IsEnabled(UBYTE irqno); +BOOL MIrq_OnOff(UBYTE irqno,UBYTE onoff); +PVI MIrq_SetHandler(UBYTE irqno,PVI handler); +void MIrq_EOI(UBYTE irqno); + +#endif diff --git a/libs/oldmik/src/mloader.c b/libs/oldmik/src/mloader.c new file mode 100644 index 0000000..6e135fc --- /dev/null +++ b/libs/oldmik/src/mloader.c @@ -0,0 +1,406 @@ +/* + +Name: +MLOADER.C + +Description: +These routines are used to access the available module loaders + +Portability: +All systems - all compilers + +*/ +#include +#include +#include +#include "mikmod.h" + + +FILE *modfp; +UNIMOD of; + +LOADER *firstloader=NULL; + + +UWORD finetune[16]={ + 8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757, + 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280 +}; + + + + + +void ML_InfoLoader(void) +{ + int t; + LOADER *l; + + /* list all registered devicedrivers: */ + + for(t=1,l=firstloader; l!=NULL; l=l->next, t++){ + printf("%d. %s\n",t,l->version); + } +} + + +void ML_RegisterLoader(LOADER *ldr) +{ + LOADER *l; + + if(firstloader==NULL){ + firstloader=ldr; + ldr->next=NULL; + } + else{ + ldr->next=firstloader; + firstloader=ldr; + } +} + + + +void *MyMalloc(size_t size) +/* + Same as malloc, but sets error variable ml_errno when it failed +*/ +{ + void *d; + + d=malloc(size); + if(d==NULL){ + myerr="Error allocating structure"; + } + return d; +} + + + +void *MyCalloc(size_t nitems,size_t size) +/* + Same as calloc, but sets error variable ml_errno when it failed +*/ +{ + void *d; + + d=calloc(nitems,size); + if(d==NULL){ + myerr="Error allocating structure"; + } + return d; +} + + + +BOOL ReadComment(UWORD len) +{ + int t; + + if(len){ + if(!(of.comment=(char *)MyMalloc(len+1))) return 0; + fread(of.comment,len,1,modfp); + of.comment[len]=0; + + /* strip any control-characters in the comment: */ + + for(t=0;tnumsmp){ + if(!(i->samples=(SAMPLE *)MyCalloc(n,sizeof(SAMPLE)))) return 0; + + for(u=0; usamples[u].panning=128; + i->samples[u].handle=-1; + } + } + return 1; +} + + +char *DupStr(char *s,UWORD len) +/* + Creates a CSTR out of a character buffer of 'len' bytes, but strips + any terminating non-printing characters like 0, spaces etc. +*/ +{ + UWORD t; + char *d=NULL; + + /* Scan for first printing char in buffer [includes high ascii up to 254] */ + + while(len){ + if(!(s[len-1]>=0 && s[len-1]<=0x20)) break; + len--; + } + + if(len){ + + /* When the buffer wasn't completely empty, allocate + a cstring and copy the buffer into that string, except + for any control-chars */ + + if((d=(char *)malloc(len+1))!=NULL){ + for(t=0;t=0 && s[t]<32) ? ' ': s[t]; + } + d[t]=0; + } + } + + return d; +} + + + +BOOL ML_LoadSamples(void) +{ + UWORD t,u; + INSTRUMENT *i; + SAMPLE *s; + + for(t=0;tnumsmp; u++){ + + s=&i->samples[u]; + +/* printf("Loading Sample %d\n",t); */ + + /* sample has to be loaded ? -> increase + number of samples and allocate memory and + load sample */ + + if(s->length){ + + if(s->seekpos){ + _mm_fseek(modfp,s->seekpos,SEEK_SET); + } + + /* Call the sample load routine of the driver module. + It has to return a 'handle' (>=0) that identifies + the sample */ + + s->handle=MD_SampleLoad(modfp, + s->length, + s->loopstart, + s->loopend, + s->flags); + + if(s->handle<0) return 0; + } + } + } + return 1; +} + + +BOOL ML_LoadHeader(void) +{ + BOOL ok=0; + LOADER *l; + + /* Try to find a loader that recognizes the module */ + + for(l=firstloader; l!=NULL; l=l->next){ + _mm_rewind(modfp); + if(l->Test()) break; + } + + if(l==NULL){ + myerr="Unknown module format."; + return 0; + } + + /* init unitrk routines */ + + if(!UniInit()) return 0; + + /* init module loader */ + + if(l->Init()){ + _mm_rewind(modfp); + ok=l->Load(); + } + + l->Cleanup(); + + /* free unitrk allocations */ + + UniCleanup(); + return ok; +} + + + +void ML_XFreeInstrument(INSTRUMENT *i) +{ + UWORD t; + + if(i->samples!=NULL){ + for(t=0; tnumsmp; t++){ + if(i->samples[t].handle>=0){ + MD_SampleUnLoad(i->samples[t].handle); + } + } + free(i->samples); + } + if(i->insname!=NULL) free(i->insname); +} + + +void ML_FreeEx(UNIMOD *mf) +{ + UWORD t; + + if(mf->modtype!=NULL) free(mf->modtype); + if(mf->patterns!=NULL) free(mf->patterns); + if(mf->pattrows!=NULL) free(mf->pattrows); + + if(mf->tracks!=NULL){ + for(t=0;tnumtrk;t++){ + if(mf->tracks[t]!=NULL) free(mf->tracks[t]); + } + free(mf->tracks); + } + + if(mf->instruments!=NULL){ + for(t=0;tnumins;t++){ + ML_XFreeInstrument(&mf->instruments[t]); + } + free(mf->instruments); + } + + if(mf->songname!=NULL) free(mf->songname); + if(mf->comment!=NULL) free(mf->comment); +} + + + +/****************************************** + + Next are the user-callable functions + +******************************************/ + + +void ML_Free(UNIMOD *mf) +{ + if(mf!=NULL){ + ML_FreeEx(mf); + free(mf); + } +} + + + + +UNIMOD *ML_LoadFP(FILE *fp) +{ + int t; + UNIMOD *mf; + + /* init fileptr, clear errorcode, clear static modfile: */ + + modfp=fp; + myerr=NULL; + memset(&of,0,sizeof(UNIMOD)); + + /* init panning array */ + + for(t=0;t<32;t++){ + of.panning[t]=((t+1)&2)?255:0; + } + + if(!ML_LoadHeader()){ + ML_FreeEx(&of); + return NULL; + } + + if(!ML_LoadSamples()){ + ML_FreeEx(&of); + return NULL; + } + + if(!(mf=(UNIMOD *)MyCalloc(1,sizeof(UNIMOD)))){ + ML_FreeEx(&of); + return NULL; + } + + /* Copy the static UNIMOD contents + into the dynamic UNIMOD struct */ + + memcpy(mf,&of,sizeof(UNIMOD)); + + return mf; +} + + + +UNIMOD *ML_LoadFN(char *filename) +{ + FILE *fp; + UNIMOD *mf; + + if((fp=fopen(filename,"rb"))==NULL){ + myerr="Error opening file"; + return NULL; + } + + mf=ML_LoadFP(fp); + fclose(fp); + + return mf; +} + diff --git a/libs/oldmik/src/mmio.c b/libs/oldmik/src/mmio.c new file mode 100644 index 0000000..1cedb8f --- /dev/null +++ b/libs/oldmik/src/mmio.c @@ -0,0 +1,216 @@ +/* + +Name: +MMIO.C + +Description: +Miscellaneous I/O routines.. used to solve some portability issues +(like big/little endian machines and word alignment in structures ) +Also includes mikmod's ingenious error handling variable + some much +used error strings. + +Portability: +All systems - all compilers + +*/ +#include +#include "mikmod.h" + +char *ERROR_ALLOC_STRUCT="Error allocating structure"; +char *ERROR_LOADING_PATTERN="Error loading pattern"; +char *ERROR_LOADING_TRACK="Error loading track"; +char *ERROR_LOADING_HEADER="Error loading header"; +char *ERROR_NOT_A_MODULE="Unknown module format"; +char *ERROR_LOADING_SAMPLEINFO="Error loading sampleinfo"; +char *ERROR_OUT_OF_HANDLES="Out of sample-handles"; +char *ERROR_SAMPLE_TOO_BIG="Sample too big, out of memory"; + +char *myerr; + +static long _mm_iobase=0; + +int _mm_fseek(FILE *stream, long offset, int whence) +{ + return fseek(stream, + (whence==SEEK_SET) ? offset+_mm_iobase : offset, + whence); +} + +long _mm_ftell(FILE *stream) +{ + return ftell(stream)-_mm_iobase; +} + +void _mm_setiobase(long iobase) +{ + _mm_iobase=iobase; +} + +void _mm_write_SBYTE(SBYTE data,FILE *fp) +{ + fputc(data,fp); +} + +void _mm_write_UBYTE(UBYTE data,FILE *fp) +{ + fputc(data,fp); +} + +void _mm_write_M_UWORD(UWORD data,FILE *fp) +{ + _mm_write_UBYTE(data>>8,fp); + _mm_write_UBYTE(data&0xff,fp); +} + +void _mm_write_I_UWORD(UWORD data,FILE *fp) +{ + _mm_write_UBYTE(data&0xff,fp); + _mm_write_UBYTE(data>>8,fp); +} + +void _mm_write_M_SWORD(SWORD data,FILE *fp) +{ + _mm_write_M_UWORD((UWORD)data,fp); +} + +void _mm_write_I_SWORD(SWORD data,FILE *fp) +{ + _mm_write_I_UWORD((UWORD)data,fp); +} + +void _mm_write_M_ULONG(ULONG data,FILE *fp) +{ + _mm_write_M_UWORD(data>>16,fp); + _mm_write_M_UWORD(data&0xffff,fp); +} + +void _mm_write_I_ULONG(ULONG data,FILE *fp) +{ + _mm_write_I_UWORD(data&0xffff,fp); + _mm_write_I_UWORD(data>>16,fp); +} + +void _mm_write_M_SLONG(SLONG data,FILE *fp) +{ + _mm_write_M_ULONG((ULONG)data,fp); +} + +void _mm_write_I_SLONG(SLONG data,FILE *fp) +{ + _mm_write_I_ULONG((ULONG)data,fp); +} + + +#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name, type) \ +void \ +_mm_write_##type_name##S (type *buffer, int number, FILE *fp) \ +{ \ + while(number>0){ \ + _mm_write_##type_name(*(buffer++),fp); \ + number--; \ + } \ +} + +DEFINE_MULTIPLE_WRITE_FUNCTION ( SBYTE, SBYTE) +DEFINE_MULTIPLE_WRITE_FUNCTION (UBYTE, UBYTE) + +DEFINE_MULTIPLE_WRITE_FUNCTION (M_SWORD, SWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION (M_UWORD, UWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION (I_SWORD, SWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION (I_UWORD, UWORD) + +DEFINE_MULTIPLE_WRITE_FUNCTION (M_SLONG, SLONG) +DEFINE_MULTIPLE_WRITE_FUNCTION (M_ULONG, ULONG) +DEFINE_MULTIPLE_WRITE_FUNCTION (I_SLONG, SLONG) +DEFINE_MULTIPLE_WRITE_FUNCTION (I_ULONG, ULONG) + + +SBYTE _mm_read_SBYTE(FILE *fp) +{ + return(fgetc(fp)); +} + +UBYTE _mm_read_UBYTE(FILE *fp) +{ + return(fgetc(fp)); +} + +UWORD _mm_read_M_UWORD(FILE *fp) +{ + UWORD result=((UWORD)_mm_read_UBYTE(fp))<<8; + result|=_mm_read_UBYTE(fp); + return result; +} + +UWORD _mm_read_I_UWORD(FILE *fp) +{ + UWORD result=_mm_read_UBYTE(fp); + result|=((UWORD)_mm_read_UBYTE(fp))<<8; + return result; +} + +SWORD _mm_read_M_SWORD(FILE *fp) +{ + return((SWORD)_mm_read_M_UWORD(fp)); +} + +SWORD _mm_read_I_SWORD(FILE *fp) +{ + return((SWORD)_mm_read_I_UWORD(fp)); +} + +ULONG _mm_read_M_ULONG(FILE *fp) +{ + ULONG result=((ULONG)_mm_read_M_UWORD(fp))<<16; + result|=_mm_read_M_UWORD(fp); + return result; +} + +ULONG _mm_read_I_ULONG(FILE *fp) +{ + ULONG result=_mm_read_I_UWORD(fp); + result|=((ULONG)_mm_read_I_UWORD(fp))<<16; + return result; +} + +SLONG _mm_read_M_SLONG(FILE *fp) +{ + return((SLONG)_mm_read_M_ULONG(fp)); +} + +SLONG _mm_read_I_SLONG(FILE *fp) +{ + return((SLONG)_mm_read_I_ULONG(fp)); +} + + +int _mm_read_str(char *buffer,int number,FILE *fp) +{ + fread(buffer,1,number,fp); + return !feof(fp); +} + + +#define DEFINE_MULTIPLE_READ_FUNCTION(type_name, type) \ +int \ +_mm_read_##type_name##S (type *buffer, int number, FILE *fp) \ +{ \ + while(number>0){ \ + *(buffer++)=_mm_read_##type_name(fp); \ + number--; \ + } \ + return !feof(fp); \ +} + +DEFINE_MULTIPLE_READ_FUNCTION ( SBYTE, SBYTE) +DEFINE_MULTIPLE_READ_FUNCTION (UBYTE, UBYTE) + +DEFINE_MULTIPLE_READ_FUNCTION (M_SWORD, SWORD) +DEFINE_MULTIPLE_READ_FUNCTION (M_UWORD, UWORD) +DEFINE_MULTIPLE_READ_FUNCTION (I_SWORD, SWORD) +DEFINE_MULTIPLE_READ_FUNCTION (I_UWORD, UWORD) + +DEFINE_MULTIPLE_READ_FUNCTION (M_SLONG, SLONG) +DEFINE_MULTIPLE_READ_FUNCTION (M_ULONG, ULONG) +DEFINE_MULTIPLE_READ_FUNCTION (I_SLONG, SLONG) +DEFINE_MULTIPLE_READ_FUNCTION (I_ULONG, ULONG) diff --git a/libs/oldmik/src/mplayer.c b/libs/oldmik/src/mplayer.c new file mode 100644 index 0000000..cbf642b --- /dev/null +++ b/libs/oldmik/src/mplayer.c @@ -0,0 +1,1386 @@ +/* + +Name: +MPLAYER.C + +Description: +The actual modplaying routines + +Portability: +All systems - all compilers + +*/ +#include +#include "mikmod.h" + +UNIMOD *pf; /* <- this modfile is being played */ +UWORD reppos; /* patternloop position */ +UWORD repcnt; /* times to loop */ +UWORD vbtick; /* tick counter */ +UWORD patbrk; /* position where to start a new pattern */ +UBYTE patdly; /* patterndelay counter */ +UBYTE patdly2; /* patterndelay counter */ +UWORD numrow; /* number of rows on current pattern */ +SWORD posjmp; /* flag to indicate a position jump is needed... + changed since 1.00: now also indicates the + direction the position has to jump to: + + 0: Don't do anything + 1: Jump back 1 position + 2: Restart on current position + 3: Jump forward 1 position + */ +int forbid; /* forbidflag */ +static int isfirst; + +/* + Set forbid to 1 when you want to modify any of the mp_sngpos, mp_patpos etc. + variables and clear it when you're done. This prevents getting strange + results due to intermediate interrupts. +*/ + + +AUDTMP mp_audio[32]; /* max 32 channels */ +UBYTE mp_bpm; /* beats-per-minute speed */ +UWORD mp_patpos; /* current row number (0-255) */ +SWORD mp_sngpos; /* current song position */ +UWORD mp_sngspd; /* current songspeed */ +UWORD mp_channel; /* channel it's working on */ +BOOL mp_extspd=1; /* extended speed flag, default enabled */ +BOOL mp_panning=1; /* panning flag, default enabled */ +BOOL mp_loop=0; /* loop module ? */ +UBYTE mp_volume=100; /* song volume (0-100) (or user volume) */ + +static SBYTE globalvolume=64; /* global volume */ +static UBYTE globalslide; + +AUDTMP *a; /* current AUDTMP it's working on */ + + +UWORD mytab[12]={ + 1712*16,1616*16,1524*16,1440*16,1356*16,1280*16, + 1208*16,1140*16,1076*16,1016*16,960*16,907*16 +}; + +static UBYTE VibratoTable[32]={ + 0,24,49,74,97,120,141,161, + 180,197,212,224,235,244,250,253, + 255,253,250,244,235,224,212,197, + 180,161,141,120,97,74,49,24 +}; + + +/* linear periods to frequency translation table: */ + +UWORD lintab[768]={ +16726,16741,16756,16771,16786,16801,16816,16832,16847,16862,16877,16892,16908,16923,16938,16953, +16969,16984,16999,17015,17030,17046,17061,17076,17092,17107,17123,17138,17154,17169,17185,17200, +17216,17231,17247,17262,17278,17293,17309,17325,17340,17356,17372,17387,17403,17419,17435,17450, +17466,17482,17498,17513,17529,17545,17561,17577,17593,17608,17624,17640,17656,17672,17688,17704, +17720,17736,17752,17768,17784,17800,17816,17832,17848,17865,17881,17897,17913,17929,17945,17962, +17978,17994,18010,18027,18043,18059,18075,18092,18108,18124,18141,18157,18174,18190,18206,18223, +18239,18256,18272,18289,18305,18322,18338,18355,18372,18388,18405,18421,18438,18455,18471,18488, +18505,18521,18538,18555,18572,18588,18605,18622,18639,18656,18672,18689,18706,18723,18740,18757, +18774,18791,18808,18825,18842,18859,18876,18893,18910,18927,18944,18961,18978,18995,19013,19030, +19047,19064,19081,19099,19116,19133,19150,19168,19185,19202,19220,19237,19254,19272,19289,19306, +19324,19341,19359,19376,19394,19411,19429,19446,19464,19482,19499,19517,19534,19552,19570,19587, +19605,19623,19640,19658,19676,19694,19711,19729,19747,19765,19783,19801,19819,19836,19854,19872, +19890,19908,19926,19944,19962,19980,19998,20016,20034,20052,20071,20089,20107,20125,20143,20161, +20179,20198,20216,20234,20252,20271,20289,20307,20326,20344,20362,20381,20399,20418,20436,20455, +20473,20492,20510,20529,20547,20566,20584,20603,20621,20640,20659,20677,20696,20715,20733,20752, +20771,20790,20808,20827,20846,20865,20884,20902,20921,20940,20959,20978,20997,21016,21035,21054, +21073,21092,21111,21130,21149,21168,21187,21206,21226,21245,21264,21283,21302,21322,21341,21360, +21379,21399,21418,21437,21457,21476,21496,21515,21534,21554,21573,21593,21612,21632,21651,21671, +21690,21710,21730,21749,21769,21789,21808,21828,21848,21867,21887,21907,21927,21946,21966,21986, +22006,22026,22046,22066,22086,22105,22125,22145,22165,22185,22205,22226,22246,22266,22286,22306, +22326,22346,22366,22387,22407,22427,22447,22468,22488,22508,22528,22549,22569,22590,22610,22630, +22651,22671,22692,22712,22733,22753,22774,22794,22815,22836,22856,22877,22897,22918,22939,22960, +22980,23001,23022,23043,23063,23084,23105,23126,23147,23168,23189,23210,23230,23251,23272,23293, +23315,23336,23357,23378,23399,23420,23441,23462,23483,23505,23526,23547,23568,23590,23611,23632, +23654,23675,23696,23718,23739,23761,23782,23804,23825,23847,23868,23890,23911,23933,23954,23976, +23998,24019,24041,24063,24084,24106,24128,24150,24172,24193,24215,24237,24259,24281,24303,24325, +24347,24369,24391,24413,24435,24457,24479,24501,24523,24545,24567,24590,24612,24634,24656,24679, +24701,24723,24746,24768,24790,24813,24835,24857,24880,24902,24925,24947,24970,24992,25015,25038, +25060,25083,25105,25128,25151,25174,25196,25219,25242,25265,25287,25310,25333,25356,25379,25402, +25425,25448,25471,25494,25517,25540,25563,25586,25609,25632,25655,25678,25702,25725,25748,25771, +25795,25818,25841,25864,25888,25911,25935,25958,25981,26005,26028,26052,26075,26099,26123,26146, +26170,26193,26217,26241,26264,26288,26312,26336,26359,26383,26407,26431,26455,26479,26502,26526, +26550,26574,26598,26622,26646,26670,26695,26719,26743,26767,26791,26815,26839,26864,26888,26912, +26937,26961,26985,27010,27034,27058,27083,27107,27132,27156,27181,27205,27230,27254,27279,27304, +27328,27353,27378,27402,27427,27452,27477,27502,27526,27551,27576,27601,27626,27651,27676,27701, +27726,27751,27776,27801,27826,27851,27876,27902,27927,27952,27977,28003,28028,28053,28078,28104, +28129,28155,28180,28205,28231,28256,28282,28307,28333,28359,28384,28410,28435,28461,28487,28513, +28538,28564,28590,28616,28642,28667,28693,28719,28745,28771,28797,28823,28849,28875,28901,28927, +28953,28980,29006,29032,29058,29084,29111,29137,29163,29190,29216,29242,29269,29295,29322,29348, +29375,29401,29428,29454,29481,29507,29534,29561,29587,29614,29641,29668,29694,29721,29748,29775, +29802,29829,29856,29883,29910,29937,29964,29991,30018,30045,30072,30099,30126,30154,30181,30208, +30235,30263,30290,30317,30345,30372,30400,30427,30454,30482,30509,30537,30565,30592,30620,30647, +30675,30703,30731,30758,30786,30814,30842,30870,30897,30925,30953,30981,31009,31037,31065,31093, +31121,31149,31178,31206,31234,31262,31290,31319,31347,31375,31403,31432,31460,31489,31517,31546, +31574,31602,31631,31660,31688,31717,31745,31774,31803,31832,31860,31889,31918,31947,31975,32004, +32033,32062,32091,32120,32149,32178,32207,32236,32265,32295,32324,32353,32382,32411,32441,32470, +32499,32529,32558,32587,32617,32646,32676,32705,32735,32764,32794,32823,32853,32883,32912,32942, +32972,33002,33031,33061,33091,33121,33151,33181,33211,33241,33271,33301,33331,33361,33391,33421 +}; + + + + +#define LOGFAC 2*16 + +UWORD logtab[]={ + LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862, + LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814, + LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768, + LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725, + LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684, + LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646, + LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610, + LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575, + LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543, + LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513, + LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484, + LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457, + LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431 +}; + + +SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2) +{ + SWORD dp,dv,di; + + if(p1==p2) return v1; + + dv=v2-v1; + dp=p2-p1; + di=p-p1; + + return v1 + ((SLONG)(di*dv) / dp); +} + + +UWORD getlinearperiod(UBYTE note,UWORD fine) +{ + return((10L*12*16*4)-((UWORD)note*16*4)-(fine/2)+64); +} + + +UWORD getlogperiod(UBYTE note,UWORD fine) +{ + UBYTE n,o; + UWORD p1,p2,i; + + n=note%12; + o=note/12; + i=(n<<3)+(fine>>4); /* n*8 + fine/16 */ + + p1=logtab[i]; + p2=logtab[i+1]; + + return(Interpolate(fine/16,0,15,p1,p2)>>o); +} + + +UWORD getoldperiod(UBYTE note,UWORD c2spd) +{ + UBYTE n,o; + ULONG period; + + if(!c2spd) return 4242; /* <- prevent divide overflow.. (42 eheh) */ + + n=note%12; + o=note/12; + period=((8363L*mytab[n]) >> o )/c2spd; + return period; +} + + + +UWORD GetPeriod(UBYTE note,UWORD c2spd) +{ + if(pf->flags&UF_XMPERIODS){ + return (pf->flags&UF_LINEAR) ? getlinearperiod(note,c2spd) : getlogperiod(note,c2spd); + } + return(getoldperiod(note,c2spd)); +} + + + +void DoEEffects(UBYTE dat) +{ + UBYTE nib; + + nib=dat&0xf; + + switch(dat>>4){ + + case 0x0: /* filter toggle, not supported */ + break; + + case 0x1: /* fineslide up */ + if(!vbtick) a->tmpperiod-=(nib<<2); + break; + + case 0x2: /* fineslide dn */ + if(!vbtick) a->tmpperiod+=(nib<<2); + break; + + case 0x3: /* glissando ctrl */ + a->glissando=nib; + break; + + case 0x4: /* set vibrato waveform */ + a->wavecontrol&=0xf0; + a->wavecontrol|=nib; + break; + + case 0x5: /* set finetune */ +/* a->c2spd=finetune[nib]; */ +/* a->tmpperiod=GetPeriod(a->note,pf->samples[a->sample].transpose,a->c2spd); */ + break; + + case 0x6: /* set patternloop */ + + if(vbtick) break; + + /* hmm.. this one is a real kludge. But now it + works. */ + + if(nib){ /* set reppos or repcnt ? */ + + /* set repcnt, so check if repcnt already is set, + which means we are already looping */ + + if(repcnt>0) + repcnt--; /* already looping, decrease counter */ + else + repcnt=nib; /* not yet looping, so set repcnt */ + + if(repcnt) /* jump to reppos if repcnt>0 */ + mp_patpos=reppos; + } + else{ + reppos=mp_patpos-1; /* set reppos */ + } + break; + + + case 0x7: /* set tremolo waveform */ + a->wavecontrol&=0x0f; + a->wavecontrol|=nib<<4; + break; + + case 0x8: /* set panning */ + if(mp_panning){ + nib<<=4; + a->panning=nib; + pf->panning[mp_channel]=nib; + } + break; + + case 0x9: /* retrig note */ + + /* only retrigger if + data nibble > 0 */ + + if(nib>0){ + if(a->retrig==0){ + + /* when retrig counter reaches 0, + reset counter and restart the sample */ + + a->kick=1; + a->retrig=nib; + } + a->retrig--; /* countdown */ + } + break; + + case 0xa: /* fine volume slide up */ + if(vbtick) break; + + a->tmpvolume+=nib; + if(a->tmpvolume>64) a->tmpvolume=64; + break; + + case 0xb: /* fine volume slide dn */ + if(vbtick) break; + + a->tmpvolume-=nib; + if(a->tmpvolume<0) a->tmpvolume=0; + break; + + case 0xc: /* cut note */ + + /* When vbtick reaches the cut-note value, + turn the volume to zero ( Just like + on the amiga) */ + + if(vbtick>=nib){ + a->tmpvolume=0; /* just turn the volume down */ + } + break; + + case 0xd: /* note delay */ + + /* delay the start of the + sample until vbtick==nib */ + + if(vbtick==nib){ + a->kick=1; + } + else a->kick=0; + break; + + case 0xe: /* pattern delay */ + if(vbtick) break; + if(!patdly2) patdly=nib+1; /* only once (when vbtick=0) */ + break; + + case 0xf: /* invert loop, not supported */ + break; + } +} + + +void DoVibrato(void) +{ + UBYTE q; + UWORD temp; + + q=(a->vibpos>>2)&0x1f; + + switch(a->wavecontrol&3){ + + case 0: /* sine */ + temp=VibratoTable[q]; + break; + + case 1: /* ramp down */ + q<<=3; + if(a->vibpos<0) q=255-q; + temp=q; + break; + + case 2: /* square wave */ + temp=255; + break; + } + + temp*=a->vibdepth; + temp>>=7; + temp<<=2; + + if(a->vibpos>=0) + a->period=a->tmpperiod+temp; + else + a->period=a->tmpperiod-temp; + + if(vbtick) a->vibpos+=a->vibspd; /* do not update when vbtick==0 */ +} + + + +void DoTremolo(void) +{ + UBYTE q; + UWORD temp; + + q=(a->trmpos>>2)&0x1f; + + switch((a->wavecontrol>>4)&3){ + + case 0: /* sine */ + temp=VibratoTable[q]; + break; + + case 1: /* ramp down */ + q<<=3; + if(a->trmpos<0) q=255-q; + temp=q; + break; + + case 2: /* square wave */ + temp=255; + break; + } + + temp*=a->trmdepth; + temp>>=6; + + if(a->trmpos>=0){ + a->volume=a->tmpvolume+temp; + if(a->volume>64) a->volume=64; + } + else{ + a->volume=a->tmpvolume-temp; + if(a->volume<0) a->volume=0; + } + + if(vbtick) a->trmpos+=a->trmspd; /* do not update when vbtick==0 */ +} + + +void DoVolSlide(UBYTE dat) +{ + if(!vbtick) return; /* do not update when vbtick==0 */ + + a->tmpvolume+=dat>>4; /* volume slide */ + a->tmpvolume-=dat&0xf; + if(a->tmpvolume<0) a->tmpvolume=0; + if(a->tmpvolume>64) a->tmpvolume=64; +} + + + +void DoS3MVolSlide(UBYTE inf) +{ + UBYTE lo,hi; + + if(inf){ + a->s3mvolslide=inf; + } + inf=a->s3mvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if(hi==0){ + a->tmpvolume-=lo; + } + else if(lo==0){ + a->tmpvolume+=hi; + } + else if(hi==0xf){ + if(!vbtick) a->tmpvolume-=lo; + } + else if(lo==0xf){ + if(!vbtick) a->tmpvolume+=hi; + } + + if(a->tmpvolume<0) a->tmpvolume=0; + if(a->tmpvolume>64) a->tmpvolume=64; +} + + + +void DoXMVolSlide(UBYTE inf) +{ + UBYTE lo,hi; + + if(inf){ + a->s3mvolslide=inf; + } + inf=a->s3mvolslide; + + if(!vbtick) return; + + lo=inf&0xf; + hi=inf>>4; + + if(hi==0) + a->tmpvolume-=lo; + else + a->tmpvolume+=hi; + + if(a->tmpvolume<0) a->tmpvolume=0; + else if(a->tmpvolume>64) a->tmpvolume=64; +} + + + +void DoXMGlobalSlide(UBYTE inf) +{ + UBYTE lo,hi; + + if(inf){ + globalslide=inf; + } + inf=globalslide; + + if(!vbtick) return; + + lo=inf&0xf; + hi=inf>>4; + + if(hi==0) + globalvolume-=lo; + else + globalvolume+=hi; + + if(globalvolume<0) globalvolume=0; + else if(globalvolume>64) globalvolume=64; +} + + + +void DoXMPanSlide(UBYTE inf) +{ + UBYTE lo,hi; + SWORD pan; + + + if(inf!=0) a->pansspd=inf; + else inf=a->pansspd; + + if(!vbtick) return; + + lo=inf&0xf; + hi=inf>>4; + + /* slide right has absolute priority: */ + + if(hi) lo=0; + + pan=a->panning; + + pan-=lo; + pan+=hi; + + if(pan<0) pan=0; + if(pan>255) pan=255; + + a->panning=pan; +} + + + +void DoS3MSlideDn(UBYTE inf) +{ + UBYTE hi,lo; + + if(inf!=0) a->slidespeed=inf; + else inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if(hi==0xf){ + if(!vbtick) a->tmpperiod+=(UWORD)lo<<2; + } + else if(hi==0xe){ + if(!vbtick) a->tmpperiod+=lo; + } + else{ + if(vbtick) a->tmpperiod+=(UWORD)inf<<2; + } +} + + + +void DoS3MSlideUp(UBYTE inf) +{ + UBYTE hi,lo; + + if(inf!=0) a->slidespeed=inf; + else inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if(hi==0xf){ + if(!vbtick) a->tmpperiod-=(UWORD)lo<<2; + } + else if(hi==0xe){ + if(!vbtick) a->tmpperiod-=lo; + } + else{ + if(vbtick) a->tmpperiod-=(UWORD)inf<<2; + } +} + + + +void DoS3MTremor(UBYTE inf) +{ + UBYTE on,off; + + if(inf!=0) a->s3mtronof=inf; + else inf=a->s3mtronof; + + if(!vbtick) return; + + on=(inf>>4)+1; + off=(inf&0xf)+1; + + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremor < on ) ? a->tmpvolume:0; + a->s3mtremor++; +} + + + +void DoS3MRetrig(UBYTE inf) +{ + UBYTE hi,lo; + + hi=inf>>4; + lo=inf&0xf; + + if(lo){ + a->s3mrtgslide=hi; + a->s3mrtgspeed=lo; + } + + if(hi){ + a->s3mrtgslide=hi; + } + + /* only retrigger if + lo nibble > 0 */ + + if(a->s3mrtgspeed>0){ + if(a->retrig==0){ + + /* when retrig counter reaches 0, + reset counter and restart the sample */ + + a->kick=1; + a->retrig=a->s3mrtgspeed; + + if(vbtick){ /* don't slide on first retrig */ + switch(a->s3mrtgslide){ + + case 1: + case 2: + case 3: + case 4: + case 5: + a->tmpvolume-=(1<<(a->s3mrtgslide-1)); + break; + + case 6: + a->tmpvolume=(2*a->tmpvolume)/3; + break; + + case 7: + a->tmpvolume=a->tmpvolume>>1; + break; + + case 9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + a->tmpvolume+=(1<<(a->s3mrtgslide-9)); + break; + + case 0xe: + a->tmpvolume=(3*a->tmpvolume)/2; + break; + + case 0xf: + a->tmpvolume=a->tmpvolume<<1; + break; + } + if(a->tmpvolume<0) a->tmpvolume=0; + if(a->tmpvolume>64) a->tmpvolume=64; + } + } + a->retrig--; /* countdown */ + } +} + + +void DoS3MSpeed(UBYTE speed) +{ + if(vbtick || patdly2) return; + + if(speed){ /* <- v0.44 bugfix */ + mp_sngspd=speed; + vbtick=0; + } +} + + +void DoS3MTempo(UBYTE tempo) +{ + if(vbtick || patdly2) return; + mp_bpm=tempo; +} + + +void DoToneSlide(void) +{ + int dist,t; + + if(!vbtick){ + a->tmpperiod=a->period; + return; + } + + /* We have to slide a->period towards a->wantedperiod, so + compute the difference between those two values */ + + dist=a->period-a->wantedperiod; + + if( dist==0 || /* if they are equal */ + a->portspeed>abs(dist) ){ /* or if portamentospeed is too big */ + + a->period=a->wantedperiod; /* make tmpperiod equal tperiod */ + } + else if(dist>0){ /* dist>0 ? */ + a->period-=a->portspeed; /* then slide up */ + } + else + a->period+=a->portspeed; /* dist<0 -> slide down */ + +/* if(a->glissando){ + + If glissando is on, find the nearest + halfnote to a->tmpperiod + + for(t=0;t<60;t++){ + if(a->tmpperiod>=npertab[a->finetune][t]) break; + } + + a->period=npertab[a->finetune][t]; + } + else +*/ + a->tmpperiod=a->period; +} + + +void DoPTEffect0(UBYTE dat) +{ + UBYTE note; + + note=a->note; + + if(dat!=0){ + switch(vbtick%3){ + case 1: + note+=(dat>>4); break; + case 2: + note+=(dat&0xf); break; + } + a->period=GetPeriod(note+a->transpose,a->c2spd); + a->ownper=1; + } +} + + +void PlayNote(void) +{ + INSTRUMENT *i; + SAMPLE *s; + UWORD period; + UBYTE inst,c; + UBYTE note; + + if(a->row==NULL) return; + + UniSetRow(a->row); + + while(c=UniGetByte()){ + + switch(c){ + + case UNI_NOTE: + note=UniGetByte(); + + if(note==96){ /* key off ? */ + a->keyon=0; + if(a->i && !(a->i->volflg & EF_ON)){ + a->tmpvolume=0; + } + } + else{ + a->note=note; + + period=GetPeriod(note+a->transpose,a->c2spd); + + a->wantedperiod=period; + a->tmpperiod=period; + + a->kick=1; + a->start=0; + + /* retrig tremolo and vibrato waves ? */ + + if(!(a->wavecontrol&0x80)) a->trmpos=0; + if(!(a->wavecontrol&0x08)) a->vibpos=0; + } + break; + + case UNI_INSTRUMENT: + inst=UniGetByte(); + if(inst>=pf->numins) break; /* <- safety valve */ + + a->sample=inst; + + i=&pf->instruments[inst]; + a->i=i; + + if(i->samplenumber[a->note]>=i->numsmp) break; + + s=&i->samples[i->samplenumber[a->note]]; + a->s=s; + + /* channel or instrument determined panning ? */ + + if(s->flags& SF_OWNPAN){ + a->panning=s->panning; + } + else{ + a->panning=pf->panning[mp_channel]; + } + + a->transpose=s->transpose; + a->handle=s->handle; + a->tmpvolume=s->volume; + a->volume=s->volume; + a->c2spd=s->c2spd; + a->retrig=0; + a->s3mtremor=0; + + period=GetPeriod(a->note+a->transpose,a->c2spd); + + a->wantedperiod=period; + a->tmpperiod=period; + break; + + default: + UniSkipOpcode(c); + break; + } + } +} + + + + +void PlayEffects(void) +{ + UBYTE c,dat; + + if(a->row==NULL) return; + + UniSetRow(a->row); + + a->ownper=0; + a->ownvol=0; + + while(c=UniGetByte()){ + + switch(c){ + + case UNI_NOTE: + case UNI_INSTRUMENT: + UniSkipOpcode(c); + break; + + case UNI_PTEFFECT0: + DoPTEffect0(UniGetByte()); + break; + + case UNI_PTEFFECT1: + dat=UniGetByte(); + if(dat!=0) a->slidespeed=(UWORD)dat<<2; + if(vbtick) a->tmpperiod-=a->slidespeed; + break; + + case UNI_PTEFFECT2: + dat=UniGetByte(); + if(dat!=0) a->slidespeed=(UWORD)dat<<2; + if(vbtick) a->tmpperiod+=a->slidespeed; + break; + + case UNI_PTEFFECT3: + dat=UniGetByte(); + a->kick=0; /* temp XM fix */ + if(dat!=0){ + a->portspeed=dat; + a->portspeed<<=2; + } + DoToneSlide(); + a->ownper=1; + break; + + case UNI_PTEFFECT4: + dat=UniGetByte(); + if(dat&0x0f) a->vibdepth=dat&0xf; + if(dat&0xf0) a->vibspd=(dat&0xf0)>>2; + DoVibrato(); + a->ownper=1; + break; + + case UNI_PTEFFECT5: + dat=UniGetByte(); + a->kick=0; + DoToneSlide(); + DoVolSlide(dat); + a->ownper=1; + break; + + case UNI_PTEFFECT6: + dat=UniGetByte(); + DoVibrato(); + DoVolSlide(dat); + a->ownper=1; + break; + + case UNI_PTEFFECT7: + dat=UniGetByte(); + if(dat&0x0f) a->trmdepth=dat&0xf; + if(dat&0xf0) a->trmspd=(dat&0xf0)>>2; + DoTremolo(); + a->ownvol=1; + break; + + case UNI_PTEFFECT8: + dat=UniGetByte(); + if(mp_panning){ + a->panning=dat; + pf->panning[mp_channel]=dat; + } + break; + + case UNI_PTEFFECT9: + dat=UniGetByte(); + if(dat) a->soffset=(UWORD)dat<<8; /* <- 0.43 fix.. */ + a->start=a->soffset; + if(a->start>a->s->length) a->start=a->s->length; + break; + + case UNI_PTEFFECTA: + DoVolSlide(UniGetByte()); + break; + + case UNI_PTEFFECTB: + dat=UniGetByte(); + if(patdly2) break; + patbrk=0; + mp_sngpos=dat-1; + posjmp=3; + break; + + case UNI_PTEFFECTC: + dat=UniGetByte(); + if(vbtick) break; + if(dat>64) dat=64; + a->tmpvolume=dat; + break; + + case UNI_PTEFFECTD: + dat=UniGetByte(); + if(patdly2) break; + { + int hi=(dat&0xf0)>>4; + int lo=(dat&0xf); + patbrk=(hi*10)+lo; + } + if(patbrk>64) patbrk=64; /* <- v0.42 fix */ + posjmp=3; + break; + + case UNI_PTEFFECTE: + DoEEffects(UniGetByte()); + break; + + case UNI_PTEFFECTF: + dat=UniGetByte(); + + if(vbtick || patdly2) break; + + if(mp_extspd && dat>=0x20){ + mp_bpm=dat; + } + else{ + if(dat){ /* <- v0.44 bugfix */ + mp_sngspd=dat; + vbtick=0; + } + } + break; + + case UNI_S3MEFFECTD: + DoS3MVolSlide(UniGetByte()); + break; + + case UNI_S3MEFFECTE: + DoS3MSlideDn(UniGetByte()); + break; + + case UNI_S3MEFFECTF: + DoS3MSlideUp(UniGetByte()); + break; + + case UNI_S3MEFFECTI: + DoS3MTremor(UniGetByte()); + a->ownvol=1; + break; + + case UNI_S3MEFFECTQ: + DoS3MRetrig(UniGetByte()); + break; + + case UNI_S3MEFFECTA: + DoS3MSpeed(UniGetByte()); + break; + + case UNI_S3MEFFECTT: + DoS3MTempo(UniGetByte()); + break; + + case UNI_XMEFFECTA: + DoXMVolSlide(UniGetByte()); + break; + + case UNI_XMEFFECTG: + globalvolume=UniGetByte(); + break; + + case UNI_XMEFFECTH: + DoXMGlobalSlide(UniGetByte()); + break; + + case UNI_XMEFFECTP: + DoXMPanSlide(UniGetByte()); + break; + + default: + UniSkipOpcode(c); + break; + } + } + + if(!a->ownper){ + a->period=a->tmpperiod; + } + + if(!a->ownvol){ + a->volume=a->tmpvolume; + } +} + + + + +SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b) +{ + return(Interpolate(p,a->pos,b->pos,a->val,b->val)); +} + + +SWORD DoPan(SWORD envpan,SWORD pan) +{ + return(pan + (((envpan-128)*(128-abs(pan-128)))/128)); +} + + + +void StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE sus,UBYTE beg,UBYTE end,ENVPT *p) +{ + t->flg=flg; + t->pts=pts; + t->sus=sus; + t->beg=beg; + t->end=end; + t->env=p; + t->p=0; + t->a=0; + t->b=1; +} + + + +SWORD ProcessEnvelope(ENVPR *t,SWORD v,UBYTE keyon) +{ + if(t->flg & EF_ON){ + + /* panning active? -> copy variables */ + + UBYTE a,b; + UWORD p; + + a=t->a; + b=t->b; + p=t->p; + + /* compute the envelope value between points a and b */ + + v=InterpolateEnv(p,&t->env[a],&t->env[b]); + + /* Should we sustain? (sustain flag on, key-on, point a is the sustain + point, and the pointer is exactly on point a) */ + + if((t->flg & EF_SUSTAIN) && keyon && a==t->sus && p==t->env[a].pos){ + /* do nothing */ + } + else{ + /* don't sustain, so increase pointer. */ + + p++; + + /* pointer reached point b? */ + + if(p >= t->env[b].pos){ + + /* shift points a and b */ + + a=b; b++; + + if(t->flg & EF_LOOP){ + if(b > t->end){ + a=t->beg; + b=a+1; + p=t->env[a].pos; + } + } + else{ + if(b >= t->pts){ + b--; + p--; + } + } + } + } + t->a=a; + t->b=b; + t->p=p; + } + return v; +} + +/* +long GetFreq2(long period) +{ + float frequency; + + frequency=8363.0*pow(2,((6*12*16*4.0)-period)/(12*16*4.0)); + return(floor(frequency)); +} +*/ + +long GetFreq2(long period) +{ + int okt; + long frequency; + period=7680-period; + okt=period/768; + frequency=lintab[period%768]; + frequency<<=2; + return(frequency>>(7-okt)); +} + +void MP_HandleTick(void) +{ + int z,t,tr; + ULONG tmpvol; + + if(isfirst){ + /* don't handle the very first ticks, this allows the + other hardware to settle down so we don't loose any + starting notes + */ + isfirst--; + return; + } + + if(forbid) return; /* don't go any further when forbid is true */ + + if(MP_Ready()) return; + + if(++vbtick>=mp_sngspd){ + + mp_patpos++; + vbtick=0; + + if(patdly){ + patdly2=patdly; + patdly=0; + } + + if(patdly2){ + + /* patterndelay active */ + + if(--patdly2){ + mp_patpos--; /* so turn back mp_patpos by 1 */ + } + } + + /* Do we have to get a new patternpointer ? + (when mp_patpos reaches 64 or when + a patternbreak is active) */ + + if( mp_patpos == numrow ) posjmp=3; + + + if( posjmp ){ + mp_patpos=patbrk; + mp_sngpos+=(posjmp-2); + patbrk=posjmp=0; + if(mp_sngpos>=pf->numpos){ + if(!mp_loop) return; + mp_sngpos=pf->reppos; + } + if(mp_sngpos<0) mp_sngpos=pf->numpos-1; + } + + + if(!patdly2){ + + for(t=0;tnumchn;t++){ + + tr=pf->patterns[(pf->positions[mp_sngpos]*pf->numchn)+t]; + numrow=pf->pattrows[pf->positions[mp_sngpos]]; + + mp_channel=t; + a=&mp_audio[t]; + a->row=(trnumtrk) ? UniFindRow(pf->tracks[tr],mp_patpos) : NULL; + + PlayNote(); + } + } + } + + /* Update effects */ + + for(t=0;tnumchn;t++){ + mp_channel=t; + a=&mp_audio[t]; + PlayEffects(); + } + + for(t=0;tnumchn;t++){ + INSTRUMENT *i; + SAMPLE *s; + SWORD envpan,envvol; + + a=&mp_audio[t]; + i=a->i; + s=a->s; + + if(i==NULL || s==NULL) continue; + + if(a->period<40) a->period=40; + if(a->period>8000) a->period=8000; + + if(a->kick){ + MD_VoicePlay(t,a->handle,a->start,s->length,s->loopstart,s->loopend,s->flags); + a->kick=0; + a->keyon=1; + + a->fadevol=32768; + + StartEnvelope(&a->venv,i->volflg,i->volpts,i->volsus,i->volbeg,i->volend,i->volenv); + StartEnvelope(&a->penv,i->panflg,i->panpts,i->pansus,i->panbeg,i->panend,i->panenv); + } + + envvol=ProcessEnvelope(&a->venv,256,a->keyon); + envpan=ProcessEnvelope(&a->penv,128,a->keyon); + + tmpvol=a->fadevol; /* max 32768 */ + tmpvol*=envvol; /* * max 256 */ + tmpvol*=a->volume; /* * max 64 */ + tmpvol/=16384; /* tmpvol/(256*64) => tmpvol is max 32768 */ + + tmpvol*=globalvolume; /* * max 64 */ + tmpvol*=mp_volume; /* * max 100 */ + tmpvol/=3276800UL; /* tmpvol/(64*100*512) => tmpvol is max 64 */ + + MD_VoiceSetVolume(t,tmpvol); + + if(s->flags& SF_OWNPAN){ + MD_VoiceSetPanning(t,DoPan(envpan,a->panning)); + } + else{ + MD_VoiceSetPanning(t,a->panning); + } + + if(pf->flags&UF_LINEAR) + MD_VoiceSetFrequency(t,GetFreq2(a->period)); + else + MD_VoiceSetFrequency(t,(3579546UL<<2)/a->period); + + /* if key-off, start substracting + fadeoutspeed from fadevol: */ + + if(!a->keyon){ + if(a->fadevol>=i->volfade) + a->fadevol-=i->volfade; + else + a->fadevol=0; + } + } +} + + + +void MP_Init(UNIMOD *m) +{ + int t; + + pf=m; + reppos=0; + repcnt=0; + mp_sngpos=0; + mp_sngspd=m->initspeed; + + vbtick=mp_sngspd; + patdly=0; + patdly2=0; + mp_bpm=m->inittempo; + + forbid=0; + mp_patpos=0; + posjmp=2; /* <- make sure the player fetches the first note */ + patbrk=0; + + isfirst=2; /* delay start by 2 ticks */ + + globalvolume=64; /* reset global volume */ + + /* Make sure the player doesn't start with garbage: */ + + for(t=0;tnumchn;t++){ + mp_audio[t].kick=0; + mp_audio[t].tmpvolume=0; + mp_audio[t].retrig=0; + mp_audio[t].wavecontrol=0; + mp_audio[t].glissando=0; + mp_audio[t].soffset=0; + } +} + + + +int MP_Ready(void) +{ + return(mp_sngpos>=pf->numpos); +} + + +void MP_NextPosition(void) +{ + forbid=1; + posjmp=3; + patbrk=0; + vbtick=mp_sngspd; + forbid=0; +} + + +void MP_PrevPosition(void) +{ + forbid=1; + posjmp=1; + patbrk=0; + vbtick=mp_sngspd; + forbid=0; +} + + +void MP_SetPosition(UWORD pos) +{ + if(pos>=pf->numpos) pos=pf->numpos; + forbid=1; + posjmp=2; + patbrk=0; + mp_sngpos=pos; + vbtick=mp_sngspd; + forbid=0; +} diff --git a/libs/oldmik/src/mtypes.h b/libs/oldmik/src/mtypes.h new file mode 100644 index 0000000..65cda6d --- /dev/null +++ b/libs/oldmik/src/mtypes.h @@ -0,0 +1,73 @@ +#ifndef MTYPES_H +#define MTYPES_H + +/* + MikMod atomic types: + ==================== +*/ + + +#ifdef __OS2__ + +typedef signed char SBYTE; /* has to be 1 byte signed */ +typedef unsigned char UBYTE; /* has to be 1 byte unsigned */ +typedef short SWORD; /* has to be 2 bytes signed */ +typedef unsigned short UWORD; /* has to be 2 bytes unsigned */ +typedef long SLONG; /* has to be 4 bytes signed */ +/* ULONG and BOOL are already defined in OS2.H */ + +#elif defined(__alpha) + +typedef char SBYTE; /* has to be 1 byte signed */ +typedef unsigned char UBYTE; /* has to be 1 byte unsigned */ +typedef short SWORD; /* has to be 2 bytes signed */ +typedef unsigned short UWORD; /* has to be 2 bytes unsigned */ +/* long is 8 bytes on dec alpha - RCA */ +typedef int SLONG; /* has to be 4 bytes signed */ +typedef unsigned int ULONG; /* has to be 4 bytes unsigned */ +typedef int BOOL; /* doesn't matter.. 0=FALSE, <>0 true */ + +#else + +typedef signed char SBYTE; /* has to be 1 byte signed */ +typedef unsigned char UBYTE; /* has to be 1 byte unsigned */ +typedef short SWORD; /* has to be 2 bytes signed */ +typedef unsigned short UWORD; /* has to be 2 bytes unsigned */ +typedef long SLONG; /* has to be 4 bytes signed */ +typedef unsigned long ULONG; /* has to be 4 bytes unsigned */ +typedef int BOOL; /* doesn't matter.. 0=FALSE, <>0 true */ + +#endif + + +#ifdef __OS2__ +#define INCL_DOS +#define INCL_MCIOS2 +#define INCL_MMIOOS2 +#include +#include +#include +#endif + + +#ifdef __WATCOMC__ +#define inportb(x) inp(x) +#define outportb(x,y) outp(x,y) +#define inport(x) inpw(x) +#define outport(x,y) outpw(x,y) +#define disable() _disable() +#define enable() _enable() +#endif + + +#ifdef __DJGPP__ +#include +#include +#include +#define inp inportw +#define outport outportw +#define inport inportw +#define interrupt +#endif + +#endif diff --git a/libs/oldmik/src/munitrk.c b/libs/oldmik/src/munitrk.c new file mode 100644 index 0000000..e976e79 --- /dev/null +++ b/libs/oldmik/src/munitrk.c @@ -0,0 +1,346 @@ +/* + +Name: +MUNITRK.C + +Description: +All routines dealing with the manipulation of UNITRK(tm) streams + +Portability: +All systems - all compilers + +*/ +#include +#include +#include "mikmod.h" + +#define BUFPAGE 128 /* smallest unibuffer size */ +#define TRESHOLD 16 + +/* unibuffer is increased by BUFPAGE + bytes when unipc reaches unimax-TRESHOLD */ + + + +/* + Ok.. I'll try to explain the new internal module format.. so here it goes: + + + The UNITRK(tm) Format: + ====================== + + A UNITRK stream is an array of bytes representing a single track + of a pattern. It's made up of 'repeat/length' bytes, opcodes and + operands (sort of a assembly language): + + rrrlllll + [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND].. + ^ ^ ^ + |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track... + + + The rep/len byte contains the number of bytes in the current row, + _including_ the length byte itself (So the LENGTH byte of row 0 in the + previous example would have a value of 5). This makes it easy to search + through a stream for a particular row. A track is concluded by a 0-value + length byte. + + The upper 3 bits of the rep/len byte contain the number of times -1 this + row is repeated for this track. (so a value of 7 means this row is repeated + 8 times) + + Opcodes can range from 1 to 255 but currently only opcodes 1 to 19 are + being used. Each opcode can have a different number of operands. You can + find the number of operands to a particular opcode by using the opcode + as an index into the 'unioperands' table. + +*/ + + + +UWORD unioperands[256]={ + 0, /* not used */ + 1, /* UNI_NOTE */ + 1, /* UNI_INSTRUMENT */ + 1, /* UNI_PTEFFECT0 */ + 1, /* UNI_PTEFFECT1 */ + 1, /* UNI_PTEFFECT2 */ + 1, /* UNI_PTEFFECT3 */ + 1, /* UNI_PTEFFECT4 */ + 1, /* UNI_PTEFFECT5 */ + 1, /* UNI_PTEFFECT6 */ + 1, /* UNI_PTEFFECT7 */ + 1, /* UNI_PTEFFECT8 */ + 1, /* UNI_PTEFFECT9 */ + 1, /* UNI_PTEFFECTA */ + 1, /* UNI_PTEFFECTB */ + 1, /* UNI_PTEFFECTC */ + 1, /* UNI_PTEFFECTD */ + 1, /* UNI_PTEFFECTE */ + 1, /* UNI_PTEFFECTF */ + 1, /* UNI_S3MEFFECTA */ + 1, /* UNI_S3MEFFECTD */ + 1, /* UNI_S3MEFFECTE */ + 1, /* UNI_S3MEFFECTF */ + 1, /* UNI_S3MEFFECTI */ + 1, /* UNI_S3MEFFECTQ */ + 1, /* UNI_S3MEFFECTT */ + 1, /* UNI_XMEFFECTA */ + 1, /* UNI_XMEFFECTG */ + 1, /* UNI_XMEFFECTH */ + 1 /* UNI_XMEFFECTP */ +}; + + +/*************************************************************************** +>>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<< +***************************************************************************/ + + +static UBYTE *rowstart; /* startadress of a row */ +static UBYTE *rowend; /* endaddress of a row (exclusive) */ +static UBYTE *rowpc; /* current unimod(tm) programcounter */ + + +void UniSetRow(UBYTE *t) +{ + rowstart=t; + rowpc=rowstart; + rowend=rowstart+(*(rowpc++)&0x1f); +} + + +UBYTE UniGetByte(void) +{ + return (rowpc end of track.. */ + + l=(c>>5)+1; /* extract repeat value */ + + if(l>row) break; /* reached wanted row? -> return pointer */ + + row-=l; /* havn't reached row yet.. update row */ + t+=c&0x1f; /* point t to the next row */ + } + + return t; +} + + + +/*************************************************************************** +>>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<< +***************************************************************************/ + + +static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */ +static UWORD unimax; /* maximum number of bytes to be written to this buffer */ + +static UWORD unipc; /* index in the buffer where next opcode will be written */ +static UWORD unitt; /* holds index of the rep/len byte of a row */ +static UWORD lastp; /* holds index to the previous row (needed for compressing) */ + + +void UniReset(void) +/* + Resets index-pointers to create a new track. +*/ +{ + unitt=0; /* reset index to rep/len byte */ + unipc=1; /* first opcode will be written to index 1 */ + lastp=0; /* no previous row yet */ + unibuf[0]=0; /* clear rep/len byte */ +} + + +void UniWrite(UBYTE data) +/* + Appends one byte of data to the current row of a track. +*/ +{ + /* write byte to current position and update */ + + unibuf[unipc++]=data; + + /* Check if we've reached the end of the buffer */ + + if(unipc>(unimax-TRESHOLD)){ + + UBYTE *newbuf; + + /* We've reached the end of the buffer, so expand + the buffer by BUFPAGE bytes */ + + newbuf=(UBYTE *)realloc(unibuf,unimax+BUFPAGE); + + /* Check if realloc succeeded */ + + if(newbuf!=NULL){ + unibuf=newbuf; + unimax+=BUFPAGE; + } + else{ + /* realloc failed, so decrease unipc so we won't write beyond + the end of the buffer.. I don't report the out-of-memory + here; the UniDup() will fail anyway so that's where the + loader sees that something went wrong */ + + unipc--; + } + } +} + + +void UniInstrument(UBYTE ins) +/* + Appends UNI_INSTRUMENT opcode to the unitrk stream. +*/ +{ + UniWrite(UNI_INSTRUMENT); + UniWrite(ins); +} + + +void UniNote(UBYTE note) +/* + Appends UNI_NOTE opcode to the unitrk stream. +*/ +{ + UniWrite(UNI_NOTE); + UniWrite(note); +} + + +void UniPTEffect(UBYTE eff,UBYTE dat) +/* + Appends UNI_PTEFFECTX opcode to the unitrk stream. +*/ +{ + if(eff!=0 || dat!=0){ /* don't write empty effect */ + UniWrite(UNI_PTEFFECT0+eff); + UniWrite(dat); + } +} + + +BOOL MyCmp(UBYTE *a,UBYTE *b,UWORD l) +{ + UWORD t; + + for(t=0;t>5)+1; /* repeat of previous row */ + l=(unibuf[lastp]&0x1f); /* length of previous row */ + + len=unipc-unitt; /* length of current row */ + + /* Now, check if the previous and the current row are identical.. + when they are, just increase the repeat field of the previous row */ + + if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)){ + unibuf[lastp]+=0x20; + unipc=unitt+1; + } + else{ + /* current and previous row aren't equal.. so just update the pointers */ + + unibuf[unitt]=len; + lastp=unitt; + unitt=unipc; + unipc++; + } +} + + +UBYTE *UniDup(void) +/* + Terminates the current unitrk stream and returns a pointer + to a copy of the stream. +*/ +{ + UBYTE *d; + + unibuf[unitt]=0; + + if((d=(UBYTE *)malloc(unipc))==NULL){ + myerr=ERROR_ALLOC_STRUCT; + return NULL; + } + memcpy(d,unibuf,unipc); + + return d; +} + + +UWORD TrkLen(UBYTE *t) +/* + Determines the length (in rows) of a unitrk stream 't' +*/ +{ + UWORD len=0; + UBYTE c; + + while(c=*t&0x1f){ + len+=c; + t+=c; + } + len++; + + return len; +} + + +BOOL UniInit(void) +{ + unimax=BUFPAGE; + + if(!(unibuf=(UBYTE *)malloc(unimax))){ + myerr=ERROR_ALLOC_STRUCT; + return 0; + } + return 1; +} + + +void UniCleanup(void) +{ + if(unibuf!=NULL) free(unibuf); + unibuf=NULL; +} + diff --git a/libs/oldmik/src/resample.asm b/libs/oldmik/src/resample.asm new file mode 100644 index 0000000..afb2540 --- /dev/null +++ b/libs/oldmik/src/resample.asm @@ -0,0 +1,132 @@ +.386p + + NAME resample + EXTRN _rvolsel :WORD + EXTRN _lvolsel :WORD + + .model small,c + +DGROUP GROUP _DATA + +_TEXT SEGMENT DWORD PUBLIC USE32 'CODE' + ASSUME CS:_TEXT ,DS:DGROUP,SS:DGROUP + + PUBLIC AsmStereoNormal_ + PUBLIC AsmMonoNormal_ + +SS2 MACRO index + even + mov edx,ebx + sar edx,0bh + mov al,[esi+edx] + add ebx,ecx + mov edx,es:[eax*4] + add (index*8)[edi],edx + mov edx,fs:[eax*4] + add (4+(index*8))[edi],edx + ENDM + +SM2 MACRO index + even + mov edx,ebx + add ebx,ecx + sar edx,0bh + mov al,[esi+edx] + mov edx,es:[eax*4] + add (index*4)[edi],edx + ENDM + + +AsmStereoNormal_ proc USES ebp fs es + mov ax,_lvolsel + mov es,ax ; voltab selector naar fs + mov ax,_rvolsel + mov fs,ax + xor eax,eax + push edx + shr edx,4 + jz sskip16 + mov ebp,edx +sagain16: + SS2 0 + SS2 1 + SS2 2 + SS2 3 + SS2 4 + SS2 5 + SS2 6 + SS2 7 + SS2 8 + SS2 9 + SS2 10 + SS2 11 + SS2 12 + SS2 13 + SS2 14 + SS2 15 + add edi,(16*8) + dec ebp + jnz sagain16 +sskip16: + pop edx + and edx,15 + jz sskip1 + mov ebp,edx +sagain1: + SS2 0 + add edi,8 + dec ebp + jnz sagain1 +sskip1: + ret +AsmStereoNormal_ endp + + +AsmMonoNormal_ proc USES ebp es + mov ax,_lvolsel + mov es,ax ; voltab selector naar fs + xor eax,eax + push edx + shr edx,4 + jz mskip16 + mov ebp,edx +magain16: + SM2 0 + SM2 1 + SM2 2 + SM2 3 + SM2 4 + SM2 5 + SM2 6 + SM2 7 + SM2 8 + SM2 9 + SM2 10 + SM2 11 + SM2 12 + SM2 13 + SM2 14 + SM2 15 + add edi,(16*4) + dec ebp + jnz magain16 +mskip16: + pop edx + and edx,15 + jz mskip1 + mov ebp,edx +magain1: + SM2 0 + add edi,4 + dec ebp + jnz magain1 +mskip1: + ret +AsmMonoNormal_ endp + +_TEXT ENDS + +_DATA SEGMENT DWORD PUBLIC USE32 'DATA' +_DATA ENDS + + END diff --git a/libs/oldmik/src/virtch.c b/libs/oldmik/src/virtch.c new file mode 100644 index 0000000..8f7e357 --- /dev/null +++ b/libs/oldmik/src/virtch.c @@ -0,0 +1,852 @@ +/* + +Name: +VIRTCH.C + +Description: +All-c sample mixing routines, using a 32 bits mixing buffer + +Portability: +All systems - all compilers + +*/ +#include +#include +#include +#include +#include "mikmod.h" + +#define FRACBITS 11 +#define FRACMASK ((1L< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + UBYTE vol; /* current volume */ + UBYTE pan; /* current panning position */ + SLONG current; /* current index in the sample */ + SLONG increment; /* fixed-point increment value */ +#ifdef __WATCOMC__ + UWORD lvolsel; /* left volume table selector */ + UWORD rvolsel; /* right volume table selector */ +#else + SLONG lvolmul; /* left volume multiply */ + SLONG rvolmul; /* right volume multiply */ +#endif +} VINFO; + + +static VINFO vinf[32]; +static VINFO *vnf; + +static UWORD samplesthatfit; +static SLONG idxsize,idxlpos,idxlend,maxvol; + +static long per256; +static int ampshift; + + +#ifdef __WATCOMC__ + +static SLONG voltab[65][256]; +static UWORD volsel[65]; + +#ifdef __cplusplus +extern "C" { +#endif + +UWORD lvolsel,rvolsel; + +void AsmStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo); +#pragma aux AsmStereoNormal \ + parm [esi] [edi] [ebx] [ecx] [edx] \ + modify [eax]; + + +void AsmMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo); +#pragma aux AsmMonoNormal \ + parm [esi] [edi] [ebx] [ecx] [edx] \ + modify [eax]; + +#ifdef __cplusplus +} +#endif + +void freedescriptor(unsigned short selector); +#pragma aux freedescriptor = \ + "mov ax,0001h" \ + "int 31h" \ + parm [bx] \ + modify [ax]; + +unsigned short getalias(void); +#pragma aux getalias = \ + "mov ax,cs " \ + "mov bx,ax " \ + "mov ax,000ah" \ + "int 31h " \ + "jnc isok " \ + "xor ax,ax " \ + "isok: " \ + modify [bx] \ + value [ax]; + +void setbase(unsigned short selector,unsigned long offset); +#pragma aux setbase = \ + "mov ax,0007h" \ + "mov ecx,edx" \ + "ror ecx,16" \ + "int 31h" \ + parm [bx] [edx] \ + modify [ax ecx] ; + +void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift); +#pragma aux VC_Sample32To16Copy = \ +"again:" \ + "mov eax,[esi]" \ + "sar eax,cl" \ + "cmp eax,32767" \ + "jg toobig" \ + "cmp eax,-32768" \ + "jl toosmall" \ +"write:" \ + "mov [edi],ax" \ + "add esi,4" \ + "add edi,2" \ + "dec edx" \ + "jnz again" \ + "jmp ready" \ +"toobig:" \ + "mov eax,32767" \ + "jmp write" \ +"toosmall:" \ + "mov eax,-32768" \ + "jmp write" \ +"ready:" \ + parm [esi] [edi] [edx] [cl] \ + modify [eax] ; + + +void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift); +#pragma aux VC_Sample32To8Copy = \ +"again:" \ + "mov eax,[esi]" \ + "sar eax,cl" \ + "cmp eax,127" \ + "jg toobig" \ + "cmp eax,-128" \ + "jl toosmall" \ +"write:" \ + "add al,080h" \ + "mov [edi],al" \ + "add esi,4" \ + "inc edi" \ + "dec edx" \ + "jnz again" \ + "jmp ready" \ +"toobig:" \ + "mov eax,127" \ + "jmp write" \ +"toosmall:" \ + "mov eax,-128" \ + "jmp write" \ +"ready:" \ + parm [esi] [edi] [edx] [cl] \ + modify [eax] ; + + +#else + + +static SLONG lvolmul,rvolmul; + + +static void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift) +{ + SLONG c; + int shift=(24-ampshift); + + while(count--){ + c=*srce >> shift; + if(c>127) c=127; + else if(c<-128) c=-128; + *dest++=c+128; + srce++; + } +} + + +static void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift) +{ + SLONG c; + int shift=(16-ampshift); + + while(count--){ + c=*srce >> shift; + if(c>32767) c=32767; + else if(c<-32768) c=-32768; + *dest++=c; + srce++; + } +} + +#endif + + +static SLONG fraction2long(ULONG dividend,UWORD divisor) +/* + Converts the fraction 'dividend/divisor' into a fixed point longword. +*/ +{ + ULONG whole,part; + + whole=dividend/divisor; + part=((dividend%divisor)<>=1; + if(md_mode & DMODE_STEREO) bytes>>=1; + return bytes; +} + + +/************************************************** +*************************************************** +*************************************************** +**************************************************/ + + +static SBYTE *Samples[MAXSAMPLEHANDLES]; + + +BOOL LargeRead(SBYTE *buffer,ULONG size) +{ + int t; + ULONG todo; + + while(size){ + /* how many bytes to load (in chunks of 8000) ? */ + + todo=(size>8000)?8000:size; + + /* read data */ + + SL_Load(buffer,todo); + /* and update pointers.. */ + + size-=todo; + buffer+=todo; + } + return 1; +} + + + +SWORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags) +{ + int handle; + ULONG t; + + SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS); + + /* Find empty slot to put sample address in */ + + for(handle=0;handle0){ + sample=srce[index>>FRACBITS]; + *(dest++)+=lvolmul*sample; + *(dest++)+=rvolmul*sample; + index+=increment; + todo--; + } +} + + +static void MixMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo) +{ + SBYTE sample; + + while(todo>0){ + sample=srce[index>>FRACBITS]; + *(dest++)+=lvolmul*sample; + index+=increment; + todo--; + } +} + + +static void MixStereoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo) +{ + SWORD sample,a,b; + + while(todo>0){ + a=srce[index>>FRACBITS]; + b=srce[1+(index>>FRACBITS)]; + sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS); + + *(dest++)+=lvolmul*sample; + *(dest++)+=rvolmul*sample; + index+=increment; + todo--; + } +} + + +static void MixMonoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo) +{ + SWORD sample,a,b; + + while(todo>0){ + a=srce[index>>FRACBITS]; + b=srce[1+(index>>FRACBITS)]; + sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS); + + *(dest++)+=lvolmul*sample; + + index+=increment; + todo--; + } +} + +#endif + + +static UWORD NewPredict(SLONG index,SLONG end,SLONG increment,UWORD todo) +/* + This functions returns the number of resamplings we can do so that: + + - it never accesses indexes bigger than index 'end' + - it doesn't do more than 'todo' resamplings +*/ +{ + SLONG di; + + di=(end-index)/increment; + index+=(di*increment); + + if(increment<0){ + while(index>=end){ + index+=increment; + di++; + } + } + else{ + while(index<=end){ + index+=increment; + di++; + } + } + return ((di0){ + + /* update the 'current' index so the sample loops, or + stops playing if it reached the end of the sample */ + + if(vnf->flags&SF_REVERSE){ + + /* The sample is playing in reverse */ + + if(vnf->flags&SF_LOOP){ + + /* the sample is looping, so check if + it reached the loopstart index */ + + if(vnf->currentflags&SF_BIDI){ + + /* sample is doing bidirectional loops, so 'bounce' + the current index against the idxlpos */ + + vnf->current=idxlpos+(idxlpos-vnf->current); + vnf->flags&=~SF_REVERSE; + vnf->increment=-vnf->increment; + } + else + /* normal backwards looping, so set the + current position to loopend index */ + + vnf->current=idxlend-(idxlpos-vnf->current); + } + } + else{ + + /* the sample is not looping, so check + if it reached index 0 */ + + if(vnf->current<0){ + + /* playing index reached 0, so stop + playing this sample */ + + vnf->current=0; + vnf->active=0; + break; + } + } + } + else{ + + /* The sample is playing forward */ + + if(vnf->flags&SF_LOOP){ + + /* the sample is looping, so check if + it reached the loopend index */ + + if(vnf->current>idxlend){ + if(vnf->flags&SF_BIDI){ + + /* sample is doing bidirectional loops, so 'bounce' + the current index against the idxlend */ + + vnf->flags|=SF_REVERSE; + vnf->increment=-vnf->increment; + vnf->current=idxlend-(vnf->current-idxlend); /* ?? */ + } + else + /* normal backwards looping, so set the + current position to loopend index */ + + vnf->current=idxlpos+(vnf->current-idxlend); + } + } + else{ + + /* sample is not looping, so check + if it reached the last position */ + + if(vnf->current>idxsize){ + + /* yes, so stop playing this sample */ + + vnf->current=0; + vnf->active=0; + break; + } + } + } + + /* Vraag een far ptr op van het sampleadres + op byte offset vnf->current, en hoeveel samples + daarvan geldig zijn (VOORDAT segment overschrijding optreed) */ + + if(!(s=Samples[vnf->handle])){ + vnf->current=0; + vnf->active=0; + break; + } + + if(vnf->flags & SF_REVERSE) + end = (vnf->flags & SF_LOOP) ? idxlpos : 0; + else + end = (vnf->flags & SF_LOOP) ? idxlend : idxsize; + + /* Als de sample simpelweg niet beschikbaar is, of als + sample gestopt moet worden sample stilleggen en stoppen */ + /* mix 'em: */ + + done=NewPredict(vnf->current,end,vnf->increment,todo); + + if(!done){ +/* printf("predict stopped it. current %ld, end %ld\n",vnf->current,end); +*/ vnf->active=0; + break; + } + + /* optimisation: don't mix anything if volume is zero */ + + if(vnf->vol){ +#ifdef __WATCOMC__ + if(md_mode & DMODE_STEREO) + AsmStereoNormal(s,ptr,vnf->current,vnf->increment,done); + else + AsmMonoNormal(s,ptr,vnf->current,vnf->increment,done); +#else + SampleMix(s,ptr,vnf->current,vnf->increment,done); +#endif + } + vnf->current+=(vnf->increment*done); + + todo-=done; + ptr+=(md_mode & DMODE_STEREO) ? (done<<1) : done; + } +} + + + + +static void VC_FillTick(SBYTE *buf,UWORD todo) +/* + Mixes 'todo' samples to 'buf'.. The number of samples has + to fit into the tickbuffer. +*/ +{ + int t; + + /* clear the mixing buffer: */ + + memset(VC_TICKBUF,0,(md_mode & DMODE_STEREO) ? todo<<3 : todo<<2); + + for(t=0;tactive){ + idxsize=(vnf->size<reppos<repend<lvolsel; + rvolsel=vnf->rvolsel; +#else + lvolmul=vnf->lvolmul; + rvolmul=vnf->rvolmul; +#endif + VC_AddChannel(VC_TICKBUF,todo); + } + } + + if(md_mode & DMODE_16BITS) + VC_Sample32To16Copy(VC_TICKBUF,(SWORD *)buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,16-ampshift); + else + VC_Sample32To8Copy(VC_TICKBUF,buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,24-ampshift); +} + + + +static void VC_WritePortion(SBYTE *buf,UWORD todo) +/* + Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the + number of samples that fit into VC_TICKBUF, the mixing operation is split + up into a number of smaller chunks. +*/ +{ + UWORD part; + + /* write 'part' samples to the buffer */ + + while(todo){ + part=min(todo,samplesthatfit); + VC_FillTick(buf,part); + buf+=samples2bytes(part); + todo-=part; + } +} + + +static UWORD TICKLEFT; + + +void VC_WriteSamples(SBYTE *buf,UWORD todo) +{ + int t; + UWORD part; + + while(todo>0){ + + if(TICKLEFT==0){ + md_player(); + + TICKLEFT=(125L*md_mixfreq)/(50L*md_bpm); + + /* compute volume, frequency counter & panning parameters for each channel. */ + + for(t=0;t>=1; + TICKLEFT=0; +} + + +void VC_PlayStop(void) +{ +} + + +BOOL VC_Init(void) +{ + int t; + for(t=0;t<32;t++){ + vinf[t].current=0; + vinf[t].flags=0; + vinf[t].handle=0; + vinf[t].kick=0; + vinf[t].active=0; + vinf[t].frq=10000; + vinf[t].vol=0; + vinf[t].pan=(t&1)?0:255; + } + +#ifdef __WATCOMC__ + if(md_mode & DMODE_INTERP) md_mode&=~DMODE_INTERP; + + for(t=0;t<65;t++) volsel[t]=0; + + for(t=0;t<65;t++){ + if(!(volsel[t]=getalias())) return 0; + setbase(volsel[t],(ULONG)voltab[t]); + } +#endif + + return 1; +} + + +void VC_Exit(void) +{ +#ifdef __WATCOMC__ + int t; + for(t=0;t<65;t++){ + if(volsel[t]) freedescriptor(volsel[t]); + } +#endif +} + + +void VC_VoiceSetVolume(UBYTE voice,UBYTE vol) +{ + vinf[voice].vol=vol; +} + + +void VC_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ + vinf[voice].frq=frq; +} + + +void VC_VoiceSetPanning(UBYTE voice,UBYTE pan) +{ + vinf[voice].pan=pan; +} + + +void VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ + if(start>=size) return; + + if(flags&SF_LOOP){ + if(repend>size) repend=size; /* repend can't be bigger than size */ + } + + vinf[voice].flags=flags; + vinf[voice].handle=handle; + vinf[voice].start=start; + vinf[voice].size=size; + vinf[voice].reppos=reppos; + vinf[voice].repend=repend; + vinf[voice].kick=1; +} diff --git a/mklibs.bat b/mklibs.bat index 72b4d8d..51f4b28 100644 --- a/mklibs.bat +++ b/mklibs.bat @@ -1,3 +1,5 @@ cd libs\imago wmake %1 %2 %3 %4 %5 %6 %7 %8 +cd ..\oldmik +wmake %1 %2 %3 %4 %5 %6 %7 %8 cd ..\.. diff --git a/src/demo.c b/src/demo.c index 9133a26..3a99314 100644 --- a/src/demo.c +++ b/src/demo.c @@ -35,7 +35,6 @@ int demo_init(int argc, char **argv) g3d_framebuffer(fb_width, fb_height, fb_pixels); if(music_open("data/test.mod") == -1) { - fprintf(stderr, "failed to open music: data/test.mod\n"); return -1; } diff --git a/src/dos/music.c b/src/dos/music.c new file mode 100644 index 0000000..5ed6b48 --- /dev/null +++ b/src/dos/music.c @@ -0,0 +1,81 @@ +#include +#include "music.h" +#include "mikmod.h" + +static void update_callback(void); + +static UNIMOD *mod; +static int initialized; + +static int init(void) +{ + ML_RegisterLoader(&load_mod); + ML_RegisterLoader(&load_s3m); + ML_RegisterLoader(&load_xm); + + MD_RegisterDriver(&drv_nos); + MD_RegisterDriver(&drv_ss); + MD_RegisterDriver(&drv_sb); + MD_RegisterDriver(&drv_gus); + + MD_RegisterPlayer(&update_callback); + + /*md_mode |= DMODE_INTERP;*/ + if(!MD_Init()) { + fprintf(stderr, "mikmod init failed: %s\n", myerr); + return -1; + } + printf("using mikmod driver %s\n", md_driver->Name); + printf(" %d bits, %s, %s mixing at %d Hz\n", md_mode & DMODE_16BITS ? 16 : 8, + md_mode & DMODE_STEREO ? "stereo" : "mono", + md_mode & DMODE_INTERP ? "interpolated" : "normal", + md_mixfreq); + + atexit(MD_Exit); + return 0; +} + +int music_open(const char *fname) +{ + if(!initialized) { + if(init() == -1) { + return -1; + } + initialized = 1; + } + + if(!(mod = ML_LoadFN((const signed char*)fname))) { + fprintf(stderr, "failed to load music: %s: %s\n", fname, myerr); + return -1; + } + md_numchn = mod->numchn; + return 0; +} + +void music_close(void) +{ + if(mod) { + music_stop(); + ML_Free(mod); + mod = 0; + } +} + +void music_play(void) +{ + MD_PlayStart(); +} + +void music_stop(void) +{ + MD_PlayStop(); +} + +void music_update(void) +{ + MD_Update(); +} + +static void update_callback(void) +{ +} diff --git a/src/music.c b/src/music.c deleted file mode 100644 index 365ad01..0000000 --- a/src/music.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "music.h" -#include "mikmod.h" - -static MODULE *mod; -static int initialized; - - -static int init(void) -{ - MikMod_RegisterAllDrivers(); - MikMod_RegisterAllLoaders(); - - md_mode |= DMODE_SOFT_MUSIC; - if(MikMod_Init("") != 0) { - fprintf(stderr, "mikmod init failed: %s\n", - MikMod_strerror(MikMod_errno)); - return -1; - } - return 0; -} - -int music_open(const char *fname) -{ - if(!initialized) { - if(init() == -1) { - return -1; - } - initialized = 1; - } - - if(!(mod = Player_Load(fname, 64, 0))) { - fprintf(stderr, "failed to load music: %s: %s\n", fname, - MikMod_strerror(MikMod_errno)); - return -1; - } - return 0; -} - -void music_close(void) -{ - if(mod) { - music_stop(); - Player_Free(mod); - } -} - -void music_play(void) -{ - Player_Start(mod); -} - -void music_stop(void) -{ - Player_Stop(); -} - -void music_update(void) -{ - if(Player_Active()) { - MikMod_Update(); - } -} diff --git a/src/music.h b/src/music.h index 9d9cb01..7f388ae 100644 --- a/src/music.h +++ b/src/music.h @@ -4,6 +4,7 @@ int music_open(const char *fname); void music_close(void); void music_play(void); +void music_stop(void); void music_update(void); #endif /* MUSIC_H_ */ diff --git a/src/sdl/music.c b/src/sdl/music.c new file mode 100644 index 0000000..e194764 --- /dev/null +++ b/src/sdl/music.c @@ -0,0 +1,63 @@ +#include "music.h" +#include "mikmod.h" + +static MODULE *mod; +static int initialized; + + +static int init(void) +{ + MikMod_RegisterAllDrivers(); + MikMod_RegisterAllLoaders(); + + md_mode |= DMODE_SOFT_MUSIC; + if(MikMod_Init("") != 0) { + fprintf(stderr, "mikmod init failed: %s\n", + MikMod_strerror(MikMod_errno)); + return -1; + } + return 0; +} + +int music_open(const char *fname) +{ + if(!initialized) { + if(init() == -1) { + return -1; + } + initialized = 1; + } + + if(!(mod = Player_Load(fname, 64, 0))) { + fprintf(stderr, "failed to load music: %s: %s\n", fname, + MikMod_strerror(MikMod_errno)); + return -1; + } + return 0; +} + +void music_close(void) +{ + if(mod) { + music_stop(); + Player_Free(mod); + mod = 0; + } +} + +void music_play(void) +{ + Player_Start(mod); +} + +void music_stop(void) +{ + Player_Stop(); +} + +void music_update(void) +{ + if(Player_Active()) { + MikMod_Update(); + } +} -- 1.7.10.4