#include <stdio.h>
#include <dos.h>
#include "dosalloc.h"
#include "types.h"
#include "gus.h"

/*
   GUS MOD, Conversion from Mc.KRiSS's PASCAL Version to WATCOM by Mr.BAGGiNS
*/

unsigned short PeriodTable[16][48] = {
    {856,808,762,720,678,640,604,570,538,508,480,453,  // C-1..B-1 FineTune 0
     428,404,381,360,339,320,302,285,269,254,240,226,  // C-2..B-2 FineTune 0
     214,202,190,180,170,160,151,143,135,127,120,113,  // C-3..B-3 FineTune 0
     107,101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56}, // C-4..B-4 FineTune 0
    {850,802,757,715,674,637,601,567,535,505,477,450,  // C-1..B-1 FineTune+1
     425,401,379,357,337,318,300,284,268,253,239,225,  // C-2..B-2 FineTune+1
     213,201,189,179,169,159,150,142,134,126,119,113,  // C-3..B-3 FineTune+1
     106,100, 94, 89, 84, 79, 75, 71, 67, 83, 59, 56}, // C-4..B-4 FineTune+1
    {844,796,752,709,670,632,597,563,532,502,474,447,  // C-1..B-1 FineTune+2
     422,398,376,355,335,316,298,282,266,251,237,224,  // C-2..B-2 FineTune+2
     211,199,188,177,167,158,149,141,133,125,118,112,  // C-3..B-3 FineTune+2
     105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59, 56}, // C-4..B-4 FineTune+2
    {838,791,746,704,665,628,592,559,528,498,470,444,  // C-1..B-1 FineTune+3
     419,395,373,352,332,314,296,280,264,249,235,222,  // C-2..B-2 FineTune+3
     209,198,187,176,166,157,148,140,132,125,118,111,  // C-3..B-3 FineTune+3
     104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59, 55}, // C-4..B-4 FineTune+3
    {832,785,741,699,660,623,588,555,524,495,467,441,  // C-1..B-1 FineTune+4
     416,392,370,350,330,312,294,278,262,247,233,220,  // C-2..B-2 FineTune+4
     208,196,185,175,165,156,147,139,131,124,117,110,  // C-3..B-3 FineTune+4
     104, 98, 92, 87, 82, 78, 73, 69, 65, 62, 58, 55}, // C-4..B-4 FineTune+4
    {826,779,736,694,655,619,584,551,520,491,463,437,  // C-1..B-1 FineTune+5
     413,390,368,347,328,309,292,276,260,245,232,219,  // C-2..B-2 FineTune+5
     206,195,184,174,164,155,146,138,130,123,116,109,  // C-3..B-3 FineTune+5
     103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58, 54}, // C-4..B-4 FineTune+5
    {820,774,730,689,651,614,580,547,516,487,460,434,  // C-1..B-1 FineTune+6
     410,387,365,345,325,307,290,274,258,244,230,217,  // C-2..B-2 FineTune+6
     205,193,183,172,163,154,145,137,129,122,115,109,  // C-3..B-3 FineTune+6
     102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57, 54}, // C-4..B-4 FineTune+6
    {814,768,725,684,646,610,575,543,513,484,457,431,  // C-1..B-1 FineTune+7
     407,384,363,342,323,305,288,272,256,242,228,216,  // C-2..B-2 FineTune+7
     204,192,181,171,161,152,144,136,128,121,114,108,  // C-3..B-3 FineTune+7
     102, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57, 54}, // C-4..B-4 FineTune+7
    {907,856,808,762,720,678,640,604,570,538,504,480,  // C-1..B-1 FineTune-8
     453,428,404,381,360,339,320,302,285,269,254,240,  // C-2..B-2 FineTune-8
     226,214,202,190,180,170,160,151,143,135,127,120,  // C-3..B-3 FineTune-8
     113,107,101, 95, 90, 85, 80, 75, 71, 67, 63, 60}, // C-4..B-4 FineTune-8
    {900,850,802,757,715,675,636,601,567,535,505,477,  // C-1..B-1 FineTune-7
     450,425,401,379,357,337,318,300,284,268,253,238,  // C-2..B-2 FineTune-7
     225,212,200,189,179,169,159,150,142,134,126,119,  // C-3..B-3 FineTune-7
     112,106,100, 94, 89, 84, 79, 75, 71, 67, 63, 59}, // C-4..B-4 FineTune-7
    {894,844,796,752,709,670,632,597,563,532,502,474,  // C-1..B-1 FineTune-6
     447,422,398,376,355,335,316,298,282,266,251,237,  // C-2..B-2 FineTune-6
     223,211,199,188,177,167,158,149,141,133,125,118,  // C-3..B-3 FineTune-6
     111,105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59}, // C-4..B-4 FineTune-6
    {887,838,791,746,704,665,628,592,559,528,498,470,  // C-1..B-1 FineTune-5
     444,419,395,373,352,332,314,296,280,264,249,235,  // C-2..B-2 FineTune-5
     222,209,198,187,176,166,157,148,140,132,125,118,  // C-3..B-3 FineTune-5
     111,104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59}, // C-4..B-4 FineTune-5
    {881,832,785,741,699,660,623,588,555,524,494,467,  // C-1..B-1 FineTune-4
     441,416,392,370,350,330,312,294,278,262,247,233,  // C-2..B-2 FineTune-4
     220,208,196,185,175,165,156,147,139,131,123,117,  // C-3..B-3 FineTune-4
     110,104, 98, 92, 87, 82, 78, 73, 69, 65, 61, 58}, // C-4..B-4 FineTune-4
    {875,826,779,736,694,655,619,584,551,520,491,463,  // C-1..B-1 FineTune-3
     437,413,390,368,347,338,309,292,276,260,245,232,  // C-2..B-2 FineTune-3
     219,206,195,184,174,164,155,146,138,130,123,116,  // C-3..B-3 FineTune-3
     109,103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58}, // C-4..B-4 FineTune-3
    {868,820,774,730,689,651,614,580,547,516,487,460,  // C-1..B-1 FineTune-2
     434,410,387,365,345,325,307,290,274,258,244,230,  // C-2..B-2 FineTune-2
     217,205,193,183,172,163,154,145,137,129,122,115,  // C-3..B-3 FineTune-2
     108,102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57}, // C-4..B-4 FineTune-2
    {862,814,768,725,684,646,610,575,543,513,484,457,  // C-1..B-1 FineTune-1
     431,407,384,363,342,323,305,288,272,256,242,228,  // C-2..B-2 FineTune-1
     216,203,192,181,171,161,152,144,136,128,121,114,  // C-3..B-3 FineTune-1
     108,101, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57}};// C-4..B-4 FineTune-1

