From: John Tsiombikas Date: Sat, 10 Feb 2018 19:56:45 +0000 (+0200) Subject: attempt to use new mikmod for djgpp build X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=commitdiff_plain;h=a6290d5ab0d369d6807bad92d05a4f38560bf111 attempt to use new mikmod for djgpp build --- diff --git a/Makefile.dj b/Makefile.dj index c2ad183..0f152b7 100644 --- a/Makefile.dj +++ b/Makefile.dj @@ -10,14 +10,14 @@ else TOOLPREFIX = i586-pc-msdosdjgpp- endif -inc = -Isrc -Isrc/dos -Ilibs/imago/src -Ilibs/oldmik/src +inc = -Isrc -Isrc/dos -Ilibs/imago/src -Ilibs/mikmod/include opt = -O3 -ffast-math dbg = -g CC = $(TOOLPREFIX)gcc AR = $(TOOLPREFIX)ar CFLAGS = -pedantic -Wall -march=pentium $(dbg) $(opt) $(inc) -LDFLAGS = libs/imago/imago.dja libs/oldmik/mikmod.dja +LDFLAGS = libs/imago/imago.dja libs/mikmod/dos/libmikmod.a $(bin): $(obj) imago mikmod $(CC) -o $@ -Wl,-Map=ld.map $(obj) $(LDFLAGS) @@ -39,12 +39,12 @@ imago: .PHONY: mikmod mikmod: - $(MAKE) -C libs/oldmik -f Makefile.dj + $(MAKE) -C libs/mikmod/dos -f Makefile.dj .PHONY: cleanlibs cleanlibs: $(MAKE) -C libs/imago -f Makefile.dj clean - $(MAKE) -C libs/oldmik -f Makefile.dj clean + $(MAKE) -C libs/mikmod/dos -f Makefile.dj clean .PHONY: clean .PHONY: cleandep diff --git a/libs/imago/Makefile b/libs/imago/Makefile index 6900d25..7959d19 100644 --- a/libs/imago/Makefile +++ b/libs/imago/Makefile @@ -22,6 +22,12 @@ opt = -5 -fp5 -otexan dbg = -d1 def = -DPNG_NO_SNPRINTF +!ifdef __UNIX__ +RM = rm -f +!else +RM = del +!endif + CC = wcc386 CFLAGS = $(dbg) $(opt) $(def) -zq -bt=dos -Ilibpng -Izlib -Ijpeglib @@ -38,7 +44,7 @@ cflags.occ: Makefile $(CC) -fo=$@ @cflags.occ $[* clean: .symbolic - del *.obj - del *.occ - del *.lbc - del $(alib) + $(RM) *.obj + $(RM) *.occ + $(RM) *.lbc + $(RM) $(alib) diff --git a/libs/mikmod/dos/Makefile.dj b/libs/mikmod/dos/Makefile.dj new file mode 100644 index 0000000..664b83b --- /dev/null +++ b/libs/mikmod/dos/Makefile.dj @@ -0,0 +1,204 @@ +# GNU Makefile for building libMikMod using GCC / DJGPP environment +# +# Edit config.h to disable/enable certain drivers, etc, if necessary. +# +# Targets: +# - all (default): make libmikmod.a, static MikMod sound library +# - clean: clean up (remove all generated files) + +# Set to 1 for debug build +DEBUG = 0 +CROSS = i586-pc-msdosdjgpp + +# The tools +ifeq ($(CROSS),) +CC=gcc +AS=as +AR=ar +RANLIB=ranlib +else +CC=$(CROSS)-gcc +AS=$(CROSS)-as +AR=$(CROSS)-ar +RANLIB=$(CROSS)-ranlib +endif +CFLAGS = -DMIKMOD_BUILD -DHAVE_CONFIG_H $(INCLUDE) +INCLUDE = -I../dos -I../drivers/dos -I../include +ARFLAGS = cr + +ifeq ($(DEBUG),1) +CFLAGS += -g -Wall +else +CFLAGS += -O2 -Wall -fomit-frame-pointer -ffast-math +endif + +.SUFFIXES: +.SUFFIXES: .o .c + +COMPILE=$(CC) -c $(CFLAGS) + +OBJ=out/dosdma.o \ + out/dosirq.o \ + out/dosgus.o \ + out/drv_ultra.o \ + out/dossb.o \ + out/drv_sb.o \ + out/doswss.o \ + out/drv_wss.o \ + out/drv_nos.o \ + out/strcasecmp.o \ + out/load_it.o \ + out/load_mod.o \ + out/load_s3m.o \ + out/load_xm.o \ + out/mmalloc.o \ + out/mmerror.o \ + out/mmio.o \ + out/mdriver.o \ + out/mdreg.o \ + out/mmcmp.o \ + out/pp20.o \ + out/s404.o \ + out/xpk.o \ + out/mloader.o \ + out/mlreg.o \ + out/mlutil.o \ + out/mplayer.o \ + out/munitrk.o \ + out/mwav.o \ + out/npertab.o \ + out/sloader.o \ + out/virtch.o \ + out/virtch2.o \ + out/virtch_common.o +# out/mdulaw.o + +.PHONY: clean + +# The build targets +TARGETS = libmikmod.a + +all: $(TARGETS) + +clean: + rm -rf out/*.o $(TARGETS) + +libmikmod.a: $(OBJ) + $(AR) $(ARFLAGS) $@ $^ + $(RANLIB) $@ + +HEADER_DEPS=../dos/config.h ../include/mikmod.h ../include/mikmod_internals.h ../include/mikmod_ctype.h +out/drv_ultra.o: ../drivers/drv_ultra.c $(HEADER_DEPS) ../drivers/dos/libgus.h + $(COMPILE) ../drivers/drv_ultra.c -o out/drv_ultra.o +out/drv_sb.o: ../drivers/drv_sb.c $(HEADER_DEPS) ../drivers/dos/dossb.h + $(COMPILE) ../drivers/drv_sb.c -o out/drv_sb.o +out/drv_wss.o: ../drivers/drv_wss.c $(HEADER_DEPS) ../drivers/dos/doswss.h + $(COMPILE) ../drivers/drv_wss.c -o out/drv_wss.o +out/dosgus.o: ../drivers/dos/dosgus.c $(HEADER_DEPS) ../drivers/dos/libgus.h + $(COMPILE) ../drivers/dos/dosgus.c -o out/dosgus.o +out/dossb.o: ../drivers/dos/dossb.c $(HEADER_DEPS) ../drivers/dos/dossb.h + $(COMPILE) ../drivers/dos/dossb.c -o out/dossb.o +out/doswss.o: ../drivers/dos/doswss.c $(HEADER_DEPS) ../drivers/dos/doswss.h + $(COMPILE) ../drivers/dos/doswss.c -o out/doswss.o +out/dosdma.o: ../drivers/dos/dosdma.c $(HEADER_DEPS) ../drivers/dos/dosdma.h + $(COMPILE) ../drivers/dos/dosdma.c -o out/dosdma.o +out/dosirq.o: ../drivers/dos/dosirq.c $(HEADER_DEPS) ../drivers/dos/dosirq.h + $(COMPILE) ../drivers/dos/dosirq.c -o out/dosirq.o +out/drv_nos.o: ../drivers/drv_nos.c $(HEADER_DEPS) + $(COMPILE) ../drivers/drv_nos.c -o out/drv_nos.o +out/drv_raw.o: ../drivers/drv_raw.c $(HEADER_DEPS) + $(COMPILE) ../drivers/drv_raw.c -o out/drv_raw.o +out/drv_aiff.o: ../drivers/drv_aiff.c $(HEADER_DEPS) + $(COMPILE) ../drivers/drv_aiff.c -o out/drv_aiff.o +out/drv_wav.o: ../drivers/drv_wav.c $(HEADER_DEPS) + $(COMPILE) ../drivers/drv_wav.c -o out/drv_wav.o +out/drv_stdout.o: ../drivers/drv_stdout.c $(HEADER_DEPS) + $(COMPILE) ../drivers/drv_stdout.c -o out/drv_stdout.o +out/drv_pipe.o: ../drivers/drv_pipe.c $(HEADER_DEPS) + $(COMPILE) ../drivers/drv_pipe.c -o out/drv_pipe.o +out/load_669.o: ../loaders/load_669.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_669.c -o out/load_669.o +out/load_amf.o: ../loaders/load_amf.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_amf.c -o out/load_amf.o +out/load_asy.o: ../loaders/load_asy.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_asy.c -o out/load_asy.o +out/load_dsm.o: ../loaders/load_dsm.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_dsm.c -o out/load_dsm.o +out/load_far.o: ../loaders/load_far.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_far.c -o out/load_far.o +out/load_gdm.o: ../loaders/load_gdm.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_gdm.c -o out/load_gdm.o +out/load_gt2.o: ../loaders/load_gt2.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_gt2.c -o out/load_gt2.o +out/load_it.o: ../loaders/load_it.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_it.c -o out/load_it.o +out/load_imf.o: ../loaders/load_imf.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_imf.c -o out/load_imf.o +out/load_m15.o: ../loaders/load_m15.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_m15.c -o out/load_m15.o +out/load_med.o: ../loaders/load_med.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_med.c -o out/load_med.o +out/load_mod.o: ../loaders/load_mod.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_mod.c -o out/load_mod.o +out/load_mtm.o: ../loaders/load_mtm.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_mtm.c -o out/load_mtm.o +out/load_okt.o: ../loaders/load_okt.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_okt.c -o out/load_okt.o +out/load_s3m.o: ../loaders/load_s3m.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_s3m.c -o out/load_s3m.o +out/load_stm.o: ../loaders/load_stm.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_stm.c -o out/load_stm.o +out/load_stx.o: ../loaders/load_stx.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_stx.c -o out/load_stx.o +out/load_ult.o: ../loaders/load_ult.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_ult.c -o out/load_ult.o +out/load_umx.o: ../loaders/load_umx.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_umx.c -o out/load_umx.o +out/load_uni.o: ../loaders/load_uni.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_uni.c -o out/load_uni.o +out/load_xm.o: ../loaders/load_xm.c $(HEADER_DEPS) + $(COMPILE) ../loaders/load_xm.c -o out/load_xm.o +out/mmalloc.o: ../mmio/mmalloc.c $(HEADER_DEPS) + $(COMPILE) ../mmio/mmalloc.c -o out/mmalloc.o +out/mmerror.o: ../mmio/mmerror.c $(HEADER_DEPS) + $(COMPILE) ../mmio/mmerror.c -o out/mmerror.o +out/mmio.o: ../mmio/mmio.c $(HEADER_DEPS) + $(COMPILE) ../mmio/mmio.c -o out/mmio.o +out/mmcmp.o: ../depackers/mmcmp.c $(HEADER_DEPS) + $(COMPILE) ../depackers/mmcmp.c -o out/mmcmp.o +out/pp20.o: ../depackers/pp20.c $(HEADER_DEPS) + $(COMPILE) ../depackers/pp20.c -o out/pp20.o +out/s404.o: ../depackers/s404.c $(HEADER_DEPS) + $(COMPILE) ../depackers/s404.c -o out/s404.o +out/xpk.o: ../depackers/xpk.c $(HEADER_DEPS) + $(COMPILE) ../depackers/xpk.c -o out/xpk.o +out/mdriver.o: ../playercode/mdriver.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mdriver.c -o out/mdriver.o +out/mdreg.o: ../playercode/mdreg.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mdreg.c -o out/mdreg.o +out/mdulaw.o: ../playercode/mdulaw.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mdulaw.c -o out/mdulaw.o +out/mloader.o: ../playercode/mloader.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mloader.c -o out/mloader.o +out/mlreg.o: ../playercode/mlreg.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mlreg.c -o out/mlreg.o +out/mlutil.o: ../playercode/mlutil.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mlutil.c -o out/mlutil.o +out/mplayer.o: ../playercode/mplayer.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mplayer.c -o out/mplayer.o +out/munitrk.o: ../playercode/munitrk.c $(HEADER_DEPS) + $(COMPILE) ../playercode/munitrk.c -o out/munitrk.o +out/mwav.o: ../playercode/mwav.c $(HEADER_DEPS) + $(COMPILE) ../playercode/mwav.c -o out/mwav.o +out/npertab.o: ../playercode/npertab.c $(HEADER_DEPS) + $(COMPILE) ../playercode/npertab.c -o out/npertab.o +out/sloader.o: ../playercode/sloader.c $(HEADER_DEPS) + $(COMPILE) ../playercode/sloader.c -o out/sloader.o +out/virtch.o: ../playercode/virtch.c ../playercode/virtch_common.c $(HEADER_DEPS) + $(COMPILE) ../playercode/virtch.c -o out/virtch.o +out/virtch2.o: ../playercode/virtch2.c ../playercode/virtch_common.c $(HEADER_DEPS) + $(COMPILE) ../playercode/virtch2.c -o out/virtch2.o +out/virtch_common.o: ../playercode/virtch_common.c $(HEADER_DEPS) + $(COMPILE) ../playercode/virtch_common.c -o out/virtch_common.o +out/strcasecmp.o: ../posix/strcasecmp.c $(HEADER_DEPS) + $(COMPILE) ../posix/strcasecmp.c -o out/strcasecmp.o diff --git a/libs/mikmod/dos/README b/libs/mikmod/dos/README new file mode 100644 index 0000000..b6d104a --- /dev/null +++ b/libs/mikmod/dos/README @@ -0,0 +1,110 @@ + Hello folks ! + + +This is libmikmod, version 3.3.10, a portable sound library for DOS. +Comments & feedback are welcome. + + +>> BUILDING LIBMIKMOD +--------------------- + +- If you're not building libmikmod for DOS, then you're lost in the sources. + Go up one directory, and read the main README file. + +This port has been designed to work only with DJGPP compiler. However, it +should not be too complex to make it compile with any other compiler. If you +manage to make libmikmod compile and work with another compiler, we'd like to +hear from you. You'll likely have to write an appropiate makefile, or build +things manually ... + +If you have all proper tools installed, just type + + make -f Makefile.dj + +To make your library ready to use, copy the files 'libmikmod.a' and +'include/mikmod.h' to your '%DJGPP%/lib' and '%DJGPP%/include' directories +respectively. + +HTML documentation of the library, for programmers, is located in the doc +directory. + + +>> SUPPORTED SOUNDCARDS +----------------------- + +Currently three brands of sound cards are supported under DOS: + +- Gravis Ultrasound and compatibles. Not tested with Interwave cards. If + somebody has one and can debug the code under Interwave, please drop us a note + (see email at bottom). + +- SoundBlaster and compatibles. There is only one driver that supports all + flavours of SoundBlasters: + + + SB original (8-bit mono, 22KHz) + + SB 2.0 (DMA autoinit mode -> less clicks, 44KHz 8-bit mono). + + SB Pro (8-bit 22KHz stereo, 8-bit 44KHz mono) + + SB 16+ (8- and 16-bit stereo, up to 44Khz both) + + Note that SB16 should cover AWE32/64 as well... + +- Windows Sound System and compatibles. Most today SoundBlaster clones + (including the CS4236B card used for testing) emulates (in hardware) WSS. + That's because Creative Labs copyrighted the SB16 interface and thus + clonemakers cannot do similar hardware without violating their rights, so + most of them only emulate SB Pro (see above). + + WSS supports up to 44KHz 16-bit stereo. The following playback rates (in both + 8- and 16-bit) are supported: 5510, 6620, 8000, 9600, 11025, 16000, 18900, + 22050, 27420, 32000, 33075, 37800, 44100 and 48000 Hz. + + +>> THANKS +--------- + +We would like to thank everyone who contributed to libmikmod. Their names +are in the AUTHORS file for the significative contributions, but some other +names can be found in the NEWS file. Thanks a lot ! Keeping libmikmod alive +wouldn't be much fun without you. + + +>> LICENSE +---------- + +The libmikmod sound library is covered by the GNU Library General Public +License as published by the Free Software Fundation (you'll find it in the +file COPYING.LIB) ; either version 2 of the licence, or (at your option) +any later version. + +The GNU Lesser General Public License, version 2.1, in file COPYING.LESSER, can +be considered as a later version of the LGPL, and is strongly recommended for +people who will embed libmikmod in their application as a shared library. + + +>> CONTACT AND DOWNLOAD INFO +---------------------------- + +libmikmod home page is located at SourceForge: + + http://mikmod.sourceforge.net/ + http://sourceforge.net/projects/mikmod/ + +There's a mailing list (mikmod-public) for discussing the development +of MikMod (new features, bugs, ideas...) Look for more information on +the web site. + +Things related to the DOS port should also be forwarded to the DOS +``portmaster'', Andrew Zabolotny, at: bit@eltech.ru + + +>> LAST NOTES +------------- + +We hope you'll enjoy using this version of libmikmod as well as we enjoyed +debugging and improving it. + +-- Miodrag ("Miod") Vallat, 10/19/1999 + miodrag@mikmod.darkorb.net + + Andrew Zabolotny + bit@eltech.ru diff --git a/libs/mikmod/dos/config.h b/libs/mikmod/dos/config.h new file mode 100644 index 0000000..de046d7 --- /dev/null +++ b/libs/mikmod/dos/config.h @@ -0,0 +1,56 @@ +/* config.h.in. Generated manually for DJGPP. */ + +/* djgpp-v2.04 and newer provide snprintf() and vsnprintf(). + * djgpp-v2.05 is already released, so let's enable them by + * default here. */ +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF + +/* Define to disable the high quality mixer (build only with the standart mixer) */ +/*#define NO_HQMIXER*/ + +/* Define if your system supports binary pipes (i.e. Unix) */ +/*#define DRV_PIPE*/ +/* Define if you want support for output to stdout */ +/*#define DRV_STDOUT*/ + +/* Define if you want an .aiff file writer driver */ +#undef DRV_AIFF +/* Define if you want a raw pcm data file writer driver */ +#undef DRV_RAW +/* Define if you want a .wav file writer driver */ +#undef DRV_WAV + +/* Define if the Gravis UltraSound driver is compiled */ +#define DRV_ULTRA +/* Define if the Windows Sound System driver is compiled */ +#define DRV_WSS +/* Define if the SoundBlaster driver is compiled */ +#define DRV_SB + +/* Define if you want a debug version of the library */ +#undef MIKMOD_DEBUG + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS + +/* Define if you have the setenv function. */ +#define HAVE_SETENV + +/* Define if you have the srandom function. */ +#define HAVE_SRANDOM + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H + +/* Define if you have the header file. */ +#define HAVE_MALLOC_H + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H + diff --git a/libs/mikmod/drivers/dos/dosdma.c b/libs/mikmod/drivers/dos/dosdma.c new file mode 100644 index 0000000..e232c30 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dosdma.h b/libs/mikmod/drivers/dos/dosdma.h new file mode 100644 index 0000000..882c9ac --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dosgus.c b/libs/mikmod/drivers/dos/dosgus.c new file mode 100644 index 0000000..637b526 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dosgus.h b/libs/mikmod/drivers/dos/dosgus.h new file mode 100644 index 0000000..ae3488a --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dosirq.c b/libs/mikmod/drivers/dos/dosirq.c new file mode 100644 index 0000000..9b0e21f --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dosirq.h b/libs/mikmod/drivers/dos/dosirq.h new file mode 100644 index 0000000..eaf60a1 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dossb.c b/libs/mikmod/drivers/dos/dossb.c new file mode 100644 index 0000000..344ac85 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/dossb.h b/libs/mikmod/drivers/dos/dossb.h new file mode 100644 index 0000000..cb8dc70 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/doswss.c b/libs/mikmod/drivers/dos/doswss.c new file mode 100644 index 0000000..41cdf38 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/doswss.h b/libs/mikmod/drivers/dos/doswss.h new file mode 100644 index 0000000..ae77bb5 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/drivers/dos/libgus.h b/libs/mikmod/drivers/dos/libgus.h new file mode 100644 index 0000000..0d66ee9 --- /dev/null +++ b/libs/mikmod/drivers/dos/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/mikmod/drvdos/dosdma.c b/libs/mikmod/drvdos/dosdma.c deleted file mode 100644 index e232c30..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dosdma.h b/libs/mikmod/drvdos/dosdma.h deleted file mode 100644 index 882c9ac..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dosgus.c b/libs/mikmod/drvdos/dosgus.c deleted file mode 100644 index 637b526..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dosgus.h b/libs/mikmod/drvdos/dosgus.h deleted file mode 100644 index ae3488a..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dosirq.c b/libs/mikmod/drvdos/dosirq.c deleted file mode 100644 index 9b0e21f..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dosirq.h b/libs/mikmod/drvdos/dosirq.h deleted file mode 100644 index eaf60a1..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dossb.c b/libs/mikmod/drvdos/dossb.c deleted file mode 100644 index 344ac85..0000000 --- a/libs/mikmod/drvdos/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/drvdos/dossb.h b/libs/mikmod/drvdos/dossb.h deleted file mode 100644 index cb8dc70..0000000 --- a/libs/mikmod/drvdos/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/drvdos/doswss.c b/libs/mikmod/drvdos/doswss.c deleted file mode 100644 index 41cdf38..0000000 --- a/libs/mikmod/drvdos/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/drvdos/doswss.h b/libs/mikmod/drvdos/doswss.h deleted file mode 100644 index ae77bb5..0000000 --- a/libs/mikmod/drvdos/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/drvdos/libgus.h b/libs/mikmod/drvdos/libgus.h deleted file mode 100644 index 0d66ee9..0000000 --- a/libs/mikmod/drvdos/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/src/dos/music.c b/src/dos/music.c deleted file mode 100644 index 5f3b84a..0000000 --- a/src/dos/music.c +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include "music.h" -#include "mikmod.h" - -static void update_callback(void); - -static UNIMOD *mod; -static int initialized; - -static int init(void) -{ - md_mixfreq = 44100; - md_dmabufsize = 20000; - md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_INTERP; - md_device = 0; - - ML_RegisterLoader(&load_m15); - ML_RegisterLoader(&load_mod); - ML_RegisterLoader(&load_mtm); - ML_RegisterLoader(&load_s3m); - ML_RegisterLoader(&load_stm); - ML_RegisterLoader(&load_ult); - ML_RegisterLoader(&load_uni); - ML_RegisterLoader(&load_xm); - - MD_RegisterDriver(&drv_nos); - MD_RegisterDriver(&drv_ss); - MD_RegisterDriver(&drv_sb); - MD_RegisterDriver(&drv_gus); - - MD_RegisterPlayer(update_callback); - - 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; - } - - MP_Init(mod); - md_numchn = mod->numchn; - printf("opened module %s (%d channels)\n", fname, md_numchn); - return 0; -} - -void music_close(void) -{ - if(mod) { - printf("shutting down music playback\n"); - 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) -{ - MP_HandleTick(); - MD_SetBPM(mp_bpm); -} diff --git a/src/music.c b/src/music.c new file mode 100644 index 0000000..8af6154 --- /dev/null +++ b/src/music.c @@ -0,0 +1,152 @@ +#include +#include "music.h" +#include "mikmod.h" + +#ifdef __WATCOMC__ +#define USE_OLDMIK +typedef UNIMOD MODULE; + +static void update_callback(void); +static void MikMod_RegisterAllDrivers(void); +static void MikMod_RegisterAllLoaders(void); +#else +#define USE_NEWMIK +#endif + +static MODULE *mod; +static int initialized; + +static int init(void) +{ + MikMod_RegisterAllDrivers(); + MikMod_RegisterAllLoaders(); + +#ifdef USE_NEWMIK + md_mode |= DMODE_SOFT_MUSIC | DMODE_16BITS | DMODE_STEREO | DMODE_INTERP; + if(MikMod_Init("") != 0) { + fprintf(stderr, "mikmod init failed: %s\n", + MikMod_strerror(MikMod_errno)); + return -1; + } + +#else + md_mixfreq = 44100; + md_dmabufsize = 20000; + md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_INTERP; + md_device = 0; + + MD_RegisterPlayer(update_callback); + + 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); +#endif + return 0; +} + +int music_open(const char *fname) +{ + if(!initialized) { + if(init() == -1) { + return -1; + } + initialized = 1; + } + +#ifdef USE_NEWMIK + if(!(mod = Player_Load(fname, 64, 0))) { + fprintf(stderr, "failed to load music: %s: %s\n", fname, + MikMod_strerror(MikMod_errno)); + return -1; + } +#else + if(!(mod = ML_LoadFN((const signed char*)fname))) { + fprintf(stderr, "failed to load music: %s: %s\n", fname, myerr); + return -1; + } + + MP_Init(mod); + md_numchn = mod->numchn; + printf("opened module %s (%d channels)\n", fname, md_numchn); +#endif + return 0; +} + +void music_close(void) +{ + if(mod) { + printf("shutting down music playback\n"); + music_stop(); +#ifdef USE_NEWMIK + Player_Free(mod); +#else + ML_Free(mod); +#endif + mod = 0; + } +} + +void music_play(void) +{ +#ifdef USE_NEWMIK + Player_Start(mod); +#else + MD_PlayStart(); +#endif +} + +void music_stop(void) +{ +#ifdef USE_NEWMIK + Player_Stop(); +#else + MD_PlayStop(); +#endif +} + +void music_update(void) +{ +#ifdef USE_NEWMIK + if(Player_Active()) { + MikMod_Update(); + } +#else + MD_Update(); +#endif +} + +#ifdef USE_OLDMIK +static void update_callback(void) +{ + MP_HandleTick(); + MD_SetBPM(mp_bpm); +} + +static void MikMod_RegisterAllDrivers(void) +{ + MD_RegisterDriver(&drv_nos); + MD_RegisterDriver(&drv_ss); + MD_RegisterDriver(&drv_sb); + MD_RegisterDriver(&drv_gus); +} + +static void MikMod_RegisterAllLoaders(void) +{ + ML_RegisterLoader(&load_m15); + ML_RegisterLoader(&load_mod); + ML_RegisterLoader(&load_mtm); + ML_RegisterLoader(&load_s3m); + ML_RegisterLoader(&load_stm); + ML_RegisterLoader(&load_ult); + ML_RegisterLoader(&load_uni); + ML_RegisterLoader(&load_xm); +} +#endif diff --git a/src/sdl/music.c b/src/sdl/music.c deleted file mode 100644 index 8e8f7cf..0000000 --- a/src/sdl/music.c +++ /dev/null @@ -1,63 +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 | DMODE_16BITS | DMODE_STEREO | DMODE_INTERP; - 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(); - } -}