#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>


#define TRUE = 1

/* 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	GF1_PAGE		0x102		/* 3X2 */
#define GF1_REG_SELECT	0x103		/* 3X3 */
#define GF1_DATA_LOW	0x104		/* 3X4 */
#define GF1_DATA_HI		0x105		/* 3X5 */
#define GF1_IRQ_STAT	0x006		/* 2X6 */
#define GF1_DRAM		0x107		/* 3X7 */

#define	GF1_MIX_CTRL	0x000		/* 2X0 */
#define GF1_TIMER_CTRL	0x008		/* 2X8 */
#define GF1_TIMER_DATA	0x009		/* 2X9 */
#define GF1_IRQ_CTRL	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

/********************************************************************
 *
 * 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 */


/* 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 */


/* ($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 */

extern void PUSH_IRQ_STATE(void);
#pragma aux PUSH_IRQ_STATE = \
  "pushf" \
  "cli";

extern void POP_IRQ_STATE(void);
#pragma aux POP_IRQ_STATE = \
  "popf" \
  "sti";


typedef struct ULTRA_DATA {
	unsigned int flags;
	unsigned int base_port;
	unsigned int dram_dma_chan;
	unsigned int adc_dma_chan;
	unsigned int gf1_irq_num;
	unsigned int midi_irq_num;
	unsigned char	mix_image;
	unsigned char	voices;
	unsigned char	timer_ctrl;
	unsigned char	timer_mask;
	int		midi_data;
	int		midi_control;
	int		voice_select;
	int		reg_select;
	int		data_low;
	int		data_hi;
	int		irq_status;
	int		dram_data;
	int		mix_control;
	int		irq_control;
	int		timer_control;
	int		timer_data;
};


struct ULTRA_DATA _gf1_data;


unsigned short int Sample_Seg;
unsigned short int Sample_Ofs;
unsigned int GusDramStart = 0;


extern char *sampdata[128][16];
extern unsigned short int DOUBLE_MEM;
extern unsigned int Mastervolume;



void gf1_delay()
{
int i;

for (i=0;i<7*3;i++)
  (void)inp(_gf1_data.dram_data); /* pick a port .... */
}

void short_gf1_delay()
{
int i;

for (i=0;i<7;i++)
  (void)inp(_gf1_data.dram_data); /* pick a port .... */
}

unsigned char UltraPeekData (address)
unsigned long address;
{
    unsigned char	ret;

    PUSH_IRQ_STATE ();

    outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_LOW);
    outpw(_gf1_data.base_port+GF1_DATA_LOW,LSW(address)); /* 16 bits */
    outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_HIGH);
    outp(_gf1_data.base_port+GF1_DATA_HI,LSB(MSW(address))); /* 8 bits */
    ret = inp(_gf1_data.base_port+GF1_DRAM);

    POP_IRQ_STATE ();
    return (ret);
}


