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

/* EDACS control channel monitoring program... */


/* Scanner / system specific stuff needed for "trunk tracking" mode   */

static double baudrate = 1200.0;        /* baud rate of computer to scanner link */

/* This is the table equating logical channel numbers with actual     */
/* frequencies. The first entry, chan[0], gives the frequency for     */
/* logical channel number 1; chan[1] give the frequency for LCN 2,    */
/* et cetera. You will need to change this table to match those       */
/* systems in your own area unless you happen to live in Huntsville   */
/* Alabama and want to monitor this 5 channel commercial SMR trunk    */
static double chan[30] = {
  856.0375, 857.0375, 858.0375, 859.0375, 860.0375
};

/* this identifies the control channel in the above table             */
int control = 1;                /* this number is the lcn of the control channel - 1 */
int nucha = 20;                 /* number of channels in system; for the above       */
                 /* system it should be set to five, but it was put to */
                 /* 20 for people who don't want to change the default */


/*                    global variables                                */
int lc = 0;
int fobp = 0;                   /* pointer to current position in array fob           */
char ob[1000];                  /* buffer for raw data frames                         */
int obp = 0;                    /* pointer to current position in array ob            */
int invfla = 0;                 /* bit inversion flag                                 */
static int comport = 0x3f8;     /* serial port base address; set in main */
static int sport = 1;           /* serial port number to use            */

static int dotting = 0;         /* dotting sequence detection flag      */
static int mode = 0;            /* mode flag; 0 means we're on control  */
                              /* channel; 1 means active frequency    */

/* array for raw data coming off the serial port		      */
static unsigned int buflen = 15000;     /* length of data buffer      */
static volatile unsigned int cpstn = 0; /* current position in buffer */
static volatile unsigned int fdata[15001];      /* timing data array      */

void interrupt(*oldfuncc) ();   /* vector to old com port interrupt    */


/*--------------------------------------------------------------------*/
/*           A BUNCH OF LOW LEVEL STUFF FOLLOWS                       */
/*--------------------------------------------------------------------*/
/**********************************************************************/
/*                            comint                                  */
/*                                                                    */
/*               this is serial com port interrupt                    */
/* we assume here that it only gets called when one of the status     */
/* lines on the serial port changes (that's all you have hooked up).  */
/* All this handler does is find the number of system timer ticks     */
/* since the last call and stores it in the fdata array. The MSB      */
/* is set to indicate whether the status line is zero. In this way    */
/* the fdata array is continuously updated with the appropriate       */
/* length and polarity of each data pulse for further processing by   */
/* the main program.						      */
void interrupt comint()
{
  static unsigned int d1, d2, ltick, tick, dtick;

  /* the system timer is a 16 bit counter whose value counts down     */
  /* from 65535 to zero and repeats ad nauseum. For those who really  */
  /* care, every time the count reaches zero the system timer         */
  /* interrupt is called (remember that thing that gets called every  */
  /* 55 milliseconds and does housekeeping such as checking the       */
  /* keyboard.                                                        */
  outportb(0x43, 0x00);         /* latch counter until we read it      */
  d1 = inportb(0x40);           /* get low count                       */
  d2 = inportb(0x40);           /* get high count                      */

  /* get difference between current, last counter reading             */
  tick = (d2 << 8) + d1;
  dtick = ltick - tick;
  ltick = tick;

  /* set MSB to reflect state of input line */
  if ((inportb(comport + 6) & 0xF0) > 0)
    dtick = dtick | 0x8000;
  else
    dtick = dtick & 0x3fff;

  fdata[cpstn] = dtick;         /* put freq in fdata array             */
  cpstn++;                      /* increment data buffer pointer       */
  if (cpstn > buflen)
    cpstn = 0;                  /* make sure cpstn doesnt leave array  */

  d1 = inportb(comport + 2);    /* clear IIR                           */
  d1 = inportb(comport + 5);    /* clear LSR                           */
  d1 = inportb(comport + 6);    /* clear MSR                           */
  d1 = inportb(comport);        /* clear RX                            */
  outportb(0x20, 0x20);         /* this is the END OF INTERRUPT SIGNAL */
}

