X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=blobdiff_plain;f=libs%2Fmikmod%2Fdrivers%2Fdos%2Fdosgus.h;fp=libs%2Fmikmod%2Fdrivers%2Fdos%2Fdosgus.h;h=ae3488a0737e201a6b258dff28ec97cbe59fef2f;hp=0000000000000000000000000000000000000000;hb=cf94899f4f4d8535074db6421245b973d2fcde8c;hpb=8024ae981f39d370af5cceb3cb97f62820b0a120 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: */