#include "types.h"
#include "gus.h"

// GUS - Mr.BAGGiNS / EXTRAVAGANZA in 1995

unsigned char AmpliferVolume = 17;
unsigned char DefaultPanning = 07;           // Default Panning;  0..7
unsigned short UltraBase     = 0x200;        // GUS Base Address
unsigned short ModError      = 0;            // 0..3 and 255

unsigned short ActiveVoice2  = ActiveVoice;
unsigned short Command2      = Command;
unsigned short DataLow2      = DataLow;
unsigned short DataHigh2     = DataHigh;
unsigned short DRamIo2       = DRamIo;

int TopAdress;                 // Next free address in GUS mem
int GUSBank;

unsigned char GUSInfo[8] = { 0x42,0x45,0x54,0x41,0x2f,0x41,0x44,0x4e };
        
unsigned short GUSVolume[65] =
    {0000,1750,2503,2701,2741,2781,2944,2964,2981,
     3000,3017,3034,3052,3070,3207,3215,3224,3232,
     3240,3248,3256,3263,3271,3279,3287,3294,3303,
     3310,3317,3325,3458,3462,3466,3469,3473,3478,
     3481,3484,3489,3492,3495,3499,3502,3506,3509,
     3513,3517,3520,3524,3528,3532,3534,3538,3543,
     3545,3549,3552,3556,3558,3563,3565,3570,3573,
     3577,3580};

int VoicePos(unsigned char V)
{
  unsigned short Temp0, Temp1;

  outp(ActiveVoice2, V);
  outp(Command2, 0x8A);
  Temp0 = inpw(DataLow2);
  outp(Command2, 0x8B);
  Temp1 = inpw(DataLow2);
  return( (Temp0 << 7) + (Temp1 >> 8) );
}

char GUSPeek(unsigned int Loc)
{
  unsigned short AddLo;
  unsigned char AddHi;

  AddLo = Loc & 0xFFFF;
  AddHi = (Loc & 0xFF0000) >> 16;
  outp(UltraBase + 0x103, 0x43);
  outpw(UltraBase + 0x104, AddLo);
  outp(UltraBase + 0x103, 0x44);
  outp(UltraBase + 0x105, AddHi);
  return( inp(UltraBase + 0x107) );
}

void GUSPoke(unsigned int Loc, unsigned char B)
{
  unsigned short AddLo;
  unsigned char AddHi;

  AddLo = Loc & 0xFFFF;
  AddHi = (Loc & 0xff0000) >> 16;
  outp(UltraBase + 0x103, 0x43);
  outpw(UltraBase + 0x104, AddLo);
  outp(UltraBase + 0x103, 0x44);
  outp(UltraBase + 0x105, AddHi);
  outp(UltraBase + 0x107, B);
}

int GUSProbe(unsigned short Adr)
{
  unsigned char B;
  unsigned short OUltraBase;

  OUltraBase = UltraBase;
  UltraBase = Adr;
  outp(UltraBase + 0x103, 0x4C);
  outp(UltraBase + 0x105, 0);
  GUSDelay();
  GUSDelay();
  outp(UltraBase + 0x103, 0x4C);
  outp(UltraBase + 0x105, 1);
  GUSPoke(0, 0xAA);
  GUSPoke(0x100, 0x55);
  B = GUSPeek(0);
  UltraBase = OUltraBase;
  if (B == 0xAA) return(true);
   else return(false);
}

void GUSFind(void)
{
  unsigned short I,B;

  if (UltraBase == 0x200)
  {
    I = 1;
    while (I <= 8)
    {
      B = 0x200 + I*0x10;
      if (GUSProbe(B)) I = 8;
      I += 1;
    }
    if (B < 0x280) UltraBase = B;
     else UltraBase = 0x200;
  }
  if (UltraBase != 0x200)
  {
      printf("þ Gravis UltraSound found at %#x\n", UltraBase);
      ActiveVoice2 = UltraBase + ActiveVoice;
      Command2 = UltraBase + Command;
      DataLow2 = UltraBase + DataLow;
      DataHigh2 = UltraBase + DataHigh;
      DRamIo2 = UltraBase + DRamIo;
  }
}

int GUSFindMem(void)
{
  int I;

  GUSPoke(0x40000, 0xAA);
  if (GUSPeek(0x40000) != 0xAA) I = 0x3FFFF;
   else
   {
     GUSPoke(0x80000, 0xAA);
     if (GUSPeek(0x80000) != 0xAA) I = 0x8FFFF;
      else
      {
        GUSPoke(0xC0000, 0xAA);
        if (GUSPeek(0xC0000) != 0xAA) I = 0xCFFFF;
         else I = 0xFFFFF;
      }
   }
  GUSPoke(0, 0xAA);
  if (GUSPeek(0) != 0xAA) I = 0;
  return( I );
}

