X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=blobdiff_plain;f=libs%2Foldmik%2Fsrc%2Fdrv_gus.c;fp=libs%2Foldmik%2Fsrc%2Fdrv_gus.c;h=7d65432d00c5f271bc2d960a9834f68569823d15;hp=0000000000000000000000000000000000000000;hb=77db1ca18d5446dcda9e524261399b63c2cd1813;hpb=a714b8c4811627d874934b0a0387b8cb27fc5921 diff --git a/libs/oldmik/src/drv_gus.c b/libs/oldmik/src/drv_gus.c new file mode 100644 index 0000000..7d65432 --- /dev/null +++ b/libs/oldmik/src/drv_gus.c @@ -0,0 +1,1985 @@ +/* + +Name: +DRV_GUS.C + +Description: +Mikmod driver for output on Gravis Ultrasound (native mode i.e. using +the onboard DRAM) + +Portability: + +MSDOS: BC(y) Watcom(y) DJGPP(y) +Win95: n +Os2: n +Linux: n + +(y) - yes +(n) - no (not possible or not useful) +(?) - may be possible, but not tested + +*/ +#include +#include +#include +#include +#include "mikmod.h" +#include "mirq.h" + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS defines <<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +/* Special macros for Least-most sig. bytes */ +#define MAKE_MSW(x) ((long)((long)(x)) << 16) +#define LSW(x) ((unsigned int)(x)) +#define MSW(x) ((unsigned int)(((long)x)>>16)) +#define MSB(x) (unsigned char)((unsigned int)(x)>>8) +#define LSB(x) ((unsigned char)(x)) + +/* Make GF1 address for direct chip i/o. */ +#define ADDR_HIGH(x) ((unsigned int)((unsigned int)((x>>7L)&0x1fffL))) +#define ADDR_LOW(x) ((unsigned int)((unsigned int)((x&0x7fL)<<9L))) + +#define JOYSTICK_TIMER (GUS_PORT+0x201) /* 201 */ +#define JOYSTICK_DATA (GUS_PORT+0x201) /* 201 */ + +#define GF1_MIDI_CTRL (GUS_PORT+0x100) /* 3X0 */ +#define GF1_MIDI_DATA (GUS_PORT+0x101) /* 3X1 */ + +#define GF1_PAGE (GUS_PORT+0x102) /* 3X2 */ +#define GF1_REG_SELECT (GUS_PORT+0x103) /* 3X3 */ +#define GF1_VOICE_SELECT (GUS_PORT+0x102) /* 3X3 */ +#define GF1_DATA_LOW (GUS_PORT+0x104) /* 3X4 */ +#define GF1_DATA_HI (GUS_PORT+0x105) /* 3X5 */ +#define GF1_IRQ_STAT (GUS_PORT+0x006) /* 2X6 */ +#define GF1_DRAM (GUS_PORT+0x107) /* 3X7 */ + +#define GF1_MIX_CTRL (GUS_PORT+0x000) /* 2X0 */ +#define GF1_TIMER_CTRL (GUS_PORT+0x008) /* 2X8 */ +#define GF1_TIMER_DATA (GUS_PORT+0x009) /* 2X9 */ +#define GF1_IRQ_CTRL (GUS_PORT+0x00B) /* 2XB */ + +/* The GF1 Hardware clock. */ +#define CLOCK_RATE 9878400L + +/* Mixer control bits. */ +#define ENABLE_LINE 0x01 +#define ENABLE_DAC 0x02 +#define ENABLE_MIC 0x04 + +/* interrupt controller 1 */ +#define CNTRL_8259 0x21 +#define OCR_8259 0x20 +#define EOI 0x20 +#define REARM3 0x2F3 +#define REARM5 0x2F5 + +/* interrupt controller 2 */ +#define CNTRL_M_8259 0x21 +#define CNTRL_M2_8259 0xA1 +#define OCR_2_8259 0xA0 + +#define DMA_CONTROL 0x41 +#define SET_DMA_ADDRESS 0x42 +#define SET_DRAM_LOW 0x43 +#define SET_DRAM_HIGH 0x44 + +#define TIMER_CONTROL 0x45 +#define TIMER1 0x46 +#define TIMER2 0x47 + +#define SET_SAMPLE_RATE 0x48 +#define SAMPLE_CONTROL 0x49 + +#define SET_JOYSTICK 0x4B +#define MASTER_RESET 0x4C + +/* Voice register mapping. */ +#define SET_CONTROL 0x00 +#define SET_FREQUENCY 0x01 +#define SET_START_HIGH 0x02 +#define SET_START_LOW 0x03 +#define SET_END_HIGH 0x04 +#define SET_END_LOW 0x05 +#define SET_VOLUME_RATE 0x06 +#define SET_VOLUME_START 0x07 +#define SET_VOLUME_END 0x08 +#define SET_VOLUME 0x09 +#define SET_ACC_HIGH 0x0a +#define SET_ACC_LOW 0x0b +#define SET_BALANCE 0x0c +#define SET_VOLUME_CONTROL 0x0d +#define SET_VOICES 0x0e + +#define GET_CONTROL 0x80 +#define GET_FREQUENCY 0x81 +#define GET_START_HIGH 0x82 +#define GET_START_LOW 0x83 +#define GET_END_HIGH 0x84 +#define GET_END_LOW 0x85 +#define GET_VOLUME_RATE 0x86 +#define GET_VOLUME_START 0x87 +#define GET_VOLUME_END 0x88 +#define GET_VOLUME 0x89 +#define GET_ACC_HIGH 0x8a +#define GET_ACC_LOW 0x8b +#define GET_BALANCE 0x8c +#define GET_VOLUME_CONTROL 0x8d +#define GET_VOICES 0x8e +#define GET_IRQV 0x8f + +/******************************************************************** + * + * MIDI defines + * + *******************************************************************/ + +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 + +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 + +/******************************************************************** + * + * JOYSTICK defines + * + *******************************************************************/ + +#define JOY_POSITION 0x0f +#define JOY_BUTTONS 0xf0 + +/******************************************************************** + * + * GF1 irq/dma programmable latches + * + *******************************************************************/ + +/* GF1_IRQ_STATUS (port 3X6) */ +#define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +#define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +#define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +#define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +#define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +#define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +#define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + + +/* GF1_MIX_CTRL (port 2X0) */ +#define ENABLE_LINE_IN 0x01 /* 0=enable */ +#define ENABLE_OUTPUT 0x02 /* 0=enable */ +#define ENABLE_MIC_IN 0x04 /* 1=enable */ +#define ENABLE_GF1_IRQ 0x08 /* 1=enable */ +#define GF122 0x10 /* ?? */ +#define ENABLE_MIDI_LOOP 0x20 /* 1=enable loop back */ +#define SELECT_GF1_REG 0x40 /* 0=irq latches */ + /* 1=dma latches */ + +/******************************************************************** + * + * GF1 global registers ($41-$4C) + * + *******************************************************************/ + +/* DMA control register */ +#define DMA_ENABLE 0x01 +#define DMA_READ 0x02 /* 1=read,0=write */ +#define DMA_WIDTH_16 0x04 /* 1=16 bit,0=8 bit (dma chan width)*/ +#define DMA_RATE 0x18 /* 00=fast, 11=slow */ +#define DMA_IRQ_ENABLE 0x20 /* 1=enable */ +#define DMA_IRQ_PENDING 0x40 /* read */ +#define DMA_DATA_16 0x40 /* write (data width) */ +#define DMA_TWOS_COMP 0x80 /* 1=do twos comp */ + +/* These are the xfer rate bits ... */ +#define DMA_R0 0x00 /* Fastest DMA xfer (~650khz) */ +#define DMA_R1 0x08 /* fastest / 2 */ +#define DMA_R2 0x10 /* fastest / 4 */ +#define DMA_R3 0x18 /* Slowest DMA xfer (fastest / 8) */ + +/* SAMPLE control register */ +#define ENABLE_ADC 0x01 +#define ADC_MODE 0x02 /* 0=mono, 1=stereo */ +#define ADC_DMA_WIDTH 0x04 /* 0=8 bit, 1=16 bit */ +#define ADC_IRQ_ENABLE 0x20 /* 1=enable */ +#define ADC_IRQ_PENDING 0x40 /* 1=irq pending */ +#define ADC_TWOS_COMP 0x80 /* 1=do twos comp */ + +/* RESET control register */ +#define GF1_MASTER_RESET 0x01 /* 0=hold in reset */ +#define GF1_OUTPUT_ENABLE 0x02 /* enable output */ +#define GF1_MASTER_IRQ 0x04 /* master IRQ enable */ + +/******************************************************************** + * + * GF1 voice specific registers ($00 - $0E and $80-$8f) + * + *******************************************************************/ + +/* ($0,$80) Voice control register */ +#define VOICE_STOPPED 0x01 /* voice has stopped */ +#define STOP_VOICE 0x02 /* stop voice */ +#define VC_DATA_TYPE 0x04 /* 0=8 bit,1=16 bit */ +#define VC_LOOP_ENABLE 0x08 /* 1=enable */ +#define VC_BI_LOOP 0x10 /* 1=bi directional looping */ +#define VC_WAVE_IRQ 0x20 /* 1=enable voice's wave irq */ +#define VC_DIRECT 0x40 /* 0=increasing,1=decreasing */ +#define VC_IRQ_PENDING 0x80 /* 1=wavetable irq pending */ + +/* ($1,$81) Frequency control */ +/* Bit 0 - Unused */ +/* Bits 1-9 - Fractional portion */ +/* Bits 10-15 - Integer portion */ + +/* ($2,$82) Accumulator start address (high) */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* ($3,$83) Accumulator start address (low) */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* ($4,$84) Accumulator end address (high) */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* ($5,$85) Accumulator end address (low) */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + + +/* ($6,$86) Volume Envelope control register */ +#define VL_RATE_MANTISSA 0x3f +#define VL_RATE_RANGE 0xC0 + +/* ($7,$87) Volume envelope start */ +#define VL_START_MANT 0x0F +#define VL_START_EXP 0xF0 + +/* ($8,$88) Volume envelope end */ +#define VL_END_MANT 0x0F +#define VL_END_EXP 0xF0 + +/* ($9,$89) Current volume register */ +/* Bits 0-3 are unused */ +/* Bits 4-11 - Mantissa of current volume */ +/* Bits 10-15 - Exponent of current volume */ + +/* ($A,$8A) Accumulator value (high) */ +/* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */ + +/* ($B,$8B) Accumulator value (low) */ +/* Bits 0-8 - Fractional portion */ +/* Bits 9-15 - Integer portion of low adress (a6-a0) */ + +/* ($C,$8C) Pan (balance) position */ +/* Bits 0-3 - Balance position 0=full left, 0x0f=full right */ + +/* ($D,$8D) Volume control register */ +#define VOLUME_STOPPED 0x01 /* volume has stopped */ +#define STOP_VOLUME 0x02 /* stop volume */ +#define VC_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */ +#define VL_LOOP_ENABLE 0x08 /* 1=enable */ +#define VL_BI_LOOP 0x10 /* 1=bi directional looping */ +#define VL_WAVE_IRQ 0x20 /* 1=enable voice's wave irq */ +#define VL_DIRECT 0x40 /* 0=increasing,1=decreasing */ +#define VL_IRQ_PENDING 0x80 /* 1=wavetable irq pending */ + +/* ($E,$8E) # of Active voices */ +/* Bits 0-5 - # of active voices -1 */ + +/* ($F,$8F) - Sources of IRQs */ +/* Bits 0-4 - interrupting voice number */ +/* Bit 5 - Always a 1 */ +#define VOICE_VOLUME_IRQ 0x40 /* individual voice irq bit */ +#define VOICE_WAVE_IRQ 0x80 /* individual waveform irq bit */ + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> Lowlevel GUS code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + +static UWORD GUS_PORT; +static UBYTE GUS_VOICES; +static UBYTE GUS_TIMER_CTRL; +static UBYTE GUS_TIMER_MASK; +static UBYTE GUS_MIX_IMAGE; + +static UWORD GUS_DRAM_DMA; +static UWORD GUS_ADC_DMA; +static UWORD GUS_GF1_IRQ; +static UWORD GUS_MIDI_IRQ; +static ULONG GUS_POOL; /* dram address of first gusmem pool node */ + +static UBYTE GUS_SELECT; /* currently selected GF1 register */ + +static void (*GUS_TIMER1_FUNC)(void); +static void (*GUS_TIMER2_FUNC)(void); + +#define UltraSelect(x) outportb(GF1_REG_SELECT,GUS_SELECT=x) + +#define USE_ROLLOVER 0 + +/*************************************************************** + * This function will convert the value read from the GF1 registers + * back to a 'real' address. + ***************************************************************/ + +#define MAKE_MS_SWORD( x ) ((unsigned long)((unsigned long)(x)) << 16) + +static ULONG make_physical_address(UWORD low,UWORD high,UBYTE mode) +{ + UWORD lower_16, upper_16; + ULONG ret_address, bit_19_20; + + upper_16 = high >> 9; + lower_16 = ((high & 0x01ff) << 7) | ((low >> 9) & 0x007f); + + ret_address = MAKE_MS_SWORD(upper_16) + lower_16; + + if (mode & VC_DATA_TYPE) + { + bit_19_20 = ret_address & 0xC0000; + ret_address <<= 1; + ret_address &= 0x3ffff; + ret_address |= bit_19_20; + } + + return( ret_address ); +} + +/*************************************************************** + * This function will translate the address if the dma channel + * is a 16 bit channel. This translation is not necessary for + * an 8 bit dma channel. + ***************************************************************/ + +static ULONG convert_to_16bit(ULONG address) +/* unsigned long address; /* 20 bit ultrasound dram address */ +{ + ULONG hold_address; + + hold_address = address; + + /* Convert to 16 translated address. */ + address = address >> 1; + + /* Zero out bit 17. */ + address &= 0x0001ffffL; + + /* Reset bits 18 and 19. */ + address |= (hold_address & 0x000c0000L); + + return(address); +} + + +static void GF1OutB(UBYTE x,UBYTE y) +{ + UltraSelect(x); + outportb(GF1_DATA_HI,y); +} + + +static void GF1OutW(UBYTE x,UWORD y) +{ + UltraSelect(x); + outport(GF1_DATA_LOW,y); +} + + +static UBYTE GF1InB(UBYTE x) +{ + UltraSelect(x); + return inportb(GF1_DATA_HI); +} + + +static UWORD GF1InW(UBYTE x) +{ + UltraSelect(x); + return inport(GF1_DATA_LOW); +} + + +static void gf1_delay(void) +{ + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); + inportb(GF1_DRAM); +} + + +static UBYTE UltraPeek(ULONG address) +{ + GF1OutW(SET_DRAM_LOW,address); + GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff); /* 8 bits */ + return(inportb(GF1_DRAM)); +} + + +static void UltraPoke(ULONG address,UBYTE data) +{ + GF1OutW(SET_DRAM_LOW,address); + GF1OutB(SET_DRAM_HIGH,(address>>16)&0xff); + outportb(GF1_DRAM,data); +} + + +static void UltraPokeFast(ULONG address,UBYTE *src,ULONG size) +/* + [address,size> doesn't cross 64k page boundary +*/ +{ + if(!size) return; + + UltraSelect(SET_DRAM_HIGH); + outportb(GF1_DATA_HI,(address>>16)&0xff); /* 8 bits */ + UltraSelect(SET_DRAM_LOW); + + while(size--){ + outport(GF1_DATA_LOW,address); + outportb(GF1_DRAM,*src); + address++; + src++; + } +} + + +static void UltraPokeChunk(ULONG address,UBYTE *src,ULONG size) +{ + ULONG todo; + + /* first 'todo' is number of bytes 'till first 64k boundary */ + + todo=0x10000-(address&0xffff); + if(todo>size) todo=size; + + do{ + UltraPokeFast(address,src,todo); + address+=todo; + src+=todo; + size-=todo; + + /* next 'todo' is in chunks of max 64k at once. */ + todo=(size>0xffff) ? 0x10000 : size; + + } while(todo); +} + + +static ULONG UltraPeekLong(ULONG address) +{ + ULONG data; + char *s=(char *)&data; + s[0]=UltraPeek(address); + s[1]=UltraPeek(address+1); + s[2]=UltraPeek(address+2); + s[3]=UltraPeek(address+3); + return data; +} + + +static void UltraPokeLong(ULONG address,ULONG data) +{ + UltraPokeChunk(address,(UBYTE *)&data,4); +} + + +static void UltraEnableOutput(void) +{ + GUS_MIX_IMAGE &= ~ENABLE_OUTPUT; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraDisableOutput(void) +{ + GUS_MIX_IMAGE |= ENABLE_OUTPUT; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraEnableLineIn(void) +{ + GUS_MIX_IMAGE &= ~ENABLE_LINE_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraDisableLineIn(void) +{ + GUS_MIX_IMAGE |= ENABLE_LINE_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + +static void UltraEnableMicIn(void) +{ + GUS_MIX_IMAGE |= ENABLE_MIC_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + + +static void UltraDisableMicIn(void) +{ + GUS_MIX_IMAGE &= ~ENABLE_MIC_IN; + outportb(GF1_MIX_CTRL,GUS_MIX_IMAGE); +} + + +static void UltraReset(int voices) +{ + int v; + + if(voices<14) voices=14; + if(voices>32) voices=32; + + GUS_VOICES=voices; + GUS_TIMER_CTRL=0; + GUS_TIMER_MASK=0; + + UltraPokeLong(0,0); + + GF1OutB(MASTER_RESET,0x00); + for(v=0;v<10;v++) gf1_delay(); + + /* Release Reset and wait */ + GF1OutB(MASTER_RESET,GF1_MASTER_RESET); + for (v=0;v<10;v++) gf1_delay(); + + /* Reset the MIDI port also */ + outportb(GF1_MIDI_CTRL,MIDI_RESET); + for (v=0;v<10;v++) gf1_delay(); + outportb(GF1_MIDI_CTRL,0x00); + + /* Clear all interrupts. */ + GF1OutB(DMA_CONTROL,0x00); + GF1OutB(TIMER_CONTROL,0x00); + GF1OutB(SAMPLE_CONTROL,0x00); + + /* Set the number of active voices */ + GF1OutB(SET_VOICES,((voices-1) | 0xC0)); + + /* Clear interrupts on voices. */ + /* Reading the status ports will clear the irqs. */ + + inportb(GF1_IRQ_STAT); + + GF1InB(DMA_CONTROL); + GF1InB(SAMPLE_CONTROL); + GF1InB(GET_IRQV); + + for(v=0;v=reqsize){ + + /* it fits, so we're allocating the first + 'size' bytes of this node */ + + /* find new node position and size */ + + newnode=curnode+reqsize; + newsize=cursize-reqsize; + + /* create a new freenode if needed: */ + + if(newsize>=8){ + UltraPokeLong(newnode,newsize); + UltraPokeLong(newnode+4,succ); + succ=newnode; + } + + /* link prednode & succnode */ + + if(pred) + UltraPokeLong(pred+4,succ); + else + GUS_POOL=succ; + + /* store size of allocated memory block in block itself: */ + + UltraPokeLong(curnode,reqsize); + return curnode; + } + + /* doesn't fit, try next node */ + curnode=succ; + } + return 0; +} + + + +static ULONG UltraMalloc16(ULONG reqsize) +/* + Allocates a free block of gus memory, suited for 16 bit samples i.e. + smaller than 256k and doesn't cross a 256k page. +*/ +{ + ULONG p,spage,epage; + + if(reqsize>262144) return 0; + + /* round size to 32 bytes */ + + reqsize+=31; + reqsize&=-32L; + + p=UltraMalloc(reqsize); + spage=p>>18; + epage=(p+reqsize-1)>>18; + + if(p && spage!=epage){ + ULONG newp,esize; + + /* free the second part of the block, and try again */ + + esize=(p+reqsize)-(epage<<18); + UltraFree(esize,epage<<18); + + newp=UltraMalloc16(reqsize); + + /* free first part of the previous block */ + + UltraFree(reqsize-esize,p); + p=newp; + } + + return p; +} + + + +static void UltraMemInit(void) +{ + UWORD memsize; + GUS_POOL=32; + memsize=UltraSizeDram(); + UltraPokeLong(GUS_POOL,((ULONG)memsize<<10)-32); + UltraPokeLong(GUS_POOL+4,0); +} + + +static void UltraNumVoices(int voices) +{ + UltraDisableLineIn(); + UltraDisableMicIn(); + UltraDisableOutput(); + UltraReset(voices); + UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ); +} + + +static void interrupt gf1handler(MIRQARGS) +{ + UBYTE irq_source; + UBYTE oldselect=GUS_SELECT; + + while(irq_source=inportb(GF1_IRQ_STAT)){ + +/* if(irq_source & DMA_TC_IRQ){ + no provisions for DMA-ready irq yet + } + + if(irq_source & (MIDI_TX_IRQ|MIDI_RX_IRQ)){ + no provisions for MIDI-ready irq yet + } +*/ + if (irq_source & GF1_TIMER1_IRQ){ + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL & ~0x04); + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); + if(GUS_TIMER1_FUNC!=NULL) GUS_TIMER1_FUNC(); + } + + if (irq_source & GF1_TIMER2_IRQ){ + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL & ~0x08); + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); + if(GUS_TIMER2_FUNC!=NULL) GUS_TIMER2_FUNC(); + } + +/* if (irq_source & (WAVETABLE_IRQ | ENVELOPE_IRQ)){ + no wavetable or envelope irq provisions yet + } +*/ + } + + MIrq_EOI(GUS_GF1_IRQ); + UltraSelect(oldselect); +} + + +static PVI oldhandler; +typedef void (*PFV)(void); + + +static PFV UltraTimer1Handler(PFV handler) +{ + PFV old=GUS_TIMER1_FUNC; + GUS_TIMER1_FUNC=handler; + return old; +} + + +static PFV UltraTimer2Handler(PFV handler) +{ + PFV old=GUS_TIMER1_FUNC; + GUS_TIMER1_FUNC=handler; + return old; +} + + +static void UltraOpen(int voices) +{ + GUS_MIX_IMAGE=0x0b; + GUS_TIMER1_FUNC=NULL; + GUS_TIMER2_FUNC=NULL; + + UltraDisableLineIn(); + UltraDisableMicIn(); + UltraDisableOutput(); + + UltraReset(voices); + UltraSetInterface(GUS_DRAM_DMA,GUS_ADC_DMA,GUS_GF1_IRQ,GUS_MIDI_IRQ); + UltraMemInit(); + oldhandler=MIrq_SetHandler(GUS_GF1_IRQ,gf1handler); + MIrq_OnOff(GUS_GF1_IRQ,1); +} + + +static void UltraClose(void) +{ + MIrq_OnOff(GUS_GF1_IRQ,0); + MIrq_SetHandler(GUS_GF1_IRQ,oldhandler); + UltraDisableOutput(); + UltraDisableLineIn(); + UltraDisableMicIn(); + UltraReset(14); +} + + +static void UltraSelectVoice(UBYTE voice) +{ + /* Make sure was are talking to proper voice */ + outportb(GF1_VOICE_SELECT,voice); +} + + + +static void UltraSetVoiceEnd(ULONG end) +{ + ULONG phys_end; + UBYTE data; + + data=GF1InB(GET_CONTROL); + + phys_end=(data&VC_DATA_TYPE)?convert_to_16bit(end):end; + + /* Set end address of buffer */ + GF1OutW(SET_END_LOW,ADDR_LOW(phys_end)); + GF1OutW(SET_END_HIGH,ADDR_HIGH(phys_end)); + + data&=~(VC_IRQ_PENDING|VOICE_STOPPED|STOP_VOICE); + + GF1OutB(SET_CONTROL,data); + gf1_delay(); + + GF1OutB(SET_CONTROL,data); +} + + +/* The formula for this table is: + 1,000,000 / (1.619695497 * # of active voices) + + The 1.619695497 is calculated by knowing that 14 voices + gives exactly 44.1 Khz. Therefore, + 1,000,000 / (X * 14) = 44100 + X = 1.619695497 +*/ + +static UWORD freq_divisor[19] = { + 44100, /* 14 active voices */ + 41160, /* 15 active voices */ + 38587, /* 16 active voices */ + 36317, /* 17 active voices */ + 34300, /* 18 active voices */ + 32494, /* 19 active voices */ + 30870, /* 20 active voices */ + 29400, /* 21 active voices */ + 28063, /* 22 active voices */ + 26843, /* 23 active voices */ + 25725, /* 24 active voices */ + 24696, /* 25 active voices */ + 23746, /* 26 active voices */ + 22866, /* 27 active voices */ + 22050, /* 28 active voices */ + 21289, /* 29 active voices */ + 20580, /* 30 active voices */ + 19916, /* 31 active voices */ + 19293} /* 32 active voices */ +; + +static void UltraSetFrequency(ULONG speed_khz) +{ + UWORD fc; + ULONG temp; + + /* FC is calculated based on the # of active voices ... */ + temp=freq_divisor[GUS_VOICES-14]; + + fc=(((speed_khz<<9L)+(temp>>1L))/temp); + GF1OutW(SET_FREQUENCY,fc<<1); +} + + +static void UltraSetLoopMode(UBYTE mode) +{ + UBYTE data; + UBYTE vmode; + + /* set/reset the rollover bit as per user request */ + + vmode=GF1InB(GET_VOLUME_CONTROL); + + if(mode&USE_ROLLOVER) + vmode|=VC_ROLLOVER; + else + vmode&=~VC_ROLLOVER; + + GF1OutB(SET_VOLUME_CONTROL,vmode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,vmode); + + data=GF1InB(GET_CONTROL); + + data&=~(VC_WAVE_IRQ|VC_BI_LOOP|VC_LOOP_ENABLE); /* isolate the bits */ + mode&=VC_WAVE_IRQ|VC_BI_LOOP|VC_LOOP_ENABLE; /* no bad bits passed in */ + data|=mode; /* turn on proper bits ... */ + + GF1OutB(SET_CONTROL,data); + gf1_delay(); + GF1OutB(SET_CONTROL,data); +} + + +static ULONG UltraReadVoice(void) +{ + UWORD count_low,count_high; + ULONG acc; + UBYTE mode; + + /* Get the high & low portion of the accumulator */ + count_high = GF1InW(GET_ACC_HIGH); + count_low = GF1InW(GET_ACC_LOW); + + /* convert from UltraSound's format to a physical address */ + + mode=GF1InB(GET_CONTROL); + + acc=make_physical_address(count_low,count_high,mode); + acc&=0xfffffL; /* Only 20 bits please ... */ + + return(acc); +} + + + +static void UltraSetVoice(ULONG location) +{ + ULONG phys_loc; + UBYTE data; + + data=GF1InB(GET_CONTROL); + + phys_loc=(data&VC_DATA_TYPE)?convert_to_16bit(location):location; + + /* First set accumulator to beginning of data */ + GF1OutW(SET_ACC_HIGH,ADDR_HIGH(phys_loc)); + GF1OutW(SET_ACC_LOW,ADDR_LOW(phys_loc)); +} + + + +static UBYTE UltraPrimeVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode) +{ + ULONG phys_start,phys_end; + ULONG phys_begin; + ULONG temp; + UBYTE vmode; + + /* if start is greater than end, flip 'em and turn on */ + /* decrementing addresses */ + if(start>end){ + temp=start; + start=end; + end=temp; + mode|=VC_DIRECT; + } + + /* if 16 bit data, must convert addresses */ + if(mode&VC_DATA_TYPE){ + phys_begin = convert_to_16bit(begin); + phys_start = convert_to_16bit(start); + phys_end = convert_to_16bit(end); + } + else{ + phys_begin = begin; + phys_start = start; + phys_end = end; + } + + /* set/reset the rollover bit as per user request */ + vmode=GF1InB(GET_VOLUME_CONTROL); + + if(mode&USE_ROLLOVER) + vmode |= VC_ROLLOVER; + else + vmode &= ~VC_ROLLOVER; + + GF1OutB(SET_VOLUME_CONTROL,vmode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,vmode); + + /* First set accumulator to beginning of data */ + GF1OutW(SET_ACC_LOW,ADDR_LOW(phys_begin)); + GF1OutW(SET_ACC_HIGH,ADDR_HIGH(phys_begin)); + + /* Set start loop address of buffer */ + GF1OutW(SET_START_HIGH,ADDR_HIGH(phys_start)); + GF1OutW(SET_START_LOW,ADDR_LOW(phys_start)); + + /* Set end address of buffer */ + GF1OutW(SET_END_HIGH,ADDR_HIGH(phys_end)); + GF1OutW(SET_END_LOW,ADDR_LOW(phys_end)); + return(mode); +} + + +static void UltraGoVoice(UBYTE mode) +{ + mode&=~(VOICE_STOPPED|STOP_VOICE); /* turn 'stop' bits off ... */ + + /* NOTE: no irq's from the voice ... */ + + GF1OutB(SET_CONTROL,mode); + gf1_delay(); + GF1OutB(SET_CONTROL,mode); +} + + +/********************************************************************** + * + * This function will start playing a wave out of DRAM. It assumes + * the playback rate, volume & balance have been set up before ... + * + *********************************************************************/ + +static void UltraStartVoice(ULONG begin,ULONG start,ULONG end,UBYTE mode) +{ + mode=UltraPrimeVoice(begin,start,end,mode); + UltraGoVoice(mode); +} + + + +/*************************************************************** + * This function will stop a given voices output. Note that a delay + * is necessary after the stop is issued to ensure the self- + * modifying bits aren't a problem. + ***************************************************************/ + +static void UltraStopVoice(void) +{ + UBYTE data; + + /* turn off the roll over bit first ... */ + + data=GF1InB(GET_VOLUME_CONTROL); + data&=~VC_ROLLOVER; + + GF1OutB(SET_VOLUME_CONTROL,data); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,data); + + /* Now stop the voice */ + + data=GF1InB(GET_CONTROL); + data&=~VC_WAVE_IRQ; /* disable irq's & stop voice .. */ + data|=VOICE_STOPPED|STOP_VOICE; + + GF1OutB(SET_CONTROL,data); /* turn it off */ + gf1_delay(); + GF1OutB(SET_CONTROL,data); +} + + +static int UltraVoiceStopped(void) +{ + return(GF1InB(GET_CONTROL) & (VOICE_STOPPED|STOP_VOICE)); +} + + +static void UltraSetBalance(UBYTE pan) +{ + GF1OutB(SET_BALANCE,pan&0xf); +} + + +static void UltraSetVolume(UWORD volume) +{ + GF1OutW(SET_VOLUME,volume<<4); +} + + +static UWORD UltraReadVolume(void) +{ + return(GF1InW(GET_VOLUME)>>4); +} + + +static void UltraStopVolume(void) +{ + UBYTE vmode; + + vmode=GF1InB(GET_VOLUME_CONTROL); + vmode|=(VOLUME_STOPPED|STOP_VOLUME); + + GF1OutB(SET_VOLUME_CONTROL,vmode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,vmode); +} + + +static void UltraRampVolume(UWORD start,UWORD end,UBYTE rate,UBYTE mode) +{ + UWORD begin; + UBYTE vmode; + + if(start==end) return; +/********************************************************************* + * If the start volume is greater than the end volume, flip them and + * turn on decreasing volume. Note that the GF1 requires that the + * programmed start volume MUST be less than or equal to the end + * volume. + *********************************************************************/ + /* Don't let bad bits thru ... */ + mode&=~(VL_IRQ_PENDING|VC_ROLLOVER|STOP_VOLUME|VOLUME_STOPPED); + + begin = start; + + if(start>end){ + /* flip start & end if decreasing numbers ... */ + start = end; + end = begin; + mode |= VC_DIRECT; /* decreasing volumes */ + } + + /* looping below 64 or greater that 4032 can cause strange things */ + if(start<64) start=64; + if(end>4032) end=4032; + + GF1OutB(SET_VOLUME_RATE,rate); + GF1OutB(SET_VOLUME_START,start>>4); + GF1OutB(SET_VOLUME_END,end>>4); + + /* Also MUST set the current volume to the start volume ... */ + UltraSetVolume(begin); + + vmode=GF1InB(GET_VOLUME_CONTROL); + + if(vmode&VC_ROLLOVER) mode|=VC_ROLLOVER; + + /* start 'er up !!! */ + GF1OutB(SET_VOLUME_CONTROL,mode); + gf1_delay(); + GF1OutB(SET_VOLUME_CONTROL,mode); +} + + +static void UltraVectorVolume(UWORD end,UBYTE rate,UBYTE mode) +{ + UltraStopVolume(); + UltraRampVolume(UltraReadVolume(),end,rate,mode); +} + + +static int UltraVolumeStopped(void) +{ + return(GF1InB(GET_VOLUME_CONTROL) & (VOLUME_STOPPED|STOP_VOLUME)); +} + + +static UWORD vol_rates[19]={ +23,24,26,28,29,31,32,34,36,37,39,40,42,44,45,47,49,50,52 +}; + + +static UBYTE UltraCalcRate(UWORD start,UWORD end,ULONG mil_secs) +{ + ULONG gap,mic_secs; + UWORD i,range,increment; + UBYTE rate_val; + UWORD value; + + gap = (start>end) ? (start-end) : (end-start); + mic_secs = (mil_secs * 1000L)/gap; + +/* OK. We now have the # of microseconds for each update to go from */ +/* A to B in X milliseconds. See what the best fit is in the table */ + + range = 4; + value = vol_rates[GUS_VOICES-14]; + + for(i=0;i<3;i++){ + if(mic_secs>1)) / mic_secs); + } + + rate_val=range<<6; + rate_val|=(increment&0x3F); + return(rate_val); +} + + +static UWORD _gf1_volumes[512] = { + 0x0000, + 0x0700, 0x07ff, 0x0880, 0x08ff, 0x0940, 0x0980, 0x09c0, 0x09ff, 0x0a20, + 0x0a40, 0x0a60, 0x0a80, 0x0aa0, 0x0ac0, 0x0ae0, 0x0aff, 0x0b10, 0x0b20, + 0x0b30, 0x0b40, 0x0b50, 0x0b60, 0x0b70, 0x0b80, 0x0b90, 0x0ba0, 0x0bb0, + 0x0bc0, 0x0bd0, 0x0be0, 0x0bf0, 0x0bff, 0x0c08, 0x0c10, 0x0c18, 0x0c20, + 0x0c28, 0x0c30, 0x0c38, 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68, + 0x0c70, 0x0c78, 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0, + 0x0cb8, 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8, + 0x0cff, 0x0d04, 0x0d08, 0x0d0c, 0x0d10, 0x0d14, 0x0d18, 0x0d1c, 0x0d20, + 0x0d24, 0x0d28, 0x0d2c, 0x0d30, 0x0d34, 0x0d38, 0x0d3c, 0x0d40, 0x0d44, + 0x0d48, 0x0d4c, 0x0d50, 0x0d54, 0x0d58, 0x0d5c, 0x0d60, 0x0d64, 0x0d68, + 0x0d6c, 0x0d70, 0x0d74, 0x0d78, 0x0d7c, 0x0d80, 0x0d84, 0x0d88, 0x0d8c, + 0x0d90, 0x0d94, 0x0d98, 0x0d9c, 0x0da0, 0x0da4, 0x0da8, 0x0dac, 0x0db0, + 0x0db4, 0x0db8, 0x0dbc, 0x0dc0, 0x0dc4, 0x0dc8, 0x0dcc, 0x0dd0, 0x0dd4, + 0x0dd8, 0x0ddc, 0x0de0, 0x0de4, 0x0de8, 0x0dec, 0x0df0, 0x0df4, 0x0df8, + 0x0dfc, 0x0dff, 0x0e02, 0x0e04, 0x0e06, 0x0e08, 0x0e0a, 0x0e0c, 0x0e0e, + 0x0e10, 0x0e12, 0x0e14, 0x0e16, 0x0e18, 0x0e1a, 0x0e1c, 0x0e1e, 0x0e20, + 0x0e22, 0x0e24, 0x0e26, 0x0e28, 0x0e2a, 0x0e2c, 0x0e2e, 0x0e30, 0x0e32, + 0x0e34, 0x0e36, 0x0e38, 0x0e3a, 0x0e3c, 0x0e3e, 0x0e40, 0x0e42, 0x0e44, + 0x0e46, 0x0e48, 0x0e4a, 0x0e4c, 0x0e4e, 0x0e50, 0x0e52, 0x0e54, 0x0e56, + 0x0e58, 0x0e5a, 0x0e5c, 0x0e5e, 0x0e60, 0x0e62, 0x0e64, 0x0e66, 0x0e68, + 0x0e6a, 0x0e6c, 0x0e6e, 0x0e70, 0x0e72, 0x0e74, 0x0e76, 0x0e78, 0x0e7a, + 0x0e7c, 0x0e7e, 0x0e80, 0x0e82, 0x0e84, 0x0e86, 0x0e88, 0x0e8a, 0x0e8c, + 0x0e8e, 0x0e90, 0x0e92, 0x0e94, 0x0e96, 0x0e98, 0x0e9a, 0x0e9c, 0x0e9e, + 0x0ea0, 0x0ea2, 0x0ea4, 0x0ea6, 0x0ea8, 0x0eaa, 0x0eac, 0x0eae, 0x0eb0, + 0x0eb2, 0x0eb4, 0x0eb6, 0x0eb8, 0x0eba, 0x0ebc, 0x0ebe, 0x0ec0, 0x0ec2, + 0x0ec4, 0x0ec6, 0x0ec8, 0x0eca, 0x0ecc, 0x0ece, 0x0ed0, 0x0ed2, 0x0ed4, + 0x0ed6, 0x0ed8, 0x0eda, 0x0edc, 0x0ede, 0x0ee0, 0x0ee2, 0x0ee4, 0x0ee6, + 0x0ee8, 0x0eea, 0x0eec, 0x0eee, 0x0ef0, 0x0ef2, 0x0ef4, 0x0ef6, 0x0ef8, + 0x0efa, 0x0efc, 0x0efe, 0x0eff, 0x0f01, 0x0f02, 0x0f03, 0x0f04, 0x0f05, + 0x0f06, 0x0f07, 0x0f08, 0x0f09, 0x0f0a, 0x0f0b, 0x0f0c, 0x0f0d, 0x0f0e, + 0x0f0f, 0x0f10, 0x0f11, 0x0f12, 0x0f13, 0x0f14, 0x0f15, 0x0f16, 0x0f17, + 0x0f18, 0x0f19, 0x0f1a, 0x0f1b, 0x0f1c, 0x0f1d, 0x0f1e, 0x0f1f, 0x0f20, + 0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29, + 0x0f2a, 0x0f2b, 0x0f2c, 0x0f2d, 0x0f2e, 0x0f2f, 0x0f30, 0x0f31, 0x0f32, + 0x0f33, 0x0f34, 0x0f35, 0x0f36, 0x0f37, 0x0f38, 0x0f39, 0x0f3a, 0x0f3b, + 0x0f3c, 0x0f3d, 0x0f3e, 0x0f3f, 0x0f40, 0x0f41, 0x0f42, 0x0f43, 0x0f44, + 0x0f45, 0x0f46, 0x0f47, 0x0f48, 0x0f49, 0x0f4a, 0x0f4b, 0x0f4c, 0x0f4d, + 0x0f4e, 0x0f4f, 0x0f50, 0x0f51, 0x0f52, 0x0f53, 0x0f54, 0x0f55, 0x0f56, + 0x0f57, 0x0f58, 0x0f59, 0x0f5a, 0x0f5b, 0x0f5c, 0x0f5d, 0x0f5e, 0x0f5f, + 0x0f60, 0x0f61, 0x0f62, 0x0f63, 0x0f64, 0x0f65, 0x0f66, 0x0f67, 0x0f68, + 0x0f69, 0x0f6a, 0x0f6b, 0x0f6c, 0x0f6d, 0x0f6e, 0x0f6f, 0x0f70, 0x0f71, + 0x0f72, 0x0f73, 0x0f74, 0x0f75, 0x0f76, 0x0f77, 0x0f78, 0x0f79, 0x0f7a, + 0x0f7b, 0x0f7c, 0x0f7d, 0x0f7e, 0x0f7f, 0x0f80, 0x0f81, 0x0f82, 0x0f83, + 0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c, + 0x0f8d, 0x0f8e, 0x0f8f, 0x0f90, 0x0f91, 0x0f92, 0x0f93, 0x0f94, 0x0f95, + 0x0f96, 0x0f97, 0x0f98, 0x0f99, 0x0f9a, 0x0f9b, 0x0f9c, 0x0f9d, 0x0f9e, + 0x0f9f, 0x0fa0, 0x0fa1, 0x0fa2, 0x0fa3, 0x0fa4, 0x0fa5, 0x0fa6, 0x0fa7, + 0x0fa8, 0x0fa9, 0x0faa, 0x0fab, 0x0fac, 0x0fad, 0x0fae, 0x0faf, 0x0fb0, + 0x0fb1, 0x0fb2, 0x0fb3, 0x0fb4, 0x0fb5, 0x0fb6, 0x0fb7, 0x0fb8, 0x0fb9, + 0x0fba, 0x0fbb, 0x0fbc, 0x0fbd, 0x0fbe, 0x0fbf, 0x0fc0, 0x0fc1, 0x0fc2, + 0x0fc3, 0x0fc4, 0x0fc5, 0x0fc6, 0x0fc7, 0x0fc8, 0x0fc9, 0x0fca, 0x0fcb, + 0x0fcc, 0x0fcd, 0x0fce, 0x0fcf, 0x0fd0, 0x0fd1, 0x0fd2, 0x0fd3, 0x0fd4, + 0x0fd5, 0x0fd6, 0x0fd7, 0x0fd8, 0x0fd9, 0x0fda, 0x0fdb, 0x0fdc, 0x0fdd, + 0x0fde, 0x0fdf, 0x0fe0, 0x0fe1, 0x0fe2, 0x0fe3, 0x0fe4, 0x0fe5, 0x0fe6, + 0x0fe7, 0x0fe8, 0x0fe9, 0x0fea, 0x0feb, 0x0fec, 0x0fed, 0x0fee, 0x0fef, + 0x0ff0, 0x0ff1, 0x0ff2, 0x0ff3, 0x0ff4, 0x0ff5, 0x0ff6, 0x0ff7, 0x0ff8, + 0x0ff9, 0x0ffa, 0x0ffb, 0x0ffc, 0x0ffd, 0x0ffe, 0x0fff +}; + + +static void UltraSetLinearVolume(UWORD index) +{ + UltraSetVolume(_gf1_volumes[index]); +} + + +static void UltraRampLinearVolume(UWORD start_idx,UWORD end_idx,ULONG msecs,UBYTE mode) +{ + UWORD start,end; + UBYTE rate; + + /* Ramp from start to end in x milliseconds */ + + start = _gf1_volumes[start_idx]; + end = _gf1_volumes[end_idx]; + + /* calculate a rate to get from start to end in msec milliseconds .. */ + rate=UltraCalcRate(start,end,msecs); + UltraRampVolume(start,end,rate,mode); +} + + +static void UltraVectorLinearVolume(UWORD end_idx,UBYTE rate,UBYTE mode) +{ + UltraStopVolume(); + UltraRampVolume(UltraReadVolume(),_gf1_volumes[end_idx],rate,mode); +} + + +static void UltraStartTimer(UBYTE timer,UBYTE time) +{ + UBYTE temp; + + if (timer == 1){ + GUS_TIMER_CTRL |= 0x04; + GUS_TIMER_MASK |= 0x01; + temp = TIMER1; + } + else{ + GUS_TIMER_CTRL |= 0x08; + GUS_TIMER_MASK |= 0x02; + temp = TIMER2; + } + +/* ENTER_CRITICAL; */ + + time = 256 - time; + + GF1OutB(temp,time); /* set timer speed */ + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); /* enable timer interrupt on gf1 */ + outportb(GF1_TIMER_CTRL,0x04); /* select timer stuff */ + outportb(GF1_TIMER_DATA,GUS_TIMER_MASK); /* start the timers */ + +/* LEAVE_CRITICAL; */ +} + + +static void UltraStopTimer(int timer) +{ + if (timer == 1){ + GUS_TIMER_CTRL &= ~0x04; + GUS_TIMER_MASK &= ~0x01; + } + else{ + GUS_TIMER_CTRL &= ~0x08; + GUS_TIMER_MASK &= ~0x02; + } + +/* ENTER_CRITICAL; */ + + GF1OutB(TIMER_CONTROL,GUS_TIMER_CTRL); /* disable timer interrupts */ + outportb(GF1_TIMER_CTRL,0x04); /* select timer stuff */ + outportb(GF1_TIMER_DATA,GUS_TIMER_MASK | 0x80); + +/* LEAVE_CRITICAL; */ +} + + +static BOOL UltraTimerStopped(UBYTE timer) +{ + UBYTE value; + UBYTE temp; + + if (timer == 1) + temp = 0x40; + else + temp = 0x20; + + value = (inportb(GF1_TIMER_CTRL)) & temp; + + return(value); +} + + +/*************************************************************************** +>>>>>>>>>>>>>>>>>>>>>>>>> The actual GUS driver <<<<<<<<<<<<<<<<<<<<<<<<<<<< +***************************************************************************/ + + +static ULONG Ultra[MAXSAMPLEHANDLES]; +static ULONG Ultrs[MAXSAMPLEHANDLES]; + +/* Ultra[] holds the sample dram adresses + of the samples of a module */ + +typedef struct{ + UBYTE kick; /* =1 -> sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + UBYTE vol; /* current volume */ + UBYTE pan; /* current panning position */ +} GHOLD; + +static GHOLD ghld[32]; + + +static UBYTE timeskip; +static UBYTE timecount; +static UBYTE GUS_BPM; + + +void UltraSetBPM(UBYTE bpm) +{ + /* The player routine has to be called (bpm*50)/125 times a second, + so the interval between calls takes 125/(bpm*50) seconds (amazing!). + + The Timer1 handler has a resolution of 80 microseconds. + + So the timer value to program: + + (125/(bpm*50)) / 80e-6 = 31250/bpm + */ + UWORD rate=31250/bpm; + + timeskip=0; + timecount=0; + + while(rate>255){ + rate>>=1; + timeskip++; + } + UltraStartTimer(1,rate); +} + + + +void GUS_Update(void) +{ + UBYTE t; + GHOLD *aud; + UWORD vol; + ULONG base,start,size,reppos,repend; + + UWORD curvol,bigvol=0,bigvoc=0; + + if(timecountkick){ + curvol=UltraReadVolume(); + if(bigvol<=curvol){ + bigvol=curvol; + bigvoc=t; + } + UltraVectorLinearVolume(0,0x3f,0); + } + } + +/* while(!UltraVolumeStopped(bigvoc)); */ + + for(t=0;tkick){ + aud->kick=0; + + base=Ultra[aud->handle]; + + start=aud->start; + reppos=aud->reppos; + repend=aud->repend; + size=aud->size; + + if(aud->flags&SF_16BITS){ + start<<=1; + reppos<<=1; + repend<<=1; + size<<=1; + } + + /* Stop current sample and start a new one */ + + UltraStopVoice(); + + UltraSetFrequency(aud->frq); + UltraVectorLinearVolume(6U*aud->vol,0x3f,0); + UltraSetBalance(aud->pan>>4); + + if(aud->flags&SF_LOOP){ + + /* Start a looping sample */ + + UltraStartVoice(base+start, + base+reppos, + base+repend,0x8|((aud->flags&SF_16BITS)?4:0)| + ((aud->flags&SF_BIDI)?16:0)); + } + else{ + + /* Start a one-shot sample */ + + UltraStartVoice(base+start, + base+start, + base+size+2,(aud->flags&SF_16BITS)?4:0); + } + } + else{ + UltraSetFrequency(aud->frq); + UltraVectorLinearVolume(6U*aud->vol,0x3f,0); + UltraSetBalance(aud->pan>>4); + } + } +} + + +SWORD GUS_Load(FILE *fp,ULONG length,ULONG loopstart,ULONG loopend,UWORD flags) +/* + callback routine for the MODLOAD module. +*/ +{ + int handle,t; + long p,l; + + SL_Init(fp,flags,flags|SF_SIGNED); + + /* Find empty slot to put sample address in */ + + for(handle=0;handle0){ + static UBYTE buffer[1024]; + long todo; + + todo=(l>1024) ? 1024 : l; + + SL_Load(buffer,todo); + + UltraPokeChunk(p,buffer,todo); + + p+=todo; + l-=todo; + } + + if(flags&SF_LOOP && !(flags&SF_BIDI)){ /* looping sample ? */ + + /* Anticlick for looping samples: + Copy the first bytes in the loop + beyond the end of the loop */ + + for(t=0;t<8;t++){ + UltraPoke(Ultra[handle]+loopend+t, + UltraPeek(Ultra[handle]+loopstart+t)); + } + } + else{ + + /* Anticlick for one-shot samples: + Zero the bytes beyond the end of the sample. + */ + + for(t=0;t<8;t++){ + UltraPoke(Ultra[handle]+length+t,0); + } + } + + return handle; +} + + + +void GUS_UnLoad(SWORD handle) +/* + callback routine to unload samples + + smp :sampleinfo of sample that is being freed +*/ +{ + UltraFree(Ultrs[handle],Ultra[handle]); + Ultra[handle]=0; +} + + + + +BOOL GUS_Init(void) +{ + ULONG p1,p2; + int irq; + + if(!(md_mode&DMODE_16BITS)){ + md_mode|=DMODE_16BITS; /* gus can't do 8 bit mixing */ + } + + if(!(md_mode&DMODE_STEREO)){ + md_mode|=DMODE_STEREO; /* gus can't do mono mixing */ + } + + if(!UltraDetect()){ + myerr="Couldn't detect gus, please check env. string"; + return 0; + } + + UltraOpen(14); + UltraTimer1Handler(GUS_Update); + + return 1; +} + + + +void GUS_Exit(void) +{ + UltraClose(); +} + + + +void GUS_PlayStart(void) +{ + int t; + for(t=0;t=size) return; + + if(flags&SF_LOOP){ + if(repend>size) repend=size; /* repend can't be bigger than size */ + } + + ghld[voice].flags=flags; + ghld[voice].handle=handle; + ghld[voice].start=start; + ghld[voice].size=size; + ghld[voice].reppos=reppos; + ghld[voice].repend=repend; + ghld[voice].kick=1; +} + +void GUS_Dummy(void) +{ +} + + +DRIVER drv_gus={ + NULL, + "Gravis Ultrasound", + "MikMod GUS Driver v2.1 (uses gus timer interrupt)", + GUS_IsThere, + GUS_Load, + GUS_UnLoad, + GUS_Init, + GUS_Exit, + GUS_PlayStart, + GUS_PlayStop, + GUS_Dummy, + GUS_VoiceSetVolume, + GUS_VoiceSetFrequency, + GUS_VoiceSetPanning, + GUS_VoicePlay +};