/************************************************************************/
/*                  SERIAL PORT INITIALIZATION                          */
/************************************************************************/
/* basic purpose: enable modem status interrupt and set serial port     */
/*		  output lines to supply power to interface             */
void set8250(double bps)
{                               /* sets up the 8250 UART               */
  static unsigned int t, dv, tp;

  dv = (int) 1843200.0 / (16.0 * bps);
  /* how to configure your serial port setup:          */
  /* do you want two stop bits? then make sure you set bit 2 in tp */
  /* do you want a parity bit generated? then set bit bit 3 in tp  */
  /* do you want even parity? then set bit 4 in tp                 */
  /* do you want an 8 bit word length? set bits 0 and 1 in tp      */
  /* do you want an 7 bit word length? set bit  1 in tp            */
  tp = 0x80 + 0x02 + 0x01;
  outportb(comport + 3, tp);    /* set line control register  */
  outport(comport, dv);         /* output brg divisor latch   */
  tp = tp & 0x7f;               /* switch out brg divisor reg */
  outportb(comport + 3, tp);


  outportb(comport + 1, 0x08);  /*  enable MODEM STATUS INTERRUPT      */
  outportb(comport + 4, 0x0a);  /*  push up RTS, DOWN DTR              */
  t = inportb(comport + 5);     /*  clear LSR                          */
  t = inportb(comport);         /*  clear RX                           */
  t = inportb(comport + 6);     /*  clear MSR                          */
  t = inportb(comport + 2);     /*  clear IID                          */
  t = inportb(comport + 2);     /*  clear IID - again to make sure     */
}

/* this routine allows the RTS output line to be set either high or   */
/* low depending on whether sta = 0.                                  */
void rts_state(int sta)
{
  static int rv;
  rv = inportb(comport + 4);
  rv = rv & 0x01;               /* save DTR state                */
  rv = rv | 0x08;               /* enable interrupt to reach PIC */
  if (sta != 0)
    rv = rv | 0x02;
  outportb(comport + 4, rv);
}

/* this routine allows the DTR output line to be set either high or   */
/* low depending on whether sta = 0.                                  */
void dtr_state(int sta)
{
  static int rv;
  rv = inportb(comport + 4);
  rv = rv & 0x02;               /* save RTS state                     */
  rv = rv | 0x08;               /* make sure interrupts can reach PIC */
  if (sta != 0)
    rv = rv | 0x01;
  outportb(comport + 4, rv);
}


/************************************************************************/
/*                         send_char                                    */
/*                                                                      */
/* This routine sends a character out on the serial port TxD line.      */
/* It makes sure the previous character has been completely sent and    */
/* then sends puts the current character into the transmit register.    */
/************************************************************************/
void send_char(int c)
{
  /* wait for transmit reg to empty */
  while ((inportb(comport + 0x05) & 0x20) == 0x00);

  /* send character */
  outportb(comport, c);
}



/************************************************************************/
/*                         set_freak                                    */
/*                                                                      */
/* This routine must be written by the user for his/her specific radio. */
/* The input is the desired frequency the scanner should go to.         */
/* Characters are sent to the scanner using the send_char routine.      */
/************************************************************************/
void set_freak(double freq)
{
  /* put your code here */
  /* call send_char() to send a byte to your scanner */

}



/************************************************************************/
/*                    TIMER CHIP INITIALIZATION                         */
/************************************************************************/
/* purpose: make sure computer timekeeper is set up properly. This      */
/*          routine probably isn't necessary - it's just an insurance   */
/*	    policy.                                                     */
void set8253()
{                               /*  set up the 8253 timer chip         */
  /* NOTE: ctr zero, the one we are using */
  /*  is incremented every 840nSec, is   */
                                                                        /*  main system time keeper for dos    */ outportb(0x43, 0x34);
                                                                        /* set ctr 0 to mode 2, binary         */
  outportb(0x40, 0x00);         /* this gives us the max count         */
  outportb(0x40, 0x00);
}

/**********************************************************************/
/*               higher level stuff follows                           */
/**********************************************************************/

/* structure for storing the control channel data frame information  */
struct ccdat {
  int cmd;                      /* command */
  int lcn;                      /* logical channel number */
  int stat;                     /* status bits */
  int id;                       /* agency/fleet/subfleet id */
  int bad;                      /* CRC check flag - when set to 1 it means data is corrupt */
};

/* array acn[] indicates which channels are currently active; aid[]  */
/* indicates which id is using the active channel                    */
/* acn[] is actually a countdown timer decremented each time a       */
/* control channel command is received and is reset whenever that    */
/* channel appears in control channel data stream. When this reaches */
/* zero the channel is assumed to have become inactive.              */
/* aid[] is used to make sure a new group appearing on an active     */
/* channel is recognized even if acn[] had not yet reached zero      */
int acn[33], aid[33], nacn = 0;