char VibratoTable[3][64] =
   {{ 00, 06, 12, 19, 24, 30, 36, 41, 45, 49, 53, 56, 59, 61, 63, 64,
      64, 64, 63, 61, 59, 56, 53, 49, 45, 41, 36, 30, 24, 19, 12, 06,
      00,-06,-12,-19,-24,-30,-36,-41,-45,-49,-53,-56,-59,-61,-63,-64,
     -64,-64,-63,-61,-59,-56,-53,-49,-45,-41,-36,-30,-24,-19,-12,-06},
    {-63,-61,-59,-57,-55,-53,-51,-49,-47,-45,-43,-41,-39,-37,-35,-33,
     -31,-29,-27,-25,-23,-21,-19,-17,-15,-13,-11, -9, -7, -5, -3, -1,
       1,  3,  5,  7,  9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31,
      33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63},
    {-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
     -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}};

char TagStrs[20][5] = {"M.K.\0","FLT4\0","4CHN\0","M!K!\0",
                       "FLT8\0","8CHN\0","OCTA\0","6CHN\0",
                       "10CH\0","12CH\0","14CH\0","16CH\0",
                       "18CH\0","20CH\0","22CH\0","24CH\0",
                       "26CH\0","28CH\0","30CH\0","32CH\0"};

int WhereIsPat[129];

unsigned char TempBuff[5000];           // used while loading MOD
unsigned short *PTempBuff;

struct MODHeader Header;

int GUSAdress[33];
unsigned short Periods[1100];
struct Channel Channels[MaxChannels];
struct MODSample Samples[33];
MODPattern __far *Patterns[129];
unsigned char Orders[256];
unsigned short MaxPatterns;                        // # of Patterns in MOD
unsigned char  CurrentPat;
unsigned char  CurrentRow;
unsigned char  CurrentTick;
unsigned char  NewPattern;
unsigned char  NewRow;    
unsigned char  Jump;   
unsigned char  Speed; 
unsigned char  NSpeed;
unsigned char  Tempo;
boolean  VBlank;                // TRUE means do not use BPM tempos
volatile unsigned short IntTick;
volatile unsigned short OldIntTick;
volatile unsigned short IntRate;
volatile unsigned int TimeCounter;      // For syncing with demos. 1/18.2
volatile unsigned int TimeCounter2;     // Increase every tick
volatile unsigned char VrtFlag;         // if 1 then vertical retrace happened

/* ************************************ */
void (__interrupt __far *prev_int_8)();
/* ************************************ */

void GeMODNotes(void)
{
  unsigned char Chn, Ptn, Org_Sam, Sam, Note;
  unsigned int St_Ofs;
  unsigned short Per, DesPer, Vol, Freq, TemFx;
  unsigned char TemFxData, Mute;
  unsigned short W;
  unsigned char TemEFxData;

  Ptn = Orders[CurrentPat];
  for (Chn=0; Chn<Header.Chns; Chn++)
  {
    if (Channels[Chn].Fx == 0)
    {
      Sam = Channels[Chn].Sample;
      Per = PeriodTable[Samples[Sam].FTune][Channels[Chn].Note-1];
      GUSSetFreq(Chn+1, Periods[Per]);
    }
    Channels[Chn].Hit = 0;
    if ( ((*Patterns[Ptn])[CurrentRow][Chn].Per > 0) ||
         ((*Patterns[Ptn])[CurrentRow][Chn].Sample > 0))
    {
      Mute = 1;
      Vol  = Channels[Chn].Vol;
      Per  = Channels[Chn].Per;
      Note = Channels[Chn].Note;
      Freq = Periods[Channels[Chn].Per];
      TemFx = (*Patterns[Ptn])[CurrentRow][Chn].Fx;
      TemFxData = (*Patterns[Ptn])[CurrentRow][Chn].FxData;
      Org_Sam = (*Patterns[Ptn])[CurrentRow][Chn].Sample;
      Channels[Chn].FxStart = 0;
      Channels[Chn].RetrigCnt = 0;
      if (Org_Sam == 0) Sam = Channels[Chn].Sample;
       else Sam = Org_Sam;
      if ((TemFx == 3) || (TemFx == 5))
      {                    // Portamento To / Portamento To & Volume Slide
        Mute = 1;          // Don't Restart sample
        if ((*Patterns[Ptn])[CurrentRow][Chn].Note > 0)
        {
          Note = (*Patterns[Ptn])[CurrentRow][Chn].Note;
          DesPer = PeriodTable[Samples[Sam].FTune][Note-1];
          if (DesPer > MaxPeriod) DesPer = MaxPeriod;
          if (DesPer < MinPeriod) DesPer = MinPeriod;
          Channels[Chn].DesPer = DesPer;
        }
      } else if ((*Patterns[Ptn])[CurrentRow][Chn].Per > 0)
             {
               if ((*Patterns[Ptn])[CurrentRow][Chn].Note > 0)
               {
                 Note = (*Patterns[Ptn])[CurrentRow][Chn].Note;
                 Per = PeriodTable[Samples[Sam].FTune][Note-1];
               } else if ((*Patterns[Ptn])[CurrentRow][Chn].Per > 0)
                         Per = (*Patterns[Ptn])[CurrentRow][Chn].Per;
               if (Per > MaxPeriod) Per = MaxPeriod;
               if (Per < MinPeriod) Per = MinPeriod;
               Channels[Chn].DesPer = Per;
               Channels[Chn].Per = Per;
               Freq = Periods[Per];
               Mute = 0;
             }
      if (Org_Sam > 0)
      {
        Vol = Samples[Sam].Volume;
        if (Channels[Chn].Sample != Org_Sam) Mute = 0;
      }
      if (Samples[Sam].Length > 0) St_Ofs = 2;   // 1..2, Amiga Loopinfo
      Channels[Chn].NoFx = 0;

      switch(TemFx)
      {
        case 0x9:  W = TemFxData*0x100;
                   St_Ofs = W;
                   Channels[Chn].NoFx = 1;
                   Channels[Chn].Fx = TemFx;
                   Channels[Chn].FxData = TemFxData;
                   break;
        case 0xC:  if (TemFxData > 64) TemFxData = 64;
                   Vol = TemFxData;
                   break;
        case 0xE:  TemEFxData = TemFxData & 15;
                   switch(TemFxData >> 4)
                   {
                     case 0x4: Channels[Chn].Fx = TemFx;
                               Channels[Chn].FxData = TemFxData;
                               if (TemEFxData & 3 < 3) Channels[Chn].VibWave = TemEFxData;
                                 else Channels[Chn].VibWave = TemEFxData & 4;
                               break;
                     case 0xC: if (TemEFxData & 15 == 0)
                               {
                                 Mute = 1;
                                 GUSStopVoice(Chn+1);
                               }
                               break;
                     case 0xD: if (TemEFxData > 0) Mute = 2;
                                 else Mute = 0;
                               break;
                     default: break;
                   }
                   break;
        default: break;
      }

      Channels[Chn].Vol = Vol;
      Channels[Chn].Note = Note;
      if ((Channels[Chn].VibWave & 4) == 0) Channels[Chn].VibCnt = 0;
      Channels[Chn].Sample = Sam;
      Channels[Chn].Bar = Channels[Chn].Vol;
      Vol = GUSVolume[Vol]*AmpliferVolume;
      if (Channels[Chn].On == 0) Mute = 1;
      if (Mute == 0)
      {
        Channels[Chn].Hit = 1;
        GUSSetBalance(Chn+1, Channels[Chn].Pan);
        if (Samples[Sam].LoopEnd > 2)
          GUSPlayAll(Chn+1,8,GUSAdress[Sam]+St_Ofs,
                     GUSAdress[Sam]+Samples[Sam].LoopStart,
                     GUSAdress[Sam]+Samples[Sam].LoopEnd,Freq,Vol);
         else GUSPlayAll(Chn+1,0,GUSAdress[Sam]+St_Ofs,
                         GUSAdress[Sam]+St_Ofs,
                         GUSAdress[Sam]+Samples[Sam].Length,Freq,Vol);
      } else if ((Channels[Chn].On == 1) && (Mute == 1))
               GUSSetVolume(Chn+1,Vol);
    }
  }
}

