📄 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)#endifstatic 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 + -