/************************************************************************/
/*                          show_setup                                  */
/*                                                                      */
/* Purpose: setup things for screen display 		                */
/************************************************************************/
void show_setup()
{
  static int i;
  clrscr();
  for (i = 0; i < 33; i++) {
    acn[i] = 0;
    aid[i] = 0xffff;
  }
  gotoxy(1, 1);
  textcolor(YELLOW);
  cprintf("LCN  ID  ST    COMMAND");
  gotoxy(1, 2);
  cprintf("--- ---- ---  ---------");
  textcolor(LIGHTGRAY);
  gotoxy(65, 2);
  cprintf("Using COM%1i", sport);
  gotoxy(63, 3);
  cprintf("# channels: %2i", nucha);
  gotoxy(60, 4);
  cprintf("Exit: Space or Esc ");
  gotoxy(60, 6);
  cprintf("Misc Control data");
  gotoxy(60, 7);
  cprintf("LCN   ID  ST  CMD");
  gotoxy(75, 1);
  textcolor(WHITE);
  cprintf("-");
  nacn = 0;
}

/************************************************************************/
/*                          show_active                                 */
/*                                                                      */
/* this routine gets called ONCE whenever a new ID becomes active on    */
/* a given channel number                                               */
/************************************************************************/
void show_active(struct ccdat info)
{
  static int linx, liny, cm;

  /* this is where you will want to insert some code that decides if   */
  /* you want to switch to this particular active frequency. Right     */
  /* now this commented out code fragment would have sent your scanner */
  /* off to any active frequency.                                      */
  /* you'll probably also want to filter out data channel assignemnts  */
/*  set_freak(chan[info.lcn-1]);    */
/*  mode = 1;                       */
/*  dotting = 0;                    */

  linx = 1;
  liny = 2 + info.lcn;

  if (liny > 22) {
    linx = 31;
    liny -= 20;
  }

  gotoxy(linx, liny);
  cm = info.cmd;
  cprintf("%2i:  %03X  %01X   %02X ", info.lcn, info.id, info.stat, cm);
  textcolor(LIGHTGRAY);
  if (cm == 0xEE)
    cprintf("VOICE");
  else if (cm == 0xEC)
    cprintf("PHONE");
  else if (cm == 0xF6)
    cprintf("VOICE");
  else if (cm == 0xA0)
    cprintf("DATA");
  else if (cm == 0xA1)
    cprintf("DATA");
  textcolor(WHITE);
}

/************************************************************************/
/* show_inactive gets called when a channel is no longer active         */
/* it simply writes out a bunch of spaces to erase the old              */
/* information                                                          */
/************************************************************************/
void show_inactive(int lcn)
{
  static int linx, liny;
  linx = 4;
  liny = 2 + lcn;

  if (liny > 22) {
    linx = 34;
    liny -= 20;
  }


  gotoxy(linx, liny);
  aid[lcn] = 0xffff;
  cprintf("                    ");

}

/* scroll raw commands on window in right hand side of display */
void show_raw(struct ccdat inf)
{
  static int lc = 8;
  if (lc == 24)
    movetext(60, 9, 79, 24, 60, 8);
  else
    lc++;
  gotoxy(61, lc);
  cprintf("%02X  %03X   %01X   %02X", inf.lcn, inf.id, inf.stat, inf.cmd);
}

/************************************************************************/
/*                        proc_cmd                                      */
/*                                                                      */
/* This routine figures out when a group becomes active on a new        */
/* channel and when a channel is has become inactive. This info is      */
/* passed to the two routines above which show the changes on the       */
/* screen                                                               */
/************************************************************************/
void show(struct ccdat info)
{
  static int idup = 0, cdup = 0, i, aid[8], sysid = 0x0000;
  static char dup[4] = { 45, 47, 124, 92 };

  /* update the "I'm still receiving data" spinwheel character on screen
   */ cdup++;
  if (cdup > 9) {
    gotoxy(75, 1);
    idup = (idup + 1) & 0x03;
    putch(dup[idup]);
    cdup = 0;
  }

  /* process only good information blocks  */
  if (info.bad == 0) {
    /* try to display all non idle information blocks */
    if (info.cmd < 0x80) {
      show_raw(info);
    } else if (((info.cmd >> 1) != 0x7E)) {
      if ((info.lcn > 0) & (info.lcn <= nucha)) {
        /* check to see if ID is in active list... if not it must be new */
        if (aid[info.lcn] != info.id) {

/*        if (aid[info.lcn] != 0xffff) show_inactive(info.lcn); */
          /* a new ID has become active */
          show_active(info);
          /* add to activity list */
          aid[info.lcn] = info.id;
        }
        /* update activity timer for this active channel */
        acn[info.lcn] = 8 + (nacn << 2);
      }
    } else if (info.cmd == 0xFD) {
      /* look at background / idle stuff to find system id */
      if (sysid != info.id) {
        sysid = info.id;
        textcolor(YELLOW);
        gotoxy(35, 1);
        cprintf("SYS ID: %03X", sysid);
        textcolor(WHITE);
      }
    }
  }

  /* see if any channel numbers have dropped off */
  /* this is done by waiting for a certain number of control channel */
  /* commands to go by before assuming the channel is no longer      */
  /* active.                                                         */
  nacn = 0;                     /* nacn holds the number of active channels */
  for (i = 1; i <= nucha; i++) {
    if (acn[i] != 0) {
      nacn++;
      acn[i]--;
      if (acn[i] == 0) {
        /* LCN has become inactive */
        show_inactive(i);
        aid[i] = 0xffff;

      }
    }
  }

}