void UltraPokeData (address, data)
unsigned long address;
unsigned char data;
{
    PUSH_IRQ_STATE ();

    outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_LOW);
    outpw(_gf1_data.base_port+GF1_DATA_LOW,LSW(address)); /* 16 bits */
    outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_HIGH);
    outp(_gf1_data.base_port+GF1_DATA_HI,LSB(MSW(address))); /* 8 bits */
    outp(_gf1_data.base_port+GF1_DRAM,data);

    POP_IRQ_STATE ();
}

                     
void mem_2_gus(char inst, char nr,unsigned int length1,unsigned int is_looped)
{
  unsigned int xij;
  char *tempptr;
  unsigned int sample_start;
  unsigned int loop_start;
  int old_gus;
  int lsw_start;

  old_gus = -99999;

  sample_start = GusDramStart;
  loop_start = sample_start + is_looped;

  tempptr = sampdata[inst][nr];

/*
  if (DOUBLE_MEM == 1) {
    for (xij = 0; xij < length1; xij++) {

      PUSH_IRQ_STATE ();

      lsw_start = LSW(GusDramStart);

      if (lsw_start != old_gus) {
        old_gus = lsw_start;
        outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_LOW);
        outpw(_gf1_data.base_port+GF1_DATA_LOW,lsw_start);
      }

      outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_HIGH);
      outp(_gf1_data.base_port+GF1_DATA_HI,LSB(MSW(GusDramStart))); 

      outp(_gf1_data.base_port+GF1_DRAM,*tempptr);

      POP_IRQ_STATE ();

      GusDramStart ++;
      tempptr++;
      tempptr++;
      xij++;
    }
  }
  else { */

    PUSH_IRQ_STATE ();

    for (xij = 0; xij < length1; xij++) {
//      UltraPokeData(GusDramStart,*tempptr);

      lsw_start = LSW(GusDramStart);

      if (lsw_start != old_gus) {
        old_gus = lsw_start;
        outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_LOW);
        outpw(_gf1_data.base_port+GF1_DATA_LOW,lsw_start); /* 16 bits */
      }

      outp(_gf1_data.base_port+GF1_REG_SELECT,SET_DRAM_HIGH);
      outp(_gf1_data.base_port+GF1_DATA_HI,LSB(MSW(GusDramStart))); /* 8 bits */

      outp(_gf1_data.base_port+GF1_DRAM,*tempptr);

      GusDramStart ++;
      tempptr++;
    }

    POP_IRQ_STATE ();


    if (is_looped > 0) {
      for (xij = 0; xij < 16; xij++) {
        UltraPokeData(GusDramStart,UltraPeekData(loop_start+xij));
        GusDramStart ++;
      }
    }
    else {
      for (xij = 0; xij < 16; xij++) {
        UltraPokeData(GusDramStart,0);
        GusDramStart ++;
      }
    }
//  }

  GusDramStart = ((GusDramStart / 32)+1)*32;

}






void Init_Gus(unsigned short int port)
{
 char *base_str;

 strncpy(base_str,getenv("ULTRASND"),3);

 if (strcmp(base_str,"200") == 0) { port = 0x200; }
 if (strcmp(base_str,"210") == 0) { port = 0x210; }
 if (strcmp(base_str,"220") == 0) { port = 0x220; }
 if (strcmp(base_str,"230") == 0) { port = 0x230; }
 if (strcmp(base_str,"240") == 0) { port = 0x240; }
 if (strcmp(base_str,"250") == 0) { port = 0x250; }
 if (strcmp(base_str,"260") == 0) { port = 0x260; }
 if (strcmp(base_str,"270") == 0) { port = 0x270; }
 if (strcmp(base_str,"280") == 0) { port = 0x280; }
 if (strcmp(base_str,"290") == 0) { port = 0x290; }  

_gf1_data.base_port = port;
_gf1_data.voice_select = port + GF1_PAGE;
_gf1_data.reg_select = port + GF1_REG_SELECT;
_gf1_data.data_low = port + GF1_DATA_LOW;
_gf1_data.data_hi = port + GF1_DATA_HI;
_gf1_data.irq_status = port + GF1_IRQ_STAT;
_gf1_data.dram_data = port + GF1_DRAM;
_gf1_data.mix_control = port + GF1_MIX_CTRL;
_gf1_data.timer_control = port + GF1_TIMER_CTRL;
_gf1_data.timer_data = port+ GF1_TIMER_DATA;
_gf1_data.dram_dma_chan = 3;

_gf1_data.irq_control = port + GF1_IRQ_CTRL;

 outp(port,0x01);
 outp(_gf1_data.reg_select,SET_VOICES); 
 outp(_gf1_data.data_hi,(char)((32-1) | 0xC0));
}

void speaker_on(void)
{
 outp(_gf1_data.base_port,1);
}

void speaker_off(void)
{
 outp(_gf1_data.base_port,3);
}


void UltraSetPanning(voice,data)
int voice;
int data;
{
unsigned char temp; /* FOR MSC .... */

  data = data >> 2;

	temp = data & 0x0f;

	PUSH_IRQ_STATE ();

  outp(_gf1_data.voice_select,voice);
  outp(_gf1_data.reg_select,SET_BALANCE);
  outp(_gf1_data.data_hi,temp);

	POP_IRQ_STATE ();
}