void GetTemFx(void)
{
  unsigned char Chn, Ptn, TemFx, TemFxData, TemEFx, TemEFxData, B;
  unsigned short Per, W;

  Ptn = Orders[CurrentPat];
  NewPattern = CurrentPat;
  NewRow = CurrentRow;
  Jump = 0;
  for (Chn=0; Chn<Header.Chns; Chn++)
  {
    if (Channels[Chn].NoFx == 0)
    {
      Channels[Chn].FxStart = 0;
      Channels[Chn].Fx = 255;
      TemFx = (*Patterns[Ptn])[CurrentRow][Chn].Fx;
      TemFxData = (*Patterns[Ptn])[CurrentRow][Chn].FxData;
      switch(TemFx)
      {
        case 0:   if (TemFxData > 0)       // Arpeggio: simulate the chords
                  {
                    Channels[Chn].Fx = TemFx;
                    Channels[Chn].FxData = TemFxData;
                    Channels[Chn].Arp1 = TemFxData >> 4;
                    Channels[Chn].Arp2 = TemFxData & 15;
                    Channels[Chn].ArpeggioCnt = 0;
                  }
                  break;
        case 0x1: Channels[Chn].Fx = TemFx;         // Portamento up
                  Channels[Chn].FxData = TemFxData;
                  Channels[Chn].FxStart = 2;
                  break;
        case 0x2: Channels[Chn].Fx = TemFx;         // Portamento down
                  Channels[Chn].FxData = TemFxData;
                  Channels[Chn].FxStart = 2;
                  break;
        case 0x3: Channels[Chn].Fx = TemFx;          // Tone Portamento
                  if (TemFxData > 0)
                  {
                    Channels[Chn].FxData = TemFxData;
                    Channels[Chn].FxSl2 = (signed short)TemFxData;
                  } else Channels[Chn].FxData = Channels[Chn].FxSl2;
                  Channels[Chn].FxStart = 2;
                  break;
        case 0x4: Channels[Chn].Fx = TemFx;         // Vibrato
                  B = TemFxData & 15;
                  if (B == 0) B = Channels[Chn].FxVib & 15;
                  W = B;
                  B = TemFxData >> 4;
                  if (B == 0) B = Channels[Chn].FxVib >> 4;
                  W = W | (B << 4);
                  B = W;
                  Channels[Chn].FxData = B;
                  Channels[Chn].FxVib = B;
                  break;
        case 0x5: Channels[Chn].Fx = TemFx;         // Tone Portamento & Volume Slide
                  if ((TemFxData & 15) > 0) TemFxData = TemFxData & 15; // slide down
                  Channels[Chn].FxData = TemFxData;
                  break;
        case 0x6: Channels[Chn].Fx = TemFx;         // Vibrato & Volume Slide
                  Channels[Chn].FxData = TemFxData;
                  break;
        case 0x7: Channels[Chn].Fx = TemFx;         // Tremolo
                  Channels[Chn].FxData = TemFxData;
                  break;
        case 0x8: Channels[Chn].Fx = TemFx;         // Set panning
                  Channels[Chn].FxData = TemFxData;
                  break;
        case 0x9: Channels[Chn].Fx = TemFx;          // Set Sample offset
                  Channels[Chn].FxData = TemFxData;
                  W = TemFxData*256;
                  B = Channels[Chn].Sample;
                  if (Channels[Chn].On == 1) GUSSetOfs(Chn+1,GUSAdress[B]+W);
                  break;
        case 0xA: Channels[Chn].Fx = TemFx;           // Volume slide
                  if (TemFxData & 15 > 0) TemFxData = TemFxData & 15;  // up
                  Channels[Chn].FxData = TemFxData;
                  Channels[Chn].FxStart = 2;
                  break;
        case 0xB: if (TemFxData < MaxPatterns)       // Position jump
                  {
                    NewPattern = TemFxData;
                    NewRow = 0;
                    Jump = 1;
                  }
                  break;
        case 0xC: if (TemFxData > 64) TemFxData = 64;   // Set volume
                  Channels[Chn].Fx = TemFx;
                  Channels[Chn].FxData = TemFxData;
                  Channels[Chn].Vol = TemFxData;
                  Channels[Chn].Bar = TemFxData;
                  W = GUSVolume[TemFxData]*AmpliferVolume;
                  if (Channels[Chn].On == 1) GUSSetVolume(Chn+1, W);
                  break;
        case 0xD: NewPattern = CurrentPat;            // Break pattern
                  NewPattern++;
                  NewRow = ((TemFxData & 0xF0) >> 4)*10 + (TemFxData & 15);
                  Jump = 1;
                  break;
        case 0xE: Channels[Chn].Fx = TemFx;               // Extended effects
                  Channels[Chn].FxData = TemFxData;
                  TemEFx = TemFxData >> 4;
                  TemEFxData = TemFxData & 15;
                  switch(TemEFx)
                  {
                    case 0x1: Per = Channels[Chn].Per;  // Unused AMiGA Filter ?
                              Per += TemEFxData;
                              if (Per > MaxPeriod) Per = MaxPeriod;
                              Channels[Chn].Per = Per;
                              W = Periods[Channels[Chn].Per];
                              GUSSetFreq(Chn+1, W);
                              break;
                    case 0x2: Per = Channels[Chn].Per;
                              Per -= TemEFxData;
                              if (Per < MinPeriod) Per = MinPeriod;
                              Channels[Chn].Per = Per;
                              W = Periods[Channels[Chn].Per];
                              GUSSetFreq(Chn+1,W);
                              break;
                    case 0x4: Channels[Chn].VibWave = TemEFxData; // Set Vibrato waveform
                              break;
                    case 0x5: Samples[Channels[Chn].Sample].FTune = TemEFxData; // Set finetune
                              break;
                    case 0x8: Channels[Chn].Pan = TemEFxData;     // set MTM-Panning
                              GUSSetBalance(Chn+1,TemEFxData);
                              break;
                    case 0x9: if (TemEFxData > 0)                 // Retrigger
                              {
                                Channels[Chn].Fx = TemFx;
                                Channels[Chn].FxData = TemFxData;
                                Channels[Chn].RetrigCnt = TemEFxData;
                              }
                              break;
                    case 0xA: B = Channels[Chn].Vol;    // Fine volume slide up
                              B += TemEFxData;
                              if (B > 64) B = 64;
                              Channels[Chn].Vol = B;
                              W = GUSVolume[B]*AmpliferVolume;
                              if (Channels[Chn].On == 1) GUSSetVolume(Chn+1,W);
                              Channels[Chn].Bar = B;
                              break;
                    case 0xB: B = Channels[Chn].Vol;         // Fine volume slide down
                              B -= TemEFxData;
                              if (B > 128) B = 0;
                              Channels[Chn].Vol = B;
                              W = GUSVolume[B]*AmpliferVolume;
                              if (Channels[Chn].On == 1) GUSSetVolume(Chn+1,W);
                              Channels[Chn].Bar = B;
                              break;
                    case 0xC: Channels[Chn].Fx = TemFx;     // Note cut
                              Channels[Chn].FxData = TemFxData;
                              break;
                    case 0xD: if (TemEFxData > 0)
                              {
                                Channels[Chn].Fx = TemFx;
                                Channels[Chn].FxData = TemFxData;
                                Channels[Chn].FxStart = 1+TemEFxData;
                              } else Channels[Chn].Fx = 255;
                              break;
                    default: break;
                  }
                  break;
        case 0xF: Channels[Chn].Fx = TemFx;           // Set speed
                  Channels[Chn].FxData = TemFxData;
                  if ((TemFxData <= 32) || (VBlank == true))
                  {
                    NSpeed = TemFxData;
                    Speed = TemFxData;
                  } else {
                           Tempo = TemFxData;
                           if (Tempo < 20) Tempo = 20; // Shall I put 50 there ?
                           IntRate = 1193182 / (Tempo*4 / 10);
if (IntRate < 12500) IntRate = 12500;
                           SetTimer(IntRate);
                         }
                  break;
        default: Channels[Chn].Fx = 255;
                 Channels[Chn].FxData = 0;
                 break;
      }
    } else Channels[Chn].NoFx = 0;
  }
}