/************************************************************************/
/*                        proc_cmd                                      */
/*                                                                      */
/* This routine processes a data frame by checking the CRC and nicely   */
/* formatting the resulting data into structure info                    */
/************************************************************************/
void proc_cmd(int c)
{
  static int ecc = 0, sre = 0, cc = 0, ud = 0xA9C, nbb = 0, tb, orf, i;
  static char oub[50];
  static struct ccdat info;
  if (c < 0) {
    cc = 0;
    sre = 0x7D7;
    ecc = 0x000;
    oub[28] = 0;

    /* pick off, store  command   (eight bits) */
    for (i = 0; i <= 7; i++) {
      orf = orf << 1;
      orf += oub[i];
    }
    orf = orf & 0xff;
    info.cmd = orf;

    /* pick off LCN   (five bits)  */
    for (i = 8; i <= 12; i++) {
      orf = orf << 1;
      orf += oub[i];
    }
    orf = orf & 0x1f;
    info.lcn = orf;

    /* pick off four status bits */
    for (i = 13; i <= 16; i++) {
      orf = orf << 1;
      orf += oub[i];
    }
    orf = orf & 0x0f;
    info.stat = orf;

    /* pick off 11 ID bits    */
    for (i = 17; i <= 27; i++) {
      orf = orf << 1;
      orf += oub[i];
    }
    orf = orf & 0x07ff;
    info.id = orf;

    if (nbb == 0)
      info.bad = 0;
    else
      info.bad = 1;

    if (nbb == 0) {
      show(info);
    }

    nbb = 0;
  } else {

    cc++;
    /* bits 1 through 28 will be run through crc routine */
    if (cc < 29) {
      oub[cc - 1] = c;
      if (c == 1)
        ecc = ecc ^ sre;
      if ((sre & 0x01) > 0)
        sre = (sre >> 1) ^ ud;
      else
        sre = sre >> 1;
    } else {
      /* for the rest of the bits - check if they match calculated crc */
      /* and keep track of the number of wrong bits in variable nbb    */
      if ((ecc & 0x800) > 0)
        tb = 1;
      else
        tb = 0;
      ecc = (ecc << 1) & 0xfff;
      if (tb != c)
        nbb++;
    }
  }
}

/************************************************************************/
/*                        proc_frame                                    */
/*                                                                      */
/* This routine processes the two raw data frames stored in array ob[]. */
/* Each data frame is repeated three times with the middle repetition   */
/* inverted. So bits at offsets of 0, 40, and 80 should all be carrying */
/* the same information - either 010 or 101 if no errors have occured). */
/* Error correction is done by ignoring any single wayward bit in each  */
/* triplet. For example a 000 triplet is assumed to have actually been  */
/* a 010; a 001 -> 101; 011 -> 010; et cetera. Array tal[] holds a table*/
/* giving the "corrected" bit value for every possible triplet. Two     */
/* or three wrong bits in a triplet cannot be corrected. The resulting  */
/* data bits are send on the proc_cmd routine for the remaining         */
/* processing.                                                          */
/************************************************************************/
void proc_frame()
{
  static int i, l;
  static int tal[9] = { 0, 1, 0, 0, 1, 1, 0, 1 };

                                                /* do the first data frame */ proc_cmd(-1);
                                                /* reset proc_cmd routine
                                                 */
  for (i = 0; i < 40; i++) {
    l = (int) ((ob[i] << 2) + (ob[i + 40] << 1) + ob[i + 80]);  /* form triplet */
    l = tal[l];                 /* look up the correct
                                   bit value in the table */
    proc_cmd(l);                /* send out bit */
  }

  /* do the second data frame */
  proc_cmd(-2);
  for (i = 0; i < 40; i++) {
    l = (int) ((ob[i + 120] << 2) + (ob[i + 160] << 1) + ob[i + 200]);
    l = tal[l];
    proc_cmd(l);
  }

}