void UltraStopVolume(voice)
int voice;
{
PUSH_IRQ_STATE ();

outp(_gf1_data.voice_select,voice);   /* select the proper voice */
outp(_gf1_data.reg_select,SET_VOLUME_CONTROL);
outp(_gf1_data.data_hi,0x03);     /* turn it off */

gf1_delay();

outp(_gf1_data.voice_select,voice);   /* select the proper voice */
outp(_gf1_data.reg_select,SET_VOLUME_CONTROL);
outp(_gf1_data.data_hi,VOLUME_STOPPED|STOP_VOLUME); /* turn it off */

POP_IRQ_STATE ();
}


static unsigned int _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
};

void
UltraSetLinearVolume(voice,index)
int voice;
int index;
{
  int volume;
	/* Use the table to get a linear volume */
  volume = _gf1_volumes[(index*4)];
  volume = (volume * Mastervolume) / 255;


  outp(_gf1_data.voice_select,voice);   /* select the proper voice */

  outp(_gf1_data.reg_select,6);   // Ramping Factor
  outp(_gf1_data.data_hi,0x3F);

  outp(_gf1_data.reg_select,9);  // Adjust volume
  outp(_gf1_data.data_hi,0x12);

  outp(_gf1_data.reg_select,7);  // ramping start-Adjust volume
  outp(_gf1_data.data_hi,0x12);

  outp(_gf1_data.reg_select,8);  // ramping end-Adjust volume
  outp(_gf1_data.data_hi,volume >> 4);

  outp(_gf1_data.reg_select,0x0d);  // set ramping direction
  outp(_gf1_data.data_hi,0);

}


void UltraSetDirectVolume(voice,index)
int voice;
int index;
{
  int volume;

  volume = _gf1_volumes[(index*4)];
  volume = (volume * Mastervolume) / 16;

  outp(_gf1_data.voice_select,voice);
  outp(_gf1_data.reg_select,SET_VOLUME);
  outpw(_gf1_data.data_low,volume);
}

void	UltraSetFrequency(voice,speed_khz)
int voice;
unsigned long speed_khz;
{
unsigned int fc;
unsigned long temp;

 temp = speed_khz / 18;

 outp(_gf1_data.voice_select,voice);
 outp(_gf1_data.reg_select,SET_FREQUENCY);
 outpw(_gf1_data.data_low,temp);
}


unsigned char UltraPrimeVoice(voice,begin,start,end,mode,vol,is_16)
int voice;              /* voice to start */
unsigned long begin;		/* start location in ultra DRAM */
unsigned long start;		/* start loop location in ultra DRAM */
unsigned long end;			/* end location in ultra DRAM */
unsigned char mode;			/* mode to run the voice (loop etc) */
unsigned short int vol;
unsigned short int is_16;
{
unsigned long phys_start;
unsigned long phys_end;
unsigned long phys_begin;


	begin = start;

	/* if start is greater than end, flip 'em and turn on */
	/* decrementing addresses */
	if (start > end)
		{
		start = end;
		end = begin;
		mode |= VC_DIRECT;
		}

  phys_begin = begin;
  phys_start = start;
  phys_end = end;


  PUSH_IRQ_STATE ();

	/* Make sure was are talking to proper voice */
  outp(_gf1_data.voice_select,voice);

	/* First set accumulator to beginning of data */
  outp(_gf1_data.reg_select,SET_ACC_HIGH);
  outpw(_gf1_data.data_low,ADDR_HIGH(phys_begin));
  outp(_gf1_data.reg_select,SET_ACC_LOW);
  outpw(_gf1_data.data_low,ADDR_LOW(phys_begin));

	/* Set start loop address of buffer */
  outp(_gf1_data.reg_select,SET_START_HIGH);
  outpw(_gf1_data.data_low,ADDR_HIGH(phys_start));
  outp(_gf1_data.reg_select,SET_START_LOW);
  outpw(_gf1_data.data_low,ADDR_LOW(phys_start));

	/* Set end address of buffer */
  outp(_gf1_data.reg_select,SET_END_HIGH);
  outpw(_gf1_data.data_low,ADDR_HIGH(phys_end));
  outp(_gf1_data.reg_select,SET_END_LOW);
  outpw(_gf1_data.data_low,ADDR_LOW(phys_end));

  POP_IRQ_STATE ();


  outp(_gf1_data.voice_select,voice);

	mode &= ~(VOICE_STOPPED|STOP_VOICE);	/* turn 'stop' bits off ... */


	/* NOTE: no irq's from the voice ... */
  outp(_gf1_data.reg_select,SET_CONTROL);
  outp(_gf1_data.data_hi,mode);

	gf1_delay();

  outp(_gf1_data.reg_select,SET_CONTROL);
  outp(_gf1_data.data_hi,mode);


  UltraSetLinearVolume(voice,vol);
  return(mode);
}