void GUSStopVoice(unsigned char V)
{
  unsigned char Temp;

  _CLI_();
  outp(ActiveVoice2, V);
  outp(ActiveVoice2, V);
  outp(Command2, 0x80);
  Temp = inp(DataHigh2);
  outp(Command2, 0);
  outp(DataHigh2, (Temp & 0xDF) | 3);
  GUSDelay();
  outp(Command2, 0);
  outp(DataHigh2, (Temp & 0xDF) | 3);
  _STI_();
  GUSSetVolume(V, 0);
}

void GUSPlayVoice(unsigned char V, unsigned char Mode, unsigned int VBegin,
                  unsigned int VStart, unsigned int VEnd)
{
  _CLI_();
  outp(ActiveVoice2, V);
  outp(ActiveVoice2, V);
  outp(Command2, 0);
  outp(DataHigh2, 2);                 // This means voice stop
  outp(Command2, 0xA);
  outpw(DataLow2, (VBegin >> 7) & 8191);
  outp(Command2, 0xB);
  outpw(DataLow2, (VBegin & 127) << 9);
  outp(Command2, 2);
  outpw(DataLow2, (VStart >> 7) & 8191);
  outp(Command2, 3);
  outpw(DataLow2, (VStart & 127) << 9);
  outp(Command2, 4);
  outpw(DataLow2, (VEnd >> 7) & 8191);
  outp(Command2, 5);
  outpw(DataLow2, (VEnd & 127) << 9);
  outp(Command2, 0);
  outp(DataHigh2, Mode & 0xFE);
  _STI_();
}

void GUSPlayAll(unsigned char V, unsigned char Mode, unsigned int VBegin,
                unsigned int VStart, unsigned int VEnd, unsigned short Freq, unsigned short Vol)
{
  _CLI_();
  outp(ActiveVoice2, V);
  outp(Command2, 0);
  outp(DataHigh2, 2);      // Stop Voice
  outp(Command2, 9);
  outpw(DataLow2, Vol);    // Set Volume
  outp(Command2, 1);
  outpw(DataLow2, Freq);

  outp(Command2, 0xA);
  outpw(DataLow2, (VBegin >> 7) & 8191);
  outp(Command2, 0xB);
  outpw(DataLow2, (VBegin & 127) << 9);
  outp(Command2, 2);
  outpw(DataLow2, (VStart >> 7) & 8191);
  outp(Command2, 3);
  outpw(DataLow2, (VStart & 127) << 9);
  outp(Command2, 4);
  outpw(DataLow2, (VEnd >> 7) & 8191);
  outp(Command2, 5);
  outpw(DataLow2, (VEnd & 127) << 9);
  outp(Command2, 0);
  outp(DataHigh2, Mode & 0xFE);
  _STI_();
}

void GUSRelVoice(unsigned char V)
{
  unsigned char B;

  outp(ActiveVoice2, V);
  outp(Command2, 0x80);
  B = inp(DataHigh2);
  outp(Command2, 0);
  outp(DataHigh2, B & 0xFC);
}

void GUSSetOfs(unsigned char V, unsigned int VBegin)
{
  _CLI_();
  outp (ActiveVoice2, V);
  outp (ActiveVoice2, V);
  outp (Command2, 0xA);
  outpw(DataLow2, (VBegin >> 7) & 8191);
  outp (Command2, 0xB);
  outpw(DataLow2, (VBegin & 127) << 9);
  _STI_();
}

void GUSReset(void)
{
  unsigned short N;

  outp(UltraBase, 3);
  outp(UltraBase+0x103, 0x4C);
  outp(UltraBase+0x105, 1);
  GUSDelay();
  outp(UltraBase+0x103, 0x4c);
  outp(UltraBase+0x105, 0);
  GUSDelay();
  outp(UltraBase+0x103, 0x4c);
  outp(UltraBase+0x105, 1);
  GUSDelay();
  outp(UltraBase+0x103, 0x4C);
  outp(UltraBase+0x105, 7);
  outp(UltraBase+0x103, 0xE);
  outp(UltraBase+0x105, 13 | 0xC0);
  GUSDelay();
  outp(UltraBase, 1);
  outp(UltraBase+Command, 0x4C);
  outp(UltraBase+DataHigh, 7);
  N = GUSInfo[1];
  for (N=1; N<=32; N++)              // Reset All Voices
  {
    outp (UltraBase+ActiveVoice, N);
    outp (UltraBase+Command, 0);
    outp (UltraBase+DataHigh, 2);
    outp (UltraBase+Command, 0xD);
    outp (UltraBase+DataHigh, 2);
    outp (UltraBase+Command, 0xA);
    outpw(UltraBase+DataLow, 0);
    outp (UltraBase+Command, 0xB);
    outpw(UltraBase+DataLow, 0);
    outp (UltraBase+Command, 9);
    outpw(UltraBase+DataLow, 0);
  }
}

void GUSDeInit(void)
{
  int N;

  for (N=1; N<=32; N++) GUSStopVoice(N);
}