/************************************************************************/
/*                       frame_sync					*/
/*									*/
/* This routine takes the raw bit stream and tries to find the 48 bit   */
/* frame sync sequence. When found it stores the next 240 raw data bits */
/* that make up a data frame and stores them in global array ob[].      */
/* Routine proc_frame is then called to process the data frame and the  */
/* cycle starts again. When mode = 1 it will only look for the dotting  */
/* sequence and updat the dotting flag.                                 */
/************************************************************************/
void frame_sync(char gin)
{
  static int sr0 = 0, sr1 = 0, sr2 = 0, hof = 300, xr0, xr1, xr2, i, nh =
    0, ninv = 0;
  static int fsy[3][3] = {
    0x1555, 0x5712, 0x5555,     /* control channel frame sync            */
    0x5555, 0x5555, 0x5555,     /* dotting sequence                      */
    0xAAAA, 0xAAAA, 0x85D3      /* data channel frame sync (not used)    */
  };

  /* update registers holding 48 bits */
  sr0 = sr0 << 1;
  if ((sr1 & 0x8000) != 0)
    sr0 = sr0 ^ 0x01;
  sr1 = sr1 << 1;
  if ((sr2 & 0x8000) != 0)
    sr1 = sr1 ^ 0x01;
  sr2 = sr2 << 1;
  sr2 = sr2 ^ (int) gin;

  /* update ob array */
  if (obp < 600) {
    ob[obp] = gin;
    obp++;
  }

  /* find number of bits not matching sync pattern */
  xr0 = sr0 ^ fsy[mode][0];
  xr1 = sr1 ^ fsy[mode][1];
  xr2 = sr2 ^ fsy[mode][2];
  nh = 0;
  for (i = 0; i < 16; i++) {
    if ((xr0 & 0x01) > 0)
      nh++;
    xr0 = xr0 >> 1;
    if ((xr1 & 0x01) > 0)
      nh++;
    xr1 = xr1 >> 1;
    if ((xr2 & 0x01) > 0)
      nh++;
    xr2 = xr2 >> 1;
  }

  /* if there are less than 3 mismatches with sync pattern and we aren't
   *//* inside a data frame (hold-off counter less than 288) sync up  */
  if (nh < 4) {                 /* mode zero means we're monitoring control channel */
    if ((hof > 287) && (mode == 0)) {
      proc_frame();
      obp = 0;
      hof = 0;
      ninv = 0;
    }                           /* mode 1 means we're on voice channel and so we just found
                                   dotting */
    else if ((mode == 1) && (nh < 2)) {
      dotting++;
    }
  }

  /* check for polarity inversion - if all frame sync bits mismatch 12  */
  /* times in a row without ever getting a good match assume that one   */
  /* must invert the bits                                               */
  if ((nh == 48) && (mode == 0)) {
    ninv++;
    if (ninv > 12) {
      invfla ^= 0x01;
      gotoxy(65, 1);
      if (invfla == 1)
        cprintf("INVERT");
      else
        cprintf("      ");
      ninv = 0;
    }
  }

  if (hof < 1000)
    hof++;

}

/************************************************************************/
/*                         DISPLAY HELP SCREEN                          */
/************************************************************************/
void help()
{
  printf("\n  EDACS control channel monitoring program\n");
  printf("     Command line arguement summary\n\n");
  printf("  /NC:x       - set x to number of channels used in system you \n");
  printf("                are monitoring. Minimum value = 3; max = 31    \n");
  printf("  /COM:y      - set y = 1,2,3,4 to set com port you want to use.\n");
  printf("\nExample: if your program is called edacs.exe and you wish to  \n");
  printf("         monitor a 10 channel system using COM2 you should type \n");
  printf("         the following in at the DOS prompt:\n\n");
  printf("             EDACS /nc:10 /com:2\n");
  printf("\n Interface requirements: Hamcomm type data slicer circuit.\n");
  printf(" The program automatically determines the property polarity of \n");
  printf(" the incoming data. When everything is working properly you will\n");
  printf(" see the the character on the upper right hand corner of your \n");
  printf(" screen spinning around indicating that EDACS control channel \n");
  printf(" information is being successfully processed.\n\n");
  printf(" See text file for further details...\n\n");
  printf(" press any key to continue...\n");
  getch();
}