unsigned char UltraRestartVoice(voice,begin,start,end,mode,vol,is_16)
int voice;              /* voice to start */
unsigned long begin;		/* start location in ultra DRAM */
unsigned long start;		/* start loop location in ultra DRAM */
unsigned long end;			/* end location in ultra DRAM */
unsigned char mode;			/* mode to run the voice (loop etc) */
unsigned short int vol;
unsigned short int is_16;
{
unsigned long phys_start,phys_end;
unsigned long phys_begin;


	begin = start;

	/* if start is greater than end, flip 'em and turn on */
	/* decrementing addresses */
	if (start > end)
		{
		start = end;
		end = begin;
		mode |= VC_DIRECT;
		}

  phys_begin = begin;
  phys_start = start;
  phys_end = end;


  PUSH_IRQ_STATE ();

	/* Make sure was are talking to proper voice */
  outp(_gf1_data.voice_select,voice);

  /* Stop the voice ... */
  outp(_gf1_data.reg_select,SET_CONTROL);
  outp(_gf1_data.data_hi,3);
	gf1_delay();
  UltraSetDirectVolume(voice,0);
  outp(_gf1_data.reg_select,SET_CONTROL);
  outp(_gf1_data.data_hi,3);
	gf1_delay();


	/* First set accumulator to beginning of data */
  outp(_gf1_data.reg_select,SET_ACC_HIGH);
  outpw(_gf1_data.data_low,ADDR_HIGH(phys_begin));
  outp(_gf1_data.reg_select,SET_ACC_LOW);
  outpw(_gf1_data.data_low,ADDR_LOW(phys_begin));

	/* Set start loop address of buffer */
  outp(_gf1_data.reg_select,SET_START_HIGH);
  outpw(_gf1_data.data_low,ADDR_HIGH(phys_start));
  outp(_gf1_data.reg_select,SET_START_LOW);
  outpw(_gf1_data.data_low,ADDR_LOW(phys_start));

	/* Set end address of buffer */
  outp(_gf1_data.reg_select,SET_END_HIGH);
  outpw(_gf1_data.data_low,ADDR_HIGH(phys_end));
  outp(_gf1_data.reg_select,SET_END_LOW);
  outpw(_gf1_data.data_low,ADDR_LOW(phys_end));

  POP_IRQ_STATE ();


  outp(_gf1_data.voice_select,voice);

	mode &= ~(VOICE_STOPPED|STOP_VOICE);	/* turn 'stop' bits off ... */

  UltraSetLinearVolume(voice,vol);


	/* NOTE: no irq's from the voice ... */
  outp(_gf1_data.reg_select,SET_CONTROL);
  outp(_gf1_data.data_hi,mode);

	gf1_delay();

  outp(_gf1_data.reg_select,SET_CONTROL);
  outp(_gf1_data.data_hi,mode);

  return(mode);
}




void stop_gus(void)
{
 unsigned short int i;
 for (i = 0; i < 32; i++) {
   UltraPrimeVoice(i,0,0,0,1,0,0);
   UltraSetLinearVolume(i,0);
 }
 outp(_gf1_data.base_port,3);
}