void DoTemFx(void)
{
  unsigned char Chn, TemFx, TemFxData, TemEFx, TemEFxData, B;
  unsigned short Per, W;
  unsigned char S;

  for (Chn=0; Chn<Header.Chns; Chn++)
  {
    if (Channels[Chn].On == 1)
    {
      if (Channels[Chn].FxStart > 0) Channels[Chn].FxStart--;
      TemFx = Channels[Chn].Fx;
      TemFxData = Channels[Chn].FxData;
      if ( (Channels[Chn].On == 1) && (Channels[Chn].FxStart == 0) )
      {
        switch(TemFx)
        {
          case 0: switch(Channels[Chn].ArpeggioCnt % 3)        // Arpeggio
                  {
                    case 0: GUSSetFreq(Chn+1,
                            Periods[PeriodTable[Samples[Channels[Chn].Sample].FTune][Channels[Chn].Note-1]]);
                            break;
                    case 1: GUSSetFreq(Chn+1,
                            Periods[PeriodTable[Samples[Channels[Chn].Sample].FTune][Channels[Chn].Note+Channels[Chn].Arp1-1]]);
                            break;
                    case 2: GUSSetFreq(Chn+1,
                            Periods[PeriodTable[Samples[Channels[Chn].Sample].FTune][Channels[Chn].Note+Channels[Chn].Arp2-1]]);
                            break;
                    default: break;
                  }
                  Channels[Chn].ArpeggioCnt++;
                  break;
          case 0x1: Per = Channels[Chn].Per;         // Port up
                    Per -= TemFxData;
                    if (Per < MinPeriod) Per = MinPeriod;
                    Channels[Chn].Per = Per;
                    GUSSetFreq(Chn+1, Periods[Per]);
                    break;
          case 0x2: Per = Channels[Chn].Per;        // port down
                    Per += TemFxData;
                    if (Per > MaxPeriod) Per = MaxPeriod;
                    Channels[Chn].Per = Per;
                    GUSSetFreq(Chn+1, Periods[Per]);
                    break;
          case 0x3: if (Channels[Chn].Per <= Channels[Chn].DesPer)   // Port to
                    {
                      W = Channels[Chn].DesPer;
                      Per = Channels[Chn].Per;
                      Per += Channels[Chn].FxSl2;
                      if (Per > W) Per = W;
                      if (Per > MaxPeriod) Per = MaxPeriod;
                      if (Per < MinPeriod) Per = MinPeriod;
                      Channels[Chn].Per = Per;
                      GUSSetFreq(Chn+1, Periods[Per]);
                    } else {
                             W = Channels[Chn].DesPer;
                             Per = Channels[Chn].Per;
                             if ( Per-Channels[Chn].FxSl2 > Per ) Per = MinPeriod;
                               else Per -= Channels[Chn].FxSl2;
                             if (Per < W) Per = W;
                             if (Per < MinPeriod) Per = MinPeriod;
                             if (Per > MaxPeriod) Per = MaxPeriod;
                             Channels[Chn].Per = Per;
                             GUSSetFreq(Chn+1,Periods[Per]);
                           }
                    break;
          case 0x4: TemFxData = Channels[Chn].FxVib;
                    B = TemFxData & 15;
                    S = VibratoTable[Channels[Chn].VibWave][Channels[Chn].VibCnt];
                    S = (S * B) / 64;
                    W = Channels[Chn].Per + S;
                    if (W > MaxPeriod) W = MaxPeriod;
                    if (W < MinPeriod) W = MinPeriod;
                    B = TemFxData >> 4;
                    GUSSetFreq(Chn+1, Periods[W]);
                    Channels[Chn].VibCnt += B;
                    if (Channels[Chn].VibCnt > 63)
                      Channels[Chn].VibCnt = Channels[Chn].VibCnt - 64;
                    break;
          case 0x5: if (TemFxData & 15 > 0)           // volume slide
                    {                                  // slide down
                      B = Channels[Chn].Vol;
                      if (B-TemFxData >= 0) B -= TemFxData;
                        else B = 0;
                      if (B > 128) B = 0;
                      Channels[Chn].Vol = B;
                      Channels[Chn].Bar = B;
                      W = GUSVolume[B]*AmpliferVolume;
                      GUSSetVolume(Chn+1,W);
                    } else {                            // slide up
                             B = Channels[Chn].Vol;
                             B += (TemFxData >> 4);
                             if (B > 64) B = 64;
                             Channels[Chn].Vol = B;
                             Channels[Chn].Bar = B;
                             W = GUSVolume[B]*AmpliferVolume;
                             GUSSetVolume(Chn+1,W);
                           }
                    TemFxData = Channels[Chn].FxSl2;
                    if (Channels[Chn].Per < Channels[Chn].DesPer)
                    {                               // port to
                      W = Channels[Chn].DesPer;
                      Per = Channels[Chn].Per;
                      Per += TemFxData;
                      if (Per > W) Per = W;
                      if (Per > MaxPeriod) Per = MaxPeriod;
                      if (Per < MinPeriod) Per = MinPeriod;
                      Channels[Chn].Per = Per;
                      GUSSetFreq(Chn+1,Periods[Per]);
                    } else {
                             W = Channels[Chn].DesPer;
                             Per = Channels[Chn].Per;
                             if (Per-TemFxData > Per) Per = MinPeriod;
                               else Per -= TemFxData;
                             if (Per < W) Per = W;
                             if (Per < MinPeriod) Per = MinPeriod;
                             if (Per > MaxPeriod) Per = MaxPeriod;
                             Channels[Chn].Per = Per;
                             GUSSetFreq(Chn+1, Periods[Per]);
                           }
                    break;
          case 0x6: B = Channels[Chn].FxVib & 15;       // volume slide ?
                    S = VibratoTable[Channels[Chn].VibWave][Channels[Chn].VibCnt];
                    S = (S * B) / 64;
                    W = Channels[Chn].Per + S;
                    if (W > MaxPeriod) W = MaxPeriod;
                    if (W < MinPeriod) W = MinPeriod;
                    B = Channels[Chn].FxVib >> 4;
                    GUSSetFreq(Chn+1,Periods[W]);
                    Channels[Chn].VibCnt += B;
                    if (Channels[Chn].VibCnt > 63)
                      Channels[Chn].VibCnt = Channels[Chn].VibCnt - 64;
                    if (TemFxData & 15 > 0)
                    {                            // exp. slide down
                      B = Channels[Chn].Vol;
                      if (B-TemFxData >= 0) B -= TemFxData;
                        else B = 0;
                      if (B > 128) B = 0;
                      Channels[Chn].Vol = B;
                      Channels[Chn].Bar = B;
                      W = GUSVolume[B]*AmpliferVolume;
                      GUSSetVolume(Chn+1,W);
                    } else {                     // slide up
                             B = Channels[Chn].Vol;
                             B += TemFxData >> 4;
                             if (B > 64) B = 64;
                             Channels[Chn].Vol = B;
                             Channels[Chn].Bar = B;
                             W = GUSVolume[B]*AmpliferVolume;
                             GUSSetVolume(Chn+1,W);
                           }
                    break;
          case 0xA: if (TemFxData & 15 > 0)         // volume slide
                    {                                // slide down
                      B = Channels[Chn].Vol;
                      if (B < (TemFxData & 15)) B = 0;
                        else B -= (TemFxData & 15);
                      if (B > 128) B = 0;
                      Channels[Chn].Vol = B;
                      Channels[Chn].Bar = B;
                      W = GUSVolume[B]*AmpliferVolume;
                      GUSSetVolume(Chn+1,W);
                    } else {                         // slide up
                             B = Channels[Chn].Vol;
                             B += (TemFxData >> 4);
                             if (B > 64) B = 64;
                             Channels[Chn].Vol = B;
                             Channels[Chn].Bar = B;
                             W = GUSVolume[B]*AmpliferVolume;
                             GUSSetVolume(Chn+1,W);
                           }
                    break;
          case 0xE: TemEFx = TemFxData >> 4;
                    TemEFxData = TemFxData & 15;
                    switch(TemEFx)
                    {
                      case 0x9: B = Channels[Chn].Sample;
                                Channels[Chn].RetrigCnt--;
                                if (Channels[Chn].RetrigCnt == 0)
                                {
                                  GUSSetOfs(Chn+1,GUSAdress[B]+2);
                                  Channels[Chn].RetrigCnt = TemEFxData;
                                }
                                break;
                      case 0xC: if (TemEFxData == 0) GUSSetVolume(Chn+1,0);
                                  else {
                                         TemEFxData--;
                                         B = TemFxData;
                                         B = B & 0xF0;
                                         B = B | TemEFxData;
                                         Channels[Chn].FxData = B;
                                       }
                                break;
                      case 0xD: W = Channels[Chn].Sample;
                                if (Channels[Chn].On == 1)
                                {
                                  Channels[Chn].Hit = 1;
                                  GUSSetBalance(Chn+1, Channels[Chn].Pan);
                                  if (Samples[W].LoopEnd > 2)
                                    GUSPlayAll(Chn+1,8,GUSAdress[W]+2,
                                               GUSAdress[W]+Samples[W].LoopStart,
                                               GUSAdress[W]+Samples[W].LoopEnd,
                                               Periods[Channels[Chn].Per],
                                               GUSVolume[Channels[Chn].Vol]*AmpliferVolume);
                                   else GUSPlayAll(Chn+1,0,GUSAdress[W]+2,
                                                   GUSAdress[W],
                                                   GUSAdress[W]+Samples[W].Length+1,
                                                   Periods[Channels[Chn].Per],
                                                   GUSVolume[Channels[Chn].Vol]*AmpliferVolume);
                                }
                                break;
                    default: break;
                    }
                    break;
          default: break;
        }
      }
    }
  }
}

void UpDateNotes(void)
{
  int N;

  if (CurrentPat > Header.Length-1) NewPattern = 0;
  CurrentPat = NewPattern;
  CurrentRow = NewRow;
  if ((CurrentTick >= Speed) && (Speed > 0))
  {
      Speed = NSpeed;
      CurrentTick = 0;
      if (Jump == 0) CurrentRow++;
      if (CurrentRow > 63)
      {
        CurrentPat++;
        CurrentRow = 0;
        if (CurrentPat > Header.Length-1)
        {
          NewPattern = 0;
          CurrentPat = 0;
        }
      }
  }

  for (N=0; N<MaxChannels; N++)
    if (Channels[N].Bar > 1) Channels[N].Bar -= 2;
      else Channels[N].Bar = 0;

  NewPattern = CurrentPat;
  NewRow = CurrentRow;
  if (Speed > 0)
  {
    CurrentTick++;
    if (CurrentTick == 1)
    {
      GeMODNotes();
      if (inp(0x3DA) & 8 == 8) VrtFlag = 1;
      GetTemFx();
    }
    if (inp(0x3DA) & 8 == 8) VrtFlag = 1;
    DoTemFx();
    if (inp(0x3DA) & 8 == 8) VrtFlag = 1;
  }
}

void PollPlayer(void)
{
  TimeCounter2++;
  UpDateNotes();
}

void __interrupt __far MODInt()
{
  _STI_();
  if (inp(0x3DA) & 8 == 8) VrtFlag = 1;
  TimeCounter2++;
  UpDateNotes();
  OldIntTick = IntTick;
  IntTick += IntRate;
  if (OldIntTick > IntTick)
  {
    TimeCounter++;
    _chain_intr( prev_int_8 );
  } else outp(0x20, 0x20);
}

void InitModule(void)
{
  unsigned int N,I,L;

  VrtFlag = 0;
  for (N=10; N<1010; N++)
  {
    Periods[N] = 586580935 / (N*7056);      // Hz = 7093789.2/(Per*2)
  }
  for (N=0; N<256; N++) Orders[N] = 0;
  for (N=0; N<MaxChannels; N++)
  {
    Channels[N].Vol = 64;
    Channels[N].Per = 0;
    Channels[N].DesPer = 0;
    Channels[N].Sample = 0;
    Channels[N].Pan = 7;
    Channels[N].Note = 0;
  }
  for (N=0; N<32; N++)
  {
    Samples[N].Addr = 0;
    for (I=0; I<=sizeof(Samples[N].Name); I++) Samples[N].Name[I] = 0;
    Samples[N].Length = 0;
    Samples[N].LoopStart = 0;
    Samples[N].LoopEnd = 0;
    Samples[N].FTune = 0;
    Samples[N].Volume = 0;
  }
  for (N=0; N<=128; N++) Patterns[N] = NULL;
  for (N=1; N<15; N++)
  {
    GUSSetVolume(N,0);
    GUSStopVoice(N);
  }
  CurrentPat = 0;
  CurrentRow = 0;
  NewPattern = 0;
  NewRow = 0;
  CurrentTick = 0;
  for (N=0; N<32; N++) GUSPoke(N,0);
  TopAdress = 16;
  GUSBank = 0;
  VBlank = false;
  prev_int_8 = _dos_getvect( 0x8 );
}

void StopPlaying(void)
{
  int N;

  IntRate = 65535;
  SetTimer(65535);
  _dos_setvect( 0x8, prev_int_8 );
  for (N=1; N<=MaxChannels; N++) GUSStopVoice(N);
}

void StartPlaying(void)
{
  int N;

  for (N=0; N<MaxChannels; N++)
  {
    Channels[N].Vol = 64;
    Channels[N].Per = 428;
    Channels[N].Sample = 0;
    Channels[N].Pan = 7;
    Channels[N].On = 1;
    Channels[N].DesPer = 428;
    Channels[N].Bar = 0;
    Channels[N].Fx = 255;
    Channels[N].FxData = 0;
    Channels[N].FxSl2 = 0;
    Channels[N].FxVib = 0;
    Channels[N].VibCnt = 0;
    Channels[N].VibWave = 0;
    Channels[N].Note = 0;
    Channels[N].Hit = 0;
    Channels[N].NoFx = 0;
    Channels[N].FxStart = 0;
    Channels[N].Arp1 = 0;
    Channels[N].Arp2 = 0;
    Channels[N].ArpeggioCnt = 0;
  }
  Speed = 6;
  NSpeed = 6;
  Tempo = 125;
  Channels[0].Pan = 7-DefaultPanning;
  Channels[1].Pan = 8+DefaultPanning;
  Channels[2].Pan = 8+DefaultPanning;
  Channels[3].Pan = 7-DefaultPanning;
  if (MaxChannels > 4)
    for (N=4; N<MaxChannels; N++)
      Channels[N].Pan = Channels[N-4].Pan;
  if (MaxChannels > 8)
    for (N=8; N<MaxChannels; N++)
      Channels[N].Pan = Channels[N-8].Pan;
  Jump = 0;
  IntTick = 0;
  CurrentPat = 0;
  CurrentRow = 0;
  NewPattern = 0;
  NewRow = 0;
  CurrentTick = 0;
  TimeCounter = 0;
  TimeCounter2 = 0;
  _dos_setvect( 0x8, MODInt );
  IntRate = 1193182 / 50;
if (IntRate <12500 ) IntRate = 12500;
  SetTimer(IntRate);
}

void Set_Up_MODHeader(FILE *f)
{
  int Chn, i;

  Header.Samples = 31;
  memmove(Header.Name, TempBuff, 20);
  memset(Header.Tag, 0x20, 4);
  memmove(Header.Tag, PTempBuff + 540, 4);
  Header.Tag[4] = 0;

  Chn = MaxChannels;

  if ( (strcmp(Header.Tag, TagStrs[0]) == 0) ||
       (strcmp(Header.Tag, TagStrs[1]) == 0) ||
       (strcmp(Header.Tag, TagStrs[2]) == 0) ||
       (strcmp(Header.Tag, TagStrs[3]) == 0) ) Chn = 4;
   else if ( (strcmp(Header.Tag, TagStrs[4]) == 0) ||
             (strcmp(Header.Tag, TagStrs[5]) == 0) ||
             (strcmp(Header.Tag, TagStrs[6]) == 0) ) Chn = 8;
         else if (strcmp(Header.Tag, TagStrs[7]) == 0) Chn = 6;
               else if (strcmp(Header.Tag, TagStrs[8]) == 0) Chn = 10;
                      else if (strcmp(Header.Tag, TagStrs[9]) == 0) Chn = 12;
                      else if (strcmp(Header.Tag, TagStrs[10]) == 0) Chn = 14;
                      else if (strcmp(Header.Tag, TagStrs[11]) == 0) Chn = 16;
                      else if (strcmp(Header.Tag, TagStrs[12]) == 0) Chn = 18;
                      else if (strcmp(Header.Tag, TagStrs[13]) == 0) Chn = 20;
                      else if (strcmp(Header.Tag, TagStrs[14]) == 0) Chn = 22;
                      else if (strcmp(Header.Tag, TagStrs[15]) == 0) Chn = 24;
                      else if (strcmp(Header.Tag, TagStrs[16]) == 0) Chn = 26;
                      else if (strcmp(Header.Tag, TagStrs[17]) == 0) Chn = 28;
                      else if (strcmp(Header.Tag, TagStrs[18]) == 0) Chn = 30;
                      else if (strcmp(Header.Tag, TagStrs[19]) == 0) Chn = 32;
                     else {
                            Header.Samples = 15;
                            Chn = 4;
                          }
  if (Chn > MaxChannels)
  {
    ModError = 1;
    return;
  }
  if (Header.Samples == 15)
  {
    for (i=0; i<128; i++)
      Orders[i] = TempBuff[472 + i];
                       //  memmove(Orders, PTempBuff + 236, 128);
    fseek(f, 600, SEEK_SET);
    Header.Length = TempBuff[470];
    Header.Chns = 4;
  } else {
            Header.Length = TempBuff[950];
            for (i=0; i<128; i++)
              Orders[i] = TempBuff[952 + i];
                       //  memmove(Orders, PTempBuff + 476,128);
         }
  Header.Chns = Chn;
}

void MOD_Sample_Info(void)
{
  int N, i;

  for (N=0; N<32; N++)
  {
    memset(Samples[N].Name, 0, sizeof(Samples[N].Name));
    Samples[N].Length = 0;
    Samples[N].FTune = 0;
    Samples[N].Volume = 0;
    Samples[N].LoopStart = 0;
    Samples[N].LoopEnd = 0;
  }
  for (N=0; N<Header.Samples; N++)
  {
    for (i=0; i<22; i++)
      Samples[N+1].Name[i] = TempBuff[N*30+20 + i];
                   //  memmove(Samples[N].Name, PTempBuff + N*15+10, 22);
    Samples[N+1].Name[23] = 0;
    Samples[N+1].Length = 2*swap(*(PTempBuff + N*15+21));    // N*30+42
    Samples[N+1].FTune = TempBuff[N*30+44];
    Samples[N+1].Volume = TempBuff[N*30+45];
    Samples[N+1].LoopStart = 2*swap(*(PTempBuff + N*15+23)); // N*30+46
    Samples[N+1].LoopEnd = 2*swap(*(PTempBuff + N*15+24));   // N*30+48
    if (Samples[N+1].LoopEnd < 3)
    {
      Samples[N+1].LoopEnd = 0;
      Samples[N+1].LoopStart = 0;
    }
    Samples[N+1].LoopEnd += Samples[N+1].LoopStart;
    if (Samples[N+1].LoopEnd > Samples[N+1].Length)
      Samples[N+1].LoopEnd = Samples[N+1].Length;
  }
}

void Read_Ptn(FILE *f, short N)
{
  int Row, Note;
  unsigned short W,W2,I;
  unsigned char B, MChn;

  MChn = Header.Chns;
  fread(TempBuff, 256*MChn, 1, f);
  for (Row=0; Row<64; Row++)
  {
    for (Note=0; Note<MChn; Note++)                   // 0000000000000000 ?
    {
      W = *(PTempBuff + Row*(2*MChn)+Note*2);    // *2
      W2 = *(PTempBuff + Row*(2*MChn)+Note*2+1);
      (*Patterns[N])[Row][Note].Per = (((W & 0xF) << 8) | (W >> 8)); // & 0xFFF;
      (*Patterns[N])[Row][Note].Sample = ((W2 >> 4) & 0xF) | (W & 0xF0);
      (*Patterns[N])[Row][Note].Fx = W2 & 0xF;     // lo(w2) and 15
      (*Patterns[N])[Row][Note].FxData = _HI_(W2); // hi(w2)
      I = (*Patterns[N])[Row][Note].Per;
      W = B = 0;
      while (B == 0)
      {
        W++;
        if ( (W > 48) || (I == PeriodTable[0][W-1])) B = 1;
      }
      if (W <= 48) (*Patterns[N])[Row][Note].Note = W;
        else (*Patterns[N])[Row][Note].Note = 0;
    }
  }
}

void Load_Patterns(FILE *f)
{
  int Num_Ptn, N;

  Num_Ptn = 0;
  for (N=0; N<128; N++)
    if (Orders[N] > Num_Ptn)
    {
      if (Orders[N] > 127)
      {
        ModError = 2;
        return;
      } else Num_Ptn = Orders[N];
    }
  MaxPatterns = Num_Ptn+1;
  for (N=0; N<MaxPatterns; N++)
  {
    if (DOS_maxavail() > 6*64*MaxChannels)
    {
      Patterns[N] = (MODPattern __far *)DOS_malloc(6*64*MaxChannels);
      WhereIsPat[N] = 1;
    } else {
              Patterns[N] = (MODPattern __far *)malloc(6*64*MaxChannels); // sizeof(MODPattern));
              WhereIsPat[N] = 0;
           }
    Read_Ptn(f, N);
  }
}

void Load_Sample(FILE *f, int Num)
{
  unsigned short W, Fl, L;
  int Len;

  GUSPoke(TopAdress, 0);
  TopAdress++;
  GUSPoke(TopAdress,0);
  TopAdress++;

  Len = Samples[Num].Length + TopAdress;
  if ( (Len > GUSBank+0x40000) && (TopAdress < GUSBank+0x40000) )
  {
    GUSBank = GUSBank+0x40000;
    TopAdress = GUSBank;
  }
  Samples[Num].Addr = TopAdress;
  GUSAdress[Num] = TopAdress;
  if (Samples[Num].Length < 1) return;
  Fl = Samples[Num].Length / 4096;
  L = Samples[Num].Length % 4096;
  if (Fl > 0)
  {
    for (W=0; W<Fl; W++)
    {
      fread(TempBuff, 4096, 1, f);
      Load2GUS(4096, TempBuff);                       // Block = 4kb's ?
    }
  }
  if (L > 0)
  {
    fread(TempBuff, L, 1, f);
    Load2GUS(L, TempBuff);
  }
  if (Samples[Num].LoopEnd > 2)
  {
    GUSPoke(TopAdress,GUSPeek(GUSAdress[Num]+Samples[Num].LoopStart));
    GUSPoke(GUSAdress[Num]+Samples[Num].LoopEnd+1,
            GUSPeek(GUSAdress[Num]+Samples[Num].LoopStart));
    TopAdress++;
  }
}

void LoadModule(char *S, int Ofst, boolean Debug)
{
  FILE *f;
  int I;

  PTempBuff = (unsigned short *)TempBuff;
  InitModule();
  GUSBank = 0;
  f = fopen(S, "rb");
  fseek(f, Ofst, SEEK_SET);
  fread(TempBuff, 1084, 1, f);                           // MOD Header
/*
  I:=IOresult;
  if I<>0 then
    Begin
      ModError:=2;
      exit;
    end;
*/
  Set_Up_MODHeader(f);
  if (ModError != 0) return;
  MOD_Sample_Info();
  if (Debug) printf("þ Loading Patterns Data into memory...");
  Load_Patterns(f);
  if (Debug) printf("done.\n");
  if (ModError != 0) return;
  if (Debug) printf("þ Loading Samples Data into memory...");
  for (I=0; I<32; I++) Load_Sample(f, I);
  if (Debug) printf("done.\n");
  fclose(f);
}

void FreeModule(void)
{
  int N,I;

  for (N=MaxPatterns-1; N>=0; N--)
    if ( WhereIsPat[N] == 1 ) DOS_free(Patterns[N]);
      else free((MODPattern __near *)Patterns[N]);
  TopAdress = 16;
  for (N=0; N<32; N++)
  {
    Samples[N].Addr = 0;
    for (I=0; I<=sizeof(Samples[N].Name); I++) Samples[N].Name[I] = 0;
    Samples[N].Length = 0;
    Samples[N].LoopStart = 0;
    Samples[N].LoopEnd = 0;
    Samples[N].FTune = 0;
    Samples[N].Volume = 0;
  }
  GUSBank = 0;
}