void main(int argc, char *argv[], char *env[])
{
  static unsigned int n, i = 0, j;
  static int irqv = 0x0c, ef = 0, ch;
  FILE *out;
  static char s = 48, temp[20];
  static double dt, exc = 0.0, clk = 0.0, xct, dto2;

  for (n = 1; n < argc; n++) {
    strcpy(temp, argv[n]);
    strupr(temp);
    j += sscanf(temp, "/NC:%i", &nucha);
    j += sscanf(temp, "/COM:%i", &sport);
    if (temp[1] == 'H')
      j = 30;
  }

  if (nucha > 31) {
    nucha = 31;
    j = 30;
  }
  if (nucha < 3) {
    nucha = 3;
    j = 30;
  }
  if (sport > 4) {
    sport = 4;
    j = 30;
  }
  if (sport < 1) {
    sport = 1;
    j = 30;
  }

  if ((j + 1) != argc)
    help();


  clrscr();

  /* dt is the number of expected clock ticks per bit */
  dt = 1.0 / (9600.0 * 838.22e-9);
  dto2 = 0.5 * dt;

  /* set up items related to serial port                                */
  n = inportb(0x21);
  if ((sport == 1) | (sport == 3)) {
    irqv = 0x0c;
    oldfuncc = getvect(irqv);   /* save COM  Vector                    */
    setvect(irqv, comint);      /* Capture COM  vector                 */
    outportb(0x21, n & 0xef);
    if (sport == 1)
      comport = 0x3f8;
    else
      comport = 0x3e8;
  } else {
    irqv = 0x0b;
    oldfuncc = getvect(irqv);   /* save COM  Vector                    */
    setvect(irqv, comint);      /* Capture COM  vector                 */
    outportb(0x21, n & 0xf7);
    if (sport == 2)
      comport = 0x2f8;
    else
      comport = 0x2e8;
  }

  set8253();                    /* set up 8253 timer chip              */

  set8250(baudrate);            /* set up 8250 UART                    */

  printf("Checking for data coming in on COM%i... \n", sport);
  printf("If program just sits here press any key to exit...\n");
  while ((cpstn < 3) & (kbhit() == 0));
  if (cpstn < 3) {
    printf("HEY - no data seems to be coming in over your interface.\n\n");
    outportb(0x21, n);          /* disable IRQ interrupt  */
    setvect(irqv, oldfuncc);    /* restore old COM Vector */
    exit(1);
  } else
    printf("Interface seems to work properly...\n\n");

  if (nucha > 31)
    nucha = 31;
  if (nucha < 3)
    nucha = 3;

  show_setup();

  while (ef == 0) {

    /* check if key has been hit */
    if (kbhit() != 0) {
      ch = getch();
      /* a space or Esc key press sets the exit flag */
      if ((ch == 32) | (ch == 27))
        ef = 1;
      else {
        /* any other key press returns the scanner to the control */
        /* channel if it had switched to an active channel        */
        set_freak(chan[control]);
        mode = 0;
        i = cpstn;
      }
    }

    if (i != cpstn) {
      s = (char) ((fdata[i] >> 15) ^ invfla);

      /* add in new number of cycles to clock  */ clk += (fdata[i] &
                                                          0x7fff);
      xct = exc + dto2;         /* exc is current boundary */
      while (clk >= xct) {
        frame_sync(s);
        clk = clk - dt;
      }                         /* clk now holds new
         boundary position. update exc slowly... *//* 0.005 sucks; 0.02 better;
         0.06 mayber even better; 0.05 seems pretty good */
      exc = exc + 0.050 * (clk - exc);

      i++;
      if (i > buflen)
        i = 0;

      /* If we are sitting on an active channel and the dotting sequence */
      /* is detected, we should switch back to the control channel       */
      if ((mode == 1) && (dotting > 0)) {
        set_freak(chan[control]);
        mode = 0;
        dotting = 0;
        i = cpstn;
      }
    }

  }

  outportb(0x21, n);            /* disable IRQ interrupt              */
  setvect(irqv, oldfuncc);      /* restore old COM Vector             */

  gotoxy(1, 23);
  printf("\n");
}


syntax highlighted by Code2HTML, v. 0.9.1