📄 dosgus.c
字号:
/* 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: dosgus.c,v 1.1 2004/02/01 02:01:17 raph Exp $
Driver for GUS cards under DOS
==============================================================================*/
/*
Written by Andrew Zabolotny <bit@eltech.ru>
*/
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <dpmi.h>
#include <sys/farptr.h>
#include <sys/nearptr.h>
#include <go32.h>
#include <string.h>
#include "dosgus.h"
/********************************************* 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 :-)
*/
volatile 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -