#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <dir.h>
#include <math.h>
#include <string.h>
#include <bios.h>

//
// POCSAG & FLEX receiving program source code;
//

/* global variables */
// mostly these are global since two or more routines need to access the
// same variable. Things would be a lot cleaner when all related routines
// and variables are packaged into objects...

// PROGRAM OPTION DEFAULT SETTINGS...
static unsigned int lognotp = 0;        // set to 1 to enable log file
FILE *out;                              // pointer to output file
static unsigned int shownumeric = 0;	// set to 1 to display numeric pages
static unsigned int showmisc    = 0;    // set to 1 to show misc flex pages
static unsigned int rcvpolarity = 0;    // 1 means invert rcv signal
static unsigned int twolevelint = 1;	// 1 means two level interface
static unsigned int screenmode  = 2;    // default screen display mode
static unsigned int sport       = 1;    // default com port
static unsigned int timestamp   = 1;    // enable date/time stamping
static unsigned int kill_lf     = 1;    // enable/disable line feed kills
					// for log files

static unsigned int textscan  = 0;      // enable / disable text scanning
static unsigned int scannumeric = 0;	// enable / disable numeric
					// "text" scanning
static unsigned int filtfile  = 0;      // is filter log file active when
					// textscan is enabled?
static char ffname[20]={"FILTERXXXXXX"};// filtered log file name
static FILE *ffout;                     // filtered log file handle

static char fname[20]={"PAGERXXXXXX"};  // regular log file name

static int  scanaddr = 0;               // enable / disable address scanning
static char addr[401][40];              // stores address + description
static int  numaddr = 0;
static int  maxaddr = 400;              // maximum number of addresses

static unsigned int numscan   = 0;      // number of text scan strings
static char textstr[31][40];		// holds text search strings...
static int  textstrlen[31];		// holds length of each text string
static int  maxtextstr = 30;		// maximum number of search strings
static int  winsize    = 33;		// special window size (% of screen)

static unsigned int beeplen   = 3;      // length of beep in 55ms increments
static unsigned int beepfreq  = 800;    // beep frequency in Hz
static unsigned int beepcntdn = 0;	// number of 55 ms periods left
					// to leave beep on

static unsigned int prn_echo  = 0;	// printer echo flag:
static unsigned int lpt_port  = 0;	// printer port # (0 = LPT1,1 = LPT2)

// serial port registers addresses... (initialized on startup)
static unsigned int comrxtx,comier,comiir,comdfr,commcr,comssr,commsr;

static unsigned int buflen= 10000;      /* length of data buffer      */
static volatile unsigned int cpstn = 0; /* current position in buffer */
static unsigned int  fdata[10001] ;     /* frequency data array       */
static unsigned char ldata[10001] ;
static volatile unsigned int  imalive=0;// imalive flag for com interrupt
// static long     int  frame[200];

char block[300];
char ob[1000];
static int reflex = 0;			// reflex mode flag
static unsigned int bch[1025];
static double dt1600,dt3200,dtdtdt;
static unsigned int ecs[25];        /* error correction sequence */
static unsigned int ltick=0x0000;

static double rcver[65];		// rcv clock error ring buf
static int   ircver=0;
static double exc=0.0;

// receive symbol clock tightness - lower numbers mean slower loop response
static double rcv_clkt = 0.065;		// rcv loop clock tightness
static double rcv_clkt_hi = 0.065;	// coarse rcv clock (sync ups only)
static double rcv_clkt_fl = 0.015;      // fine rcv clock (data) - FLEX
static double rcv_clkt_po = 0.025;      // fine rcv clock (data) - POCSAG

// contains pocsag numeric paging data format
static char nume[17]={'0','1','2','3','4','5','6','7',
		       '8','9','*','U',' ','-',')','('};
static int pocbit = 0;

// DESCRIPTION OF EACH FLEX VECTOR TYPE
static char vtype[8][10]={
"SECURE ",
"-INST- ",
"-TONE- ",
"ST NUM ",
"SF NUM ",
" ALPHA ",
"BINARY ",
"NU NUM "
};

// spinner doohickey characters: / ³ \ -
static char spin1[4]={47,179,92,'-' };

// this table translates received modem status line combinations into
// the received symbol; it gives the number of modem status lines that
// are high (other than RI) for a given line status nybble
static int rcv[16]=
{
  0, 1, 1, 2, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3
};

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

void interrupt (*oldtimer) (...); // vector to original system timer int.

// This interrupt is called whenever one of the modem status lines changes.
// it stores the elapsed time since the last change in array fdata[] and the
// number of status lines that were high for that time period in ldata[].
// This interrupt handler can get hung up if a second modem status line
// changes state while this routine is processing... The com port will then
// have issued a second IRQ that is missed by this routine, and no further
// interrupts come in from the com port since it's still waiting for the
// missed interrupt to be serviced.The system timer interrupt is used as
// a lame workaround (see interrupt service routine newtimer).
// Why is it in assembly? hopefully so it runs just a little faster...
// but if you look at the executable code you find that a sizable portion
// of this handler is involved with pushing and popping the complete
// processor state onto and off of the stack.
void interrupt com1int(...)
{
  static unsigned int dtick,k;

  asm{
    XOR al,al;		// latch system timer and read it out
    OUT 0x43,al;
    IN 	al, 0x40;
    ROL ax, 0x08;
    in 	al, 0x40;
    ROL ax, 0x08;

    mov bx, ltick;      // subtract off previous count, place in dtick
    mov ltick, ax;
    sub bx, ax;
    mov dtick, bx;

    xor ax,ax;          // reset imalive flag
    mov imalive,ax;

  };



  // guess I'm to lazy to code this in assembly now...
  fdata[cpstn] = dtick;        /* put freq in fdata array             */
  ldata[cpstn] = k;

  cpstn  ++;                   /* increment data buffer pointer       */
  if (cpstn>buflen) cpstn=0;   /* make sure cpstn doesnt leave array  */

  asm{
    mov dx, comiir;     // clear IIR
    in	al, dx;
    mov dx, comssr;
    in 	al, dx;

    mov dx, commsr;     // this clears interrupt on serial port
    in	al, dx;
    and ax, 0x00ff;
    mov k , ax;

    mov al, 0x20;	// issue general end of interrupt to PIC
    out 0x20, al;
  };
}

// new timer interrupt - purpose is to check if above ISR is hung
// every time timer rolls over (every 55 milliseconds); also turns
// beep off after appropriate length of time
void interrupt newtimer(...)
{
  static int hups = 0,t;

  // if imalive flag hasn't been reset in one whole clock tick
  // then we may have screwed the pooch.
  if (imalive != 0)
  {
    // Make sure we actually have a pending interrupt by reading iir.
    // Otherwise a long period where the input lines do not change
    // (as may happen when transmitter turns on/off and level sensing
    // circuitry hasn't had enough time to readjust) would cause the
    // hangup counter to jump.
    if (inportb(comiir) == 0)
    {
      // at this point: we've been screwed up for one whole cycle plus
      // the time indicated in ltick. So we attempt to make up for
      // this lost time in the RCV data buffer; hopefully letting
      // us continue processing the rest of any messages being received

      hups++;			// provide visual indicator of screwups
      t = ( hups      & 0x0f) + 48;
      if (t > 57) t+= 7;
      pokeb(0xb800,156,t);
      t = ((hups>>4 ) & 0x0f) + 48;
      if (t > 57) t+= 7;
      pokeb(0xb800,154,t);
      t = ((hups>>8 ) & 0x0f) + 48;
      if (t > 57) t+= 7;
      pokeb(0xb800,152,t);
      t = ((hups>>12) & 0x0f) + 48;
      if (t > 57) t+= 7;
      pokeb(0xb800,150,t);

      fdata[cpstn] = ltick;       // this takes care of time in previous
      ldata[cpstn] = 0;           // cycle that was skipped
      cpstn  ++;
      if (cpstn>buflen) cpstn=0;

      fdata[cpstn] = 0xffff;      // this takes care of the clock tick
      ldata[cpstn] = 0;
      cpstn  ++;
      if (cpstn>buflen) cpstn=0;

      ltick = 0x0000;             // let com ISR restart from even keel

      inportb(commsr);             // start ball rolling again
    }
  }
  imalive = 666;

  // important - turn sound off after count down has been reached
  if (beepcntdn > 0)
  {
    beepcntdn--;
    if (beepcntdn == 0)
    {
      t = inportb(0x61);
      t = t & 0xFC;
      outportb(0x61,t);
    }
  }

  oldtimer();                   // jump on to original timer interrupt
}

void set8250 ()                /* sets up the 8250 UART               */
{
  static unsigned int t;
  outportb (comdfr, 0x00);
  outportb (comier, 0x08);
  outportb (commcr, 0x0a);     /*  push up RTS, DOWN DTR              */
  t = inportb(comssr);         /*  clear LSR                          */
  t = inportb(comrxtx);        /*  clear RX                           */
  t = inportb(commsr);         /*  clear MSR                          */
  t = inportb(comiir);         /*  clear IID                          */
  t = inportb(comiir);         /*  clear IID - again to make sure     */
}

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);
}


/****************************************************************/

// remember : put in small buffer; expand single lf to crlf
// if char = 175 and !kill_lf then expand that into crlf
// send character to printer routine
void print_char(int c)
{
  static int col=0;
  if (c == 13)
  {
    col = 0;
    biosprint(0,13,lpt_port);
  }
  else if (c != 10)
  {
    biosprint(0,c,lpt_port);
    col++;
    if (col > 79)
    {
      biosprint(0,10,lpt_port);
      biosprint(0,13,lpt_port);
      col = 0;
    }
  }
  else biosprint(0,10,lpt_port);
}



// This object handles ALL display processing, all log files, and all
// filtering functions. The "screen" object (of class display) should
// probably have a different name now since more and more of these
// non-display related tasks seem to keep getting piled onto it.
class display
{
  private : int num_col, num_row;
	    int scr_x  , scr_y;  	// screen coordinates
	    int win_x  , win_y;		// special window coordinates
	    int sw_color;		// text color in special window
	    int spwin1, spwin2;		// two locations in special window
	    int text_color;
	    int spec_color;
	    union REGS regs;
	    int orig_mode;		// original video mode
	    void statusline();		// redraw status line
	    int  logon;			// log file open flag
	    void stoplog();
	    char tempst[40];
	    int  mesbuf[1025];		// message buffer (for text search)
	    int  ims;
	    void textsearch();

	    int  numess;		// numeric message flag

	    long int pager_addr;	// pager address
	    int      addr_type;		// type of address
	    int  pass_filt;		// flags pages that passed filters

	    int  rowsplit;

	    void proc_swchar(int c);	// show character in special window
	    void proc_swstr(char strin[]); // show string in special window
	    void sw_crlf();

	    void show_filt(int ct);	// show filtered page
	    void show_bound();		// special cap window boundary

	    int time_stamp(int nu);     // time stamp
	    char ts_str[60];		// time stamp string

	    // eventually: keep current pager capcode in memory
	    // for filtering functions...

  public :  void scroll_up(int l1, int l2); // scrolls part of screen up a line

  public  : display();
	    ~display();
	    void startlog();
	    void pauselog();
	    int mode(int mo);		// set video mode
	    void color(int co);		// set displayed textcolor
	    void show_char(char cin);	// display a single character
	    void show_str(char strin[]);// display a NULL terminated string
	    void show_crlf();		// carriage return + line feed
	    void show_hex21(long int l);// show 21 bit word...
	    void show_hex16(int l);     // show 16 bit hex word
	    void cls();			// clear screen
	    void cfstatus(int cy, int fr); // show cycle & frame status
	    void showmo(int mo);	// shows which mode is active
	    void show_flexad(long int l); // show flex address (short)
	    void show_flexad(long int l1, long int l2); // show long flex ad
	    void show_pocaddr(long int l,int fn); // show POCSAG address

	    void start_spwin(int pof);	// start special window

	    void flush_filt();		// flush out any filtered pages

	    void shrink();		// shrink special window down
	    void grow();		// increase special window by a line

} screen;


// goodness... the special window routines should really be in an object
// by themselves, and the regular unfiltered display in another object of
// the same type; both with their own associated log files... but I'm
// just too lazy



// ----------> also checks if particular time stamp is activated
// TIME STAMP ROUTINE - when called it figures out if the clock has
//         changed (ignoring seconds) and if so writes a new time stamp
//         string into ts_str[].
// input : nu gives time stamp sequence number... this way you can
//            distinguish 4 independent time stamps without them
//            clobbering each other
// return: 0 means no time stamp needs to be printed - no change from last call
//         1 time stamp in ts_str[] should be printed
//
// currently: time stamp 1 used by special window
//            time stamp 2 used by regular window
int display::time_stamp(int nu)
{
  static union REGS regs;
  static int hrmi[5],moda[5],year[5],month;
  static char mon[13][4]={"???","JAN","FEB","MAR","APR","MAY","JUN",
			  "JUL","AUG","SEP","OCT","NOV","DEC"};
  static int i;

  // if timestamping is turned off then always return 0
  if (timestamp == 0) return(0);

  if (nu == 0)  // reset everything
  {
    for (i=0; i<5; i++)
    {
      hrmi[i] = 0; moda[i] = 0; year[i]=0;
    }
    return(0);
  }
  else if (nu < 5)
  {
    // read real time clock - get hours and minutes
    regs.h.ah = 0x02;
    int86(0x1A,&regs,&regs);
    hrmi[0] = regs.x.cx;
    // read real time clock - get date
    regs.h.ah = 0x04;
    int86(0x1A,&regs,&regs);
    moda[0] = regs.x.dx;
    year[0] = regs.x.cx;

    // check if things stayed the same since last call
    if ((hrmi[nu] == hrmi[0]) && (moda[nu] == moda[0]) && (year[nu] == year[0]))
    {
      return(0);  // everything equal... no new time stamp needed
    }

    hrmi[nu] = hrmi[0];
    moda[nu] = moda[0];
    year[nu] = year[0];

    // new time / date now stored in [nu]... all that's left is to
    // update ts_str[]
    month = (moda[0] >> 8) & 0xff;
    month = (10 * (month >> 4)) + (month & 0x0f);
    if (month > 12) month = 0;
    sprintf(ts_str,"--------------- %2X %s %4X %2X:%02X ---------------",
	   moda[0] & 0xff,mon[month],year[0],hrmi[0] >> 8, hrmi[0] & 0xff);

    return(1);
  }

  return(0);
}

// start special window; input : percent of screen height to be taken up
// by windown
void display::start_spwin(int pof)
{
  if (rowsplit < 2)
  {
    rowsplit = pof * num_row * 0.01;
    if (rowsplit < 3) rowsplit = 3;
    if (rowsplit > (num_row - 4)) rowsplit = num_row - 4;
    // update winsize
    winsize = (int) (100* (double) rowsplit / num_row);
    cls();
  }
}

// shrinks special message display window... If special message display
// window inactive (rowsplit = 1) then nothing happens
void display::shrink()
{
  if (rowsplit > 3)
  {
    // scroll spec window up a line...
    if ( win_y >= (rowsplit - 2))
    {
      // if we're at bottom of screen scroll whole window up
      scroll_up(1,rowsplit);
      // pull back currently active line
      win_y--;
      if (win_y < 1) win_y = 1;
    }
    else
    {
      // if we haven't reached bottom just roll up boundary
      scroll_up(rowsplit-2,rowsplit);
    }

    rowsplit--;

    winsize = (int) (100.0 * (double) rowsplit / num_row);


    spwin1 -= ((num_col) << 1);
    spwin2 -= ((num_col) << 1);
  }
}

// increase special message window size by one line if active (rowsplit>1)
void display::grow()
{
  static int l,c,c1,c2,t;
  if ((rowsplit < (num_row-5)) && (rowsplit > 1))
  {

    //move bottom boundary line down line
    c = ((rowsplit-1)*num_col) << 1;
    for (l=0; l<num_col; l++)
    {
      c1 = c + (l << 1);   	// source location
      c2 = c1 + (num_col<<1); 	// destination
      t = peek(0xb800,c1);	// get source
      poke(0xb800,c1,0x0720);	// blank it out
      poke(0xb800,c2,t);	// store char
    }

    rowsplit++;

    winsize = (int) (100.0 * (double) rowsplit / num_row);

    spwin1 += ((num_col) << 1);
    spwin2 += ((num_col) << 1);

    poke(0xb800, spwin1 ,0x17BA);
    poke(0xb800, spwin2 ,0x17BA);

    // make sure normal messages don't end up in special window
    if (scr_y < rowsplit)
    {
      scr_y = rowsplit;
      scr_x = 0;
    }
  }
  else if ((rowsplit == 1) && ((textscan) || (scanaddr)) )
  {
    // if window disabled but textscan or scanaddr is on: start window
    start_spwin(2);
  }

}

// show edge of special message window (if special message window open)
void display::show_bound()
{
  static int l,c;
  // color attributes: lightgray on blue background (0x17)
  if (rowsplit > 1)
  {
    for (l=1; l<(rowsplit-1); l++)
    {
      c = (l*num_col) << 1; // show 'º' on left window edge
      poke(0xb800,c,0x17BA);
      spwin1 = c;
      c = c + ((num_col - 1) << 1);   // show 'º' on right edge
      poke(0xb800,c,0x17BA);
      spwin2 = c;
    }
    c = ((rowsplit-1)*num_col) << 1;
    for (l=1; l<num_col; l++)
    {
      poke(0xb800,c + (l<<1),0x17CD);
    }
    poke(0xb800, c ,0x17C8);
    poke(0xb800, c + ((num_col-1)<<1) ,0x17BC);

  }

}


// show null terminated string in special window
void display::proc_swstr(char strin[])
{
 static int j;
 j = 0;
 while ( (strin[j] != 0) && (j <256) )
 {
   proc_swchar(strin[j]);
   j++;
 }
}


// process character in a filtered message - display it on screen
// if special window open and send it on the filtered log file
void display::proc_swchar(int c)
{
  static int cc,co;

  if (rowsplit > 1)
  {
    // change LF to ¯ (char 175) for on screen display
    if ((c & 0xff) != 0x0A) cc = (sw_color << 8) + (c & 0xff);
		  else      cc = (sw_color << 8) + 175;
//     cc = (sw_color << 8) + (c & 0xff);

    co = (win_x + (win_y*num_col)) << 1;
    poke(0xb800,co,cc);

    // do cr / lf if needed
    win_x++;
    if (win_x >= (num_col-1))
    {
      win_x=1;
      win_y++;
      if ( win_y > (rowsplit - 2))
      {
	win_y = rowsplit - 2;
	scroll_up(1,rowsplit-1);
	poke(0xb800, spwin1 ,0x17BA);
	poke(0xb800, spwin2 ,0x17BA);
      }
    }
  }

  c = c & 0xff;
  // bad characters shown as 'ú' in log file
  if (sw_color == LIGHTGRAY) c = 'ú';
  if ( (c == 10) && (kill_lf)) c = 175;

  // echo character to special message file
  if (filtfile)
  {
    fputc(c,ffout);
//    c = c & 0xff;
//    // bad characters shown as 'ú' in log file
//    if (sw_color == LIGHTGRAY) fputc('ú',ffout);
//    else if (c != 10) fputc(c,ffout);
//    else
//    {
//      if (kill_lf) fputc(175,ffout); else fputc(c,ffout);
//    }
  }

  // echo to printer if prn_echo == 1
  if (prn_echo == 1)
  {
    print_char(c);
  }
}


void display::sw_crlf()
{
  if (rowsplit > 1)   // do CR if special window open
  {
    // do CR
    win_x = 1;
    win_y ++;
    if ( win_y > (rowsplit - 2))
    {
      win_y = rowsplit - 2;
      scroll_up(1,rowsplit-1);
      poke(0xb800, spwin1 ,0x17BA);
      poke(0xb800, spwin2 ,0x17BA);
    }
  }

  if (filtfile)
  {
    fprintf(ffout,"\n");
  }

  if (prn_echo == 1)
  {
    print_char(10);
    print_char(13);
  }

}

// displays special messages in window at top of screen. rowsplit gives
// the line number of the boundary with the regular display. This
// boundary does not show any message information...
void display::show_filt(int ct)
{
  static int l,mb;
  pass_filt = 0;

  // if we have a special window available use it to show message
  sw_crlf();

  if (time_stamp(1) == 1)   // TIME STAMP special window if necessary
  {
    sw_color = LIGHTBLUE;
    proc_swstr(ts_str);
    sw_crlf();
  }

  // print out capcode description (if any)
  if (ct >= 0)
  {
    l=10;
    sw_color = WHITE;
    while ((l <40) && (addr[ct][l] != 0))
    {
      proc_swchar(addr[ct][l]);
      l++;
    }
    proc_swchar(32);
  }


  // show address in white if it matched an address search
  if (ct < 0) sw_color = GREEN; else sw_color = WHITE;
  // show address which is still stored in tempst
  l=0;
  while ( (l <15) && (tempst[l] != 0))
  {
    proc_swchar(tempst[l]);
    l++;
  }

  // show a space right after address
  proc_swchar(32);

  // now print out the message, highlighting trigger phrase in WHITE
  for (l=0; l<ims; l++)
  {
    mb = mesbuf[l];

    sw_color = (mb >> 12) & 0x0f;	// set foreground color

    if ( (mb & 0x400) != 0) sw_color = WHITE;  // show trigger phrase in white

    if ( (mb & 0x100) != 0) mb = mb ^ 0x20;

    proc_swchar(mb);
  }


  // if special log file on do crlf
//  if (filtfile)
//  {
//    fprintf(ffout,"\n");
//  }
}

void display::flush_filt()
{
  static int l,l2,k;

  if (pass_filt)    // show message if it passed text string search
  {
    show_filt(-1);
    pass_filt=0;
  }
  else if ((addr_type < 0x80) && (scanaddr) )
  {
   // only do address search on numeric messages if shownumeric
   // and scannumeric equal to 1
   if ( (numess == 0) || ( (numess==1) && (shownumeric==1) && (scannumeric==1)))
   {
    // do capcode search if needed
    for (l=0; l<numaddr; l++)
    {
      // address type matches
      if ( ((int) addr[l][0]) == addr_type)
      {
	// set number of characters to compare
	if (addr_type < 2) l2 = 7; else l2 = 9;
	for (k=1; k<=l2; k++)
	{
	  // if any two characters in capcode string mismatch
	  // (excluding wildcard) stop comparision
	  if ((addr[l][k] != tempst[k]) && (addr[l][k] != '?')) k = 200;
	}

	// if k is small then the capcode matched up
	if (k < 20)
	{
	  show_filt(l);

	  l = numaddr +5;  	// make sure we stop checking

	  // start beep
	  if (beeplen > 0)
	  {
	    k = inportb(61);
	    k = k | 0x03;
	    outportb(0x61,k);

	    // tell timer interrupt when to shut beep off
	    beepcntdn = beeplen;
	  }

	}
      }

    }
   }
  }
}



// show POCSAG address in decimal along with function number
void display::show_pocaddr(long int l, int fn)
{
  ims = 0; // reset message buffer

  l = l & 0x1fffffl;
  sprintf(tempst,"{%07li} %1i ",l,fn);

  // display capcode; check if it has errors in it
  if (l > 0x3fffffl)
  {
    addr_type = 0x80;
    strcpy(tempst,"{???????} ");
  }
  else
  {
    addr_type = 0x00;
  }

  // check timestamp
  if (time_stamp(2) == 1)
  {
    color(LIGHTBLUE);
    show_str(ts_str);
    show_crlf();
  }

  color(GREEN);
  show_str(tempst);

  color(YELLOW);

  pass_filt = 0;
  pager_addr = l;
}

// converts a short flex address to a CAPCODE; shows it on screen
void display::show_flexad(long int l)
{
  static long int capcode;

  ims = 0; // reset message buffer

  capcode = (l & 0x1fffffl) - 32768l;
  sprintf(tempst,"[%07li] ",capcode);

  // capcode is bad if it was derived from a word with uncorrectable
  // errors or if it is less than zero
  if ( (l > 0x3fffffl) || (capcode < 0))
  {
    addr_type = 0x81;
    strcpy(tempst,"[???????] ");
  }
  else
  {
    addr_type = 0x01;
  }

  // check timestamp
  if (time_stamp(2) == 1)
  {
    color(LIGHTBLUE);
    show_str(ts_str);
    show_crlf();
  }

  color(GREEN);
  show_str(tempst);

  pass_filt = 0;
  pager_addr = capcode;
}

// converts a long flex address to a CAPCODE; shows it on screen
void display::show_flexad(long int l1, long int l2)
{
  static long int capcode;

  ims = 0; // reset message buffer

  // to get capcode: take second word, invert it...
  capcode = (l2 & 0x1fffffl) ^ 0x1fffffl;
  // multiply by 32768
  capcode = capcode << 15;
  // add in 2068480 and first word
  // NOTE : in the patent for FLEX, the number given was 2067456...
  //        which is apparently not correct
  capcode = capcode + 2068480l + (l1 & 0x1fffffl);
  // convert capcode to text string for display
  sprintf(tempst,"[%09li] ",capcode);

  // capcode is bad if it is less than zero or either one of the
  // words it was derived from had uncorrectable errors
  if ( (l1 > 0x3fffffl) || (l2 > 0x3fffffl) || (capcode < 0))
  {
    addr_type = 0x82;
    strcpy(tempst,"[?????????] ");
  }
  else
  {
    addr_type = 0x02;
  }

  // check timestamp
  if (time_stamp(2) == 1)
  {
    color(LIGHTBLUE);
    show_str(ts_str);
    show_crlf();
  }

  color(GREEN);
  show_str(tempst);

  pass_filt = 0;
  pager_addr = capcode;
}

// show FLEX cycle and frame information on status line
void display::cfstatus(int cy, int fr)
{
  static int c;

  if (cy < 10) pokeb(0xb800,38,cy+48); else pokeb(0xb800,38,cy+55);

  c = (fr >> 4) & 0x0f;
  if (c < 10) pokeb(0xb800,48,c +48); else pokeb(0xb800,48,c +55);
  c = fr & 0x0f;
  if (c < 10) pokeb(0xb800,50,c +48); else pokeb(0xb800,50,c +55);
}

void display::cls()
{
  static int xx,yy,z,cc;
  cc = (text_color << 8) + 0x20;
  for (xx = 0; xx<num_col; xx++)
  {
    for (yy = 0; yy<num_row; yy++)
    {
      z = ( (yy * num_col) + xx) << 1;
      pokeb(0xb800,z,cc);
    }
  }
  win_x = 1;
  win_y = 1;
  scr_x = 0;
  scr_y = rowsplit;
  statusline();
}

void display::show_hex21(long int l)
{
  static char tt[10];
  sprintf(tt,"%06lX",l);
  if (l > 0x3fffffl) text_color = LIGHTGRAY;
  show_str(tt);
}

void display::show_hex16(int l)
{
  static char tt[10];
  sprintf(tt,"%04X",l);
  show_str(tt);
}

void display::statusline()
{
  static int i,ox,oy,otc,oof;
  static char gg[30];

  ox = scr_x; oy = scr_y;	// save screen coordinates
  otc = text_color;
  oof = lognotp;

  lognotp = 0;

  scr_x = 0; scr_y = 0;

  // set up color scheme on status line: lightgray on blue background
  for (i=0; i<num_col; i++)
  {
    poke(0xb800, i << 1,0x1720);  // should be 0x1720
  }                  //
  text_color = 0x17; // char 179 = ³     INT: 4L INV                           4INMA
  show_str("  ³ FLEX ABCD ³ CY:- FR:-- ³ REFLEX ABCD ³ POCSAG ---- ³ LOG OFF ³ 4INMA ³ 0000");

  if (logon)	// is log file open ?
  {
    if (oof)	// it's open and data is being logged
    {
      pokeb(0xb800,122,79);	// O
      pokeb(0xb800,124,78);	// N
      pokeb(0xb800,126,32);     // blank
    }
    else		// log file open; but we're paused
    {
      pokeb(0xb800,122,80);	// P
      pokeb(0xb800,124,65);	// A
      pokeb(0xb800,126,85);     // U
    }

    if (num_col > 110)	// if there's enough space show log file name
    {
      sprintf(gg," ³ LOG TO: %s ³",fname);
      scr_x = 80;
      scr_y = 0;
      show_str(gg);
    }
  }
  else			// log file is off
  {
    pokeb(0xb800,122,79);	// O
    pokeb(0xb800,124,70);       // F
    pokeb(0xb800,126,70);       // F
  }

  // ALWAYS HAVE ALPHANUMERIC MODE ON so color "A" YELLOW
  pokeb(0xb800,143,0x1E);
  // ALWAYS HAVE AN INTERFACE - COLOR IT YELLOW;  CHANGE TO 2 if needed
  pokeb(0xb800,135,0x1E);
  if (twolevelint) pokeb(0xb800,134,50);
  // do we show numeric messages ??? then shown "N" as yellow
  if (shownumeric) pokeb(0xb800,139,0x1E);
  // show miscellaneous flex messages ??? then shown "M" yellow
  if (showmisc) pokeb(0xb800,141,0x1E);
  // UPDATE INVERT FLAG COLOR
  if (rcvpolarity == 1) pokeb(0xb800,137,0x1E);

  lognotp = oof;
  scr_x = ox; scr_y = oy;	// restore screen coordinates
  text_color = otc;

  show_bound();			// show special message window if needed

}

void display::show_crlf()
{
  scr_x = 0;
  scr_y ++;
  if (scr_y >= num_row)
  {
    scroll_up(rowsplit,num_row);
    scr_y = num_row - 1;
  }

  if (lognotp) fprintf(out,"\n");

  if (prn_echo == 2)
  {
    print_char(10);
    print_char(13);
  }
}

// do text search
void display::textsearch()
{
  static int t,l,j,il;


  for (l=0; l<numscan; l++)
  {
    // don't bother if message collected so far is shorter
    // than the length of the text string...
    if ( ims >= textstrlen[l])
    {
      il = ims - textstrlen[l];

      for (j=0; j<textstrlen[l]; j++)
      {

	if (textstr[l][j] != (char) (mesbuf[il + j] & 0xff)) j = 6665;
      }

      // at this point : if there were any mismatched characters j
      // would have been set to 6665 in above loop, then incremented
      // by 1 on exiting loop. If j != 6666 then the current text
      // string has been successfully matched.
      if (j != 6666)
      {
	// tag message as containing a desired trigger phrase so it
	// will get special treatment once full message has come in
	pass_filt = 1;

	// tag trigger phrase so it can be highlighted on screen
	for (j=0; j<textstrlen[l]; j++) mesbuf[il+j] ^= 0x400;

	// start beep...
	if (beeplen > 0)
	{
	  t = inportb(61);
	  t = t | 0x03;
	  outportb(0x61,t);

	  // tell timer interrupt when to shut beep off
	  beepcntdn = beeplen;
	}

      }

    }
  }

}

// Every character shown on the screen except for those on the
// status line is funneled to this routine, so this is the perfect
// place to hook in the text search search routine. And since we
// have a multicolor display, we can use the current textcolor to
// distinguish the alphanumeric characters (yellow) to search through
// from the other stuff being sent to the screen
//
void display::show_char(char cin)
{
  static int cc,zz;

  // replace line feed character by ¯ (char 175) for on screen display
  if (cin != 0x0A) cc = (text_color << 8) + (cin & 0xff);
	    else   cc = (text_color << 8) + 175;


  zz = (scr_x + (scr_y * num_col)) << 1;
  poke(0xb800,zz,cc);
  scr_x ++;
  if (scr_x >= num_col)
  {
    scr_x = 0;
    scr_y++;
    if (scr_y >= num_row)
    {
      scroll_up(rowsplit,num_row);
      scr_y = num_row - 1;
    }
  }

  if (text_color == GREEN) numess = 1;   // reset numeric message flag
  else if (text_color == YELLOW) numess=0;

  // update message buffer if doing textscan
  if (textscan)
  {
    // store all alphanumeric message characters (yellow), numeric
    // characters (lightred) if scannumeric set, if within a message
    // (ims>0) the bad characters (lightgray).
    // Bad addresses (also shown in lightgray) are not mistaken for part
    // of a message because in that case ims = 0. However, this means
    // that a message will not start to be put into mesbuf until the
    // first good char is encountered (so that ims no longer is zero).
    // Oh well... close enough
    if ( (text_color == YELLOW) ||
	 ( (scannumeric == 1)  && (text_color == LIGHTRED)) ||
	 ((ims > 0) && (text_color == LIGHTGRAY)) )
    {
      // store upper case only in first 8 bits of a mesbuf entry;
      // flag lower case letters by setting ninth bit
      if ( (cin >= 97) && (cin <= 122) )
      {
	mesbuf[ims] = (((int) cin ) ^ 0x20) ^ 0x100;
      }
      else
      {
	mesbuf[ims] = (int) cin;
      }

      // keep foreground color in highest 4 bits of mesbuf
      mesbuf[ims] += (text_color << 12);

      if (ims < 1024) ims++;

      // search for trigger phrases only if one hasn't been found yet
      if (pass_filt == 0) textsearch();
    }



  }

  if (lognotp)
  {
    // echo timestamp, address, and message characters only to log file
    // if character has error it is replaced by a 'ú' in log file
    if (text_color == LIGHTGRAY) fputc('ú',out);
    else
    if ((text_color == GREEN) || (text_color == LIGHTRED) ||
	(text_color == YELLOW) || (text_color == LIGHTBLUE) )
    {
      if (cin != 10) fputc(cin,out);
      else
      {
	if (kill_lf) fputc(175,out); else fputc(cin,out);
      }
    }
  }

  if (prn_echo == 2)
  {
    if (text_color == LIGHTGRAY) cin = 'ú';
    if ((cin == 10) && (kill_lf)) cin = 175;

    if ((text_color == GREEN) || (text_color == LIGHTRED) ||
	(text_color == YELLOW) || (text_color == LIGHTBLUE) )
	print_char(cin);
  }
}

// show null terminated string
void display::show_str(char strin[])
{
 static int j;
 j = 0;
 while ( (strin[j] != 0) && (j <256) )
 {
   show_char(strin[j]);
   j++;
 }
}


void display::scroll_up(int l1, int l2)
{
  static int xx,yy,z,of;

  // remember : top line (line 0) is protected status line
//  for (yy=1; yy < num_row; yy++)
  for (yy=l1; yy < l2; yy++)
  {
    for (xx=0; xx< num_col; xx++)
    {
      of = (xx + num_col*yy) << 1;
      z = peek(0xb800,of + (num_col << 1));
      poke(0xb800,of,z);
    }
  }

  // fill line at bottom of screen with spaces
  z = (text_color << 8) + 0x20;
  of = num_col * (l2 - 1);
  for (xx=0; xx<num_col; xx++)
  {
    poke(0xb800,(of+xx) << 1,z);
  }
}

void display::color(int co)
{
  text_color=co;
}

int display::mode(int mo)
{
  if (mo < 0x100)
  {

    if (mo >= 0x02)
    {
      regs.x.ax = 0x0003;
      int86(0x10,&regs,&regs);
      num_row = 25;
      num_col = 80;
    }
    else
    {
      regs.x.ax = 0x0001;	// 40 by 25 text mode is so silly there's
      int86(0x10,&regs,&regs);	// no point in doing it right - treat it
      num_row = 13;		// as 80 by 13 text mode
      num_col = 80;
    }
  }
  else
  {
    // SVGA modes
    if (mo < 0x108) return(1);  	// filter out graphics modes...
    if (mo > 0x10c) return(3);		// unknown mode...

    regs.x.ax = 0x4f02;			// command mode change
    regs.x.bx = mo;
    int86(0x10,&regs,&regs);

    // AL is 0x4f is SVGA bios present; AH is 0x00 if mode change ok
    if (regs.x.ax == 0x004f)
    {
      if ( mo > 0x108) num_col = 132; else num_col = 80;
      switch (mo)
      {
	case 0x108 : num_row = 60; break;
	case 0x109 : num_row = 25; break;
	case 0x10A : num_row = 43; break;
	case 0x10B : num_row = 50; break;
	case 0x10C : num_row = 60; break;
      }
    }
    else return(2);
  }

  // if special window active (rowsplit > 1): resize it to a given
  // percentage of screen height
  if (rowsplit > 1)
  {
    rowsplit = winsize * num_row * 0.01;
    if (rowsplit < 2) rowsplit = 2; // make sure window remains active
  }

  if (rowsplit > (num_row - 4)) rowsplit = num_row - 4;

  win_x = 1;
  win_y = 1;
  scr_x = 0;
  scr_y = rowsplit;
  statusline();
  return (0);
}

// shows active mode
void display::showmo(int moe)
{
  static int i,j,phst;
  static int puf[3][4]={' ','5','1','2',
			'1','2','0','0',
			'2','4','0','0'};

  for (i=1; i< 121; i+=2) pokeb(0xb800,i,0x17);
  if ( (moe & 0x0f) > 0)
  {
    if ( (moe & 0x10) > 0)
    {
      pokeb(0xb800,61,0x1E); // if in reflex mode yellow up RE
      pokeb(0xb800,59,0x1E);
      phst = 63;
    } else phst = 9;
    // show FLEX ABCD in YELLOW
    for (i=0; i<9; i++) pokeb(0xb800,phst + (i << 1),0x1E);
    // GRAY out inactive phases
    if ( (moe & 0x08) == 0) pokeb(0xb800,phst + 10,0x17);
    if ( (moe & 0x04) == 0) pokeb(0xb800,phst + 12,0x17);
    if ( (moe & 0x02) == 0) pokeb(0xb800,phst + 14,0x17);
    if ( (moe & 0x01) == 0) pokeb(0xb800,phst + 16,0x17);
    // if two level interface - show active phases that
    // cannot be decoded in red
    if (twolevelint)
    {
      if ( (moe & 0x04) != 0) pokeb(0xb800,phst + 12,0x1C);
      if ( (moe & 0x01) != 0) pokeb(0xb800,phst + 16,0x1C);
    }

  }
  else if (moe >= 0x20)	// see if pocsag active
  {

    for (i=85; i<111; i+=2) pokeb(0xb800,i,0x1e); // yellow in POCSAG

    // show baud rate
    j = 0;
    if ( (moe & 0x40) != 0) j = 1;
    else if ( (moe & 0x80) != 0) j = 2;
    for (i= 0; i<4; i++) pokeb(0xb800, 100 + (i<<1), puf[j][i]);
  }
}


void display::startlog()
{
  if (logon == 0)
  {
    mktemp(fname);  // fname initialized with PAGERXXXXXX template

    out = fopen(fname,"wt");
    if (out == NULL)
    {
      show_str("Error opening output file. Output won't be echoed to file.");
      lognotp = 0;
    }
    else
    {
      fflush(out);
      lognotp = 1;
      logon = 1;
    }
  }

}

void display::pauselog()
{
  // if logfile off - start it up
  if (logon == 0)
  {
    startlog();

    statusline();

  }
  else
  {
    // if log file already on (logon set) then toggle logfile
    if (lognotp == 0) lognotp = 1; else lognotp = 0;

    // update status - we're either paused or on

    if (lognotp)	// it's open and data is being logged
    {
      pokeb(0xb800,122,79);	// O
      pokeb(0xb800,124,78);	// N
      pokeb(0xb800,126,32);     // blank
    }
    else		// log file open; but we're paused
    {
      pokeb(0xb800,122,80);	// P
      pokeb(0xb800,124,65);	// A
      pokeb(0xb800,126,85);     // U
    }


  }

}

void display::stoplog()
{
  if (logon == 1)
  {
    fprintf(out,"\n\n");
    fclose(out);
  }
  lognotp=0;
  logon = 0;
}

display::display()
{
  rowsplit = 1;

  num_col = 80;
  num_row = 25;
  scr_x   = 0;
  scr_y   = rowsplit;

  win_x   = 0;
  win_y   = 1;

  logon   = 0;

  // get orignal video mode
  regs.x.ax = 0x0f00;
  int86(0x10,&regs,&regs);
  orig_mode = regs.h.al;

  // set to page zero
  regs.x.ax = 0x0500;
  int86(0x10,&regs,&regs);

  text_color = 15;

  cls();

  statusline();

  if (lognotp) startlog();

  ims = 0;

  show_bound();

  time_stamp(0);
}

display::~display()
{

  regs.h.ah = 0x00;
  regs.h.al = orig_mode;
  int86(0x10,&regs,&regs);

  // reset video mode; no longer using direct VRAM access, so
  // printf is ok again...

  if (logon)
  {
    printf("Regular log sent to file : %s\n",fname);
    stoplog();
  }

  if (filtfile)
  {
    printf("Filtered messages logged to file: %s\n",ffname);
    fprintf(ffout,"\n\n");
    fclose(ffout);
  }
}

//************************************************************

class phase
{

  private:  int  xsumchk(long int l);
  public :  char block[300];
  public :  long int frame[200];
  public :  char ppp;
  public :  void reset();
	    void showblock(int blknum);
	    phase();
	    void showframe();
	    void showword(int wordnum);
	    void showwordhex(int wordnum);
} PHASEA,PHASEB,PHASEC,PHASED;

phase::phase()
{
  ppp = 'X';
}

void phase::reset()
{
  screen.show_str("This is a test..\n\r");
}


int nones(int k)
{
  static int i,kt;
  kt = 0;
  for (i=0; i<=15; i++)
  {
    if ( (k & 0x0001) != 0) kt++;
    k = k >> 1;
  }
  return(kt);
}

int bit10(int gin)
{
  static int i,k;
  k = 0;
  for (i=0; i<10; i++)
  {
    if ( (gin & 0x01) != 0) k++;
    gin = gin >> 1;
  }
  return(k);
}

int ecd()
{
  static int i,synd,errl,acc,pari,ecc,b1,b2;

  errl = 0;
  pari = 0;

  /* run through error detection and correction routine */
  ecc = 0x000;

  for (i=0; i<=20; i++)
  {
    if ( ob[i] == 49) { ecc = ecc ^ ecs[i]; pari = pari ^ 0x01; }
  }
  acc = 0;
  for (i=21; i<=30; i++)
  {
    acc = acc << 1;
    if ( ob[i] == 49) acc = acc ^ 0x01;
  }
  synd = ecc ^ acc;

  errl = 0;

  if ( synd != 0)		/* if nonzero syndrome we have error */
  {

    if (bch[synd] != 0)		/* check for correctable error */
    {
      b1 = bch[synd] & 0x1f;
      b2 = bch[synd] >> 5;
      b2 = b2 & 0x1f;

      if (b2 != 0x1f)
      {
	ob[b2] = ob[b2] ^ 0x01;
	ecc = ecc ^ ecs[b2];
      }

      if (b1 != 0x1f)
      {
	ob[b1] = ob[b1] ^ 0x01;
	ecc = ecc ^ ecs[b1];
      }

      errl = bch[synd] >> 12;
    }
    else
    {
      errl = 3;
    }

    if (errl == 1) pari = pari ^ 0x01;

  }

  /* check parity .... */
  pari = (pari + bit10(ecc)) & 0x01;
  if ( (pari + 48) != ob[31]) errl++;

  if (errl == 4) errl = 3;

  return (errl);

}

// checksum check for BIW and vector type words
// returns: 0 if word passes test; 1 if test failed
int phase::xsumchk(long int l)
{
  static int xs;

  // was word already marked as bad?
  if ( l > 0x3fffffl) return(1);

  // 4 bit checksum is made by summing up remaining part of word
  // in 4 bit increments, and taking the 4 lsb and complementing them.
  // Therefore: if we add up the whole word in 4 bit chunks the 4 lsb
  // bits had better come out to be 0x0f
  xs =  (int) ( l      & 0x0f);
  xs += (int) ((l>>4 ) & 0x0f);
  xs += (int) ((l>>8 ) & 0x0f);
  xs += (int) ((l>>12) & 0x0f);
  xs += (int) ((l>>16) & 0x0f);
  xs += (int) ((l>>20) & 0x01);

  xs = xs & 0x0f;

  if (xs == 0x0f) return (0); else return(1);
}

// This routine is called when a complete phase of information is
// collected. First, the BIW is used to determine the length of the
// address and vector field blocks. Each address field is then
// processed according to the information in the vector field.
void phase::showframe()
{
  static int c,j,vsa,vb,vt,w1,w2,k,lad=0,l,m,n,fnu,asa;
  static long int cc,cc2,cc3;

  // get word where vector field starts (6 bits)
  vsa = (int) ((frame[0] >> 10) & 0x3f ); /* word where vector field starts */
  // get address field starts (2 bits)
  asa = (int) ((frame[0] >> 8) & 0x03);
  // convert to word # (0 = word 1; 1 = word 2; 2 = word 3; 3 = word 4)
  asa ++;

  // make sure we start out with valid BIW
  if ( xsumchk(frame[0]) == 0)
  {
  // run through whole address field
  for (j=asa; j<vsa; j++)
  {
    // this is the vector word number associated with the address word j
    vb = vsa + j - asa;
    cc2 = frame[j] & 0x1fffffl;

    // check for long addresses (lad != 0 indicates long address)
    lad = 0;
    if ( cc2 < 0x008001l) lad++;
    else if ( (cc2 > 0x1e0000l) && (cc2 < 0x1f0001l)) lad++;
    else if ( cc2 > 0x1f7FFEl) lad++;

    // get message vector type
    vt = (int) ( (frame[vb] >> 4) & 0x07l);

    // this makes sure screwed up vector fields are not processed
    if ( xsumchk(frame[vb]) != 0) vt = 69;

    // process alphanumeric and secure messages
    if ((vt == 5) || (vt==0))
    {
      // show address
      if (lad != 0) screen.show_flexad(frame[j],frame[j+1]);
	       else screen.show_flexad(frame[j]);

      screen.color(CYAN);
      screen.show_char(ppp);
      // show vector type description (ie "ALPHA" or "SECURE")
      screen.color(MAGENTA);
      screen.show_str(vtype[vt]);

      // get start and stop word numbers
      w1 = frame[vb] >> 7;
      w2 = w1 >> 7;
      w1 = w1 & 0x7f;
      w2 = (w2 & 0x7f) + w1 - 1;
      // get message fragment number (bits 11 and 12) from first header word
      // if != 3 then this is a continued message
      if (lad == 0)
      {
	fnu = (int) (frame[w1] >> 11) & 0x03;
	w1++;
      }
      else
      {
	fnu = (int) (frame[vb+1] >> 11) & 0x03;
	w2--;
      }

      // dump all message characters onto screen
      for (k=w1; k<=w2; k++)
      {
       if (frame[k] > 0x3fffffl) screen.color(LIGHTGRAY);
			    else screen.color(YELLOW);

	// skip over header info (depends on fragment number)
	if ( (k > w1) || (fnu != 0x03))
	{
	  c = (int) frame[k] & 0x7fl;
	  if (c != 0x03) screen.show_char(c);
	}
	cc = (long) frame[k] >> 7;
	c = (int) cc & 0x7fl;
	if (c != 0x03) screen.show_char(c);
	cc = (long) frame[k] >> 14;
	c = (int) cc & 0x7fl;
	if (c != 0x03) screen.show_char(c);
      }
      screen.show_crlf();
      screen.flush_filt();
    }
    // standard / special format numeric / numbered numeric message
    else if ((vt == 3) || (vt == 4) || (vt == 7))
    {
     if (shownumeric == 1)
     {
      if (lad != 0) screen.show_flexad(frame[j],frame[j+1]);
	else screen.show_flexad(frame[j]);
      screen.color(CYAN);
      screen.show_char(ppp);
      screen.color(MAGENTA);
      screen.show_str(vtype[vt]);

      w1 = frame[vb] >> 7;
      w2 = w1 >> 7;
      w1 = w1 & 0x7f;
      w2 = (w2 & 0x07) + w1;  	// numeric message is 7 words max

      // load first message word into cc
      if (lad == 0)
      {
	cc = frame[w1];		// if short adress first message word @ w1
	w1++;
	w2++;
      }
      else
      {
	cc = frame[vb+1];	// long address - first message word in
				// second vecor field
      }

      // skip over first 10 bits for numbered numeric; otherwise skip first 2
      if ( vt == 7) m = 14; else m = 6;

      for (k=w1; k<=w2; k++)
      {
	if (cc < 0x400000l) screen.color(LIGHTRED); else screen.color(LIGHTGRAY);

	for (l=0; l<21; l++)
	{
	  c = c >> 1;
	  if ( (cc & 0x01) != 0l) c ^= 0x08;
	  cc = cc >> 1;
	  m--;
	  if (m == 0)
	  {
	    screen.show_char(nume[c]);
	    m = 4;
	  }
	}
	cc = (long) frame[k];
      }
      screen.show_crlf();
      screen.flush_filt();
     }
    }
    else if (vt == 2)
    {
     if (showmisc == 1)
     {
      if (lad != 0) screen.show_flexad(frame[j],frame[j+1]);
	else screen.show_flexad(frame[j]);
      screen.color(CYAN);
      screen.show_char(ppp);
      screen.color(MAGENTA);
      screen.show_str(vtype[vt]);
      screen.show_str("<--TONE ONLY  ");
      screen.show_crlf();
     }
    }
    else if (vt == 6)	// HEX / BINARY
    {
     if (showmisc == 1)
     {
      if (lad != 0) screen.show_flexad(frame[j],frame[j+1]);
	else screen.show_flexad(frame[j]);
      screen.color(CYAN);
      screen.show_char(ppp);
      screen.color(MAGENTA);
      screen.show_str(vtype[vt]);

      w1 = frame[vb] >> 7;
      w2 = w1 >> 7;
      w1 = w1 & 0x7f;
      w2 = (w2 & 0x7f) + w1 - 1;

      if (lad == 0)
      {
	fnu = (int) (frame[w1] >> 13) & 0x03;
	if (fnu == 3) w1+=2; else w1++;
      }
      else
      {
	fnu = (int) (frame[vb+1] >> 13) & 0x03;
	if (fnu == 3) w1++;
	w2--;
      }

      n = 0;
      m = 0;
      screen.color(BROWN);
      for (k=w1; k<=w2; k++)
      {
	cc3 = frame[k];
	for (l=0; l<21; l++)
	{
	  m = m >> 1;
	  if ( (cc3 & 0x01l) != 0) m = m ^ 0x08;
	  cc3 = cc3 >> 1;
	  n++;
	  if (n == 4)
	  {
	    if ( m < 10) screen.show_char(48+m); else screen.show_char(55+m);
	    n=0;
	    m = 0;
	  }
	}
      }
      screen.show_crlf();
     }
    }

    // now ... if long address then make sure we skip over both parts
    if (lad > 0) j++;
  }
  }

}

/* format a received frame */
void phase::showblock(int blknum)
{
  static int i,j,k,err;
  static long int cc;
  for (i=0; i<8; i++)
  {

    /* format 32 bit frame into output buffer to do error correction */
    for (j=0; j<32; j++)
    {
      k = (j*8) + i;
      ob[j] = block[k];
    }
    err = ecd();

    k = (blknum << 3) + i;
    cc = 0x0000l;
    for (j=0; j<21; j++)
    {
      cc = cc >> 1;
      if (ob[j] == 48) cc ^= 0x100000l;
    }
    if (err == 3) cc ^= 0x400000l; // flag uncorrectable errors
    frame[k] = cc;

  }
  // show messages in frame if last block was processed and we're
  // not in reflex mode
  if ( (blknum == 10) && (reflex == 0))
  {
    showframe();
  }
}

// displays given three character word... used when displaying a
// REFLEX message where the characters are spread over multiple
// phases.
void phase::showword(int wordnum)
{
  static int c;
  static long int cc;

  cc = (long) frame[wordnum];
  if (cc > 0x200000l) screen.color(LIGHTGRAY); else screen.color(YELLOW);
  if ( (cc != 0x0000l) && (cc != 0x1fffffl) )
  {
    c = (int) cc & 0x7fl;
    screen.show_char(c);
    cc = (long) frame[wordnum] >> 7;
    c = (int) cc & 0x7fl;
    screen.show_char(c);
    cc = (long) frame[wordnum] >> 14;
    c = (int) cc & 0x7fl;
    screen.show_char(c);
  }
}

void phase::showwordhex(int wordnum)
{
  screen.color(MAGENTA);
  screen.show_char('[');
  screen.show_hex21(frame[wordnum]);
  screen.show_char(']');
}


void frame_flex(char gin)
{
  static unsigned int sup[4] = {0x870C,0xA6C6,0xAAAA,0x78F3},slr[4];
  static int i,nh,te,bc=0,blk=0,bct=0,level=2,sps=1600,hbit=0,j;
  static int cer=0,hd,ihd,cy0,cy1,cy2;
  static double aver;

  /* update bit buffer */
  /* sync up signal is 1600 BPS 2 level FSK signal */
  for (i=0; i<3; i++)
  {
    slr[i] = slr[i] << 1;
    if ( (slr[i+1] & 0x8000) != 0x00) slr[i] = slr[i] | 0x0001;
  }
  slr[3] = slr[3] << 1;
  if (gin < 2) slr[3] = slr[3] | 0x0001;


  nh = 0;
  // center portion always the same
  te = slr[1] ^ sup[1]; nh += nones(te);
  te = slr[2] ^ sup[2]; nh += nones(te);

  if (blk == 0)
  {
    // NOTE: STILL MISSING 3200SPS 2 LEVEL FSK SYNC UP*****************
    //       AND SEVERAL REFLEX SYNC UPS
    // sync up with 4 or less mismatched bits out of center 32
    // AND 4 or less mismatched bits out of outside 32
    if (nh < 5)   // guessing we've gotten sync up...
    {
      bc = 89;

      te = slr[0] ^ 0xB068; nh = nones(te);  // FLEX 4 level 1600 sps
      te = slr[3] ^ 0x4F97; nh += nones(te);
      if (nh < 5)
      {
	screen.showmo(12);
	sps = 1600;
	level = 4;
	reflex = 0;
      }
      else
      {
	te = slr[0] ^ 0xDEA0; nh = nones(te);  // FLEX 4 level 3200 sps
	te = slr[3] ^ 0x215F; nh += nones(te);
	if (nh < 5)
	{
	  screen.showmo(15);
	  sps = 3200;
	  level = 4;
	  reflex = 0;
	}
	else
	{
	  te = slr[0] ^ 0x870C; nh = nones(te);  // FLEX 2 level 1600 sps
	  te = slr[3] ^ 0x78F3; nh += nones(te);
	  if (nh < 5)
	  {
	    screen.showmo(8);
	    sps = 1600;
	    level = 2;
	    reflex = 0;
	  }
	  else
	  {
	    te = slr[0] ^ 0x4C7C; nh = nones(te);  // REFLEX 4 level 3200 sps
	    te = slr[3] ^ 0xB383; nh += nones(te);
	    if (nh < 5)
	    {
	      screen.showmo(31);
	      sps = 3200;
	      level = 4;
	      reflex = 1;
	    }

	    // display unknown sync header; we recognize them since
	    // XORing words 0 and 3 of the sync headers gives 0xffff
	    else if ( (slr[0] ^ slr[3]) == 0xffff)
	    {
	      screen.color(RED);
	      screen.show_crlf();
	      screen.show_str ("UNKNOWN SYNC HEADER : ");
	      screen.show_hex16(slr[0]);
	      screen.show_hex16(slr[1]);
	      screen.show_hex16(slr[2]);
	      screen.show_hex16(slr[3]);
	      screen.show_crlf();

	      // for now guess it's 2 level 3200 sps (assumes more
	      // people will be listening to FLEX than ReFLEX)
	      sps = 3200;
	      level = 2;
	      reflex = 0;
	    }
	  }
	}
      }

      // FINE DIDDLE RCV CLOCK RIGHT HERE****************************
      // idea - take average error over last 64 bits; subtract it off
      // this allows us to go to slower main rcv loop clock response

      // get average rcv clock error over last 64 bits
      aver = 0.0;
      for (j=0; j<64; j++) aver = aver + rcver[j];
      aver = aver * 0.015625;

      // divide by two - just for the heck of it
      aver = aver * 0.5;
      exc = exc + aver;
      // go to slower main rcv loop clock
      rcv_clkt = rcv_clkt_fl;

    }

    if (bc > 0)
    {
      bc--;

      // pick off cycle info word (bit numbers 71 to 40 in sync holdoff)
      if ( (bc < 72) && (bc > 39 ))
      {
	if (gin < 2) ob[71- bc] = '1'; else ob[71-bc] = '0';
      }
      else
      if (bc == 39)  // process cycle info word when its been collected
      {
	cer = ecd();	// do error correction
	if (cer < 2)
	{
	  for (ihd = 4; ihd<8; ihd++) { hd = hd >> 1; if (ob[ihd] == '1') hd^=0x08; }
	  cy0 = (hd & 0x0f) ^ 0x0f;
	  for (ihd = 8; ihd<=14; ihd++) { hd = hd >> 1; if (ob[ihd] == '1') hd^=0x40; }
	  cy1 = (hd & 0x7f) ^ 0x7f;
	  for (ihd = 0; ihd<4; ihd++) { hd = hd >> 1; if (ob[ihd] == '1') hd^=0x08; }
	  cy2 = hd & 0x0f;
	  screen.cfstatus(cy0,cy1);
	}
      }

      if (bc == 0)
      {
	blk = 11;
	bct = 0;
	hbit = 0;

	// at this point data rate could become either 1600 or 3200 SPS
	if (sps == 1600) dtdtdt = dt1600; else dtdtdt = dt3200;

	// loosen up rcv loop clock constant again
	rcv_clkt = rcv_clkt_hi;
      }
    }
  }
  else
  {
    /* update phases depending on transmission speed */
    if (sps == 1600)
    {
      // always have PHASE A
      if (gin < 2) PHASEA.block[bct] = '1';
	      else PHASEA.block[bct] = '0';
      // if 4 level FSK - do PHASE B also
      if (level == 4)
      {
	if ( (gin ==0) || (gin == 3)) PHASEB.block[bct] = '1';
				 else PHASEB.block[bct] = '0';
      }
      bct++;
    }
    else
    {
      // split out bits
      if (hbit == 0)
      {
	if (gin < 2) PHASEA.block[bct] = '1';
	      else PHASEA.block[bct] = '0';
	if (level == 4)
	{
	  if ( (gin ==0) || (gin == 3)) PHASEB.block[bct] = '1';
				 else PHASEB.block[bct] = '0';
	}
	hbit ++;
      }
      else
      {
	if (gin < 2) PHASEC.block[bct] = '1';
	      else PHASEC.block[bct] = '0';
	if (level == 4)
	{
	  if ( (gin ==0) || (gin == 3)) PHASED.block[bct] = '1';
				   else PHASED.block[bct] = '0';
	}
	hbit = 0;
	bct ++;
      }


    }


    if (bct == 256)
    {
      bct = 0;      /* also pass on block # (0 - 10) */

      PHASEA.showblock(11-blk);
      if (level == 4) PHASEB.showblock(11-blk);
      if (sps == 3200)
      {
	PHASEC.showblock(11-blk);
	if (level == 4) PHASED.showblock(11-blk);
      }

      blk--;

      if (blk == 0)
      {
	// if finished set speed back to 1600 sps
	dtdtdt = dt1600;

	// if in reflex mode: display raw message if BIW != 0x1fffff
	if ((reflex == 1) && (PHASEA.frame[0] != 0x1fffffl))
	{
	  PHASEA.showwordhex(0);
	  for (i=0; i<88; i++)
	  {
	    PHASEA.showword(i);
	    PHASEB.showword(i);
	    PHASEC.showword(i);
	    PHASED.showword(i);
	  }
	  screen.show_crlf();
	}
      }
    }
  }
}

//******************************************************************

class poc_class{
  private: long int pocaddr;
	   int wordc,nalp,nnum,shown,srca,srcn;
	   int lwad; // last word an address flag
	   void show_short();
	   int alp[25],num[40];
	   int fn_num;

  public : void proc_word(int fn2);
	   void frame(char gin);
	   void reset();
	   poc_class();

} pocsag;


void poc_class::show_short()
{
  static signed int fal, fnu, i;

  // Sum up number of good alphanumeric characters, penalize for bad.
  // Good characters: anything greater than 31; 10 (line feed); 23 (ETB)
  fal = 0;
  for (i=0; i<nalp; i++)
  {
    if (alp[i]  > 31) fal ++;
    else if ( (alp[i] != 10) && (alp[i] != 23)) fal -= 1;

  }

  // count up number of good numeric characters...
  //
  // also: heavily penalize "numeric" messages that contain 'U','(' or ')'
  //       characters
  // In case you're wondering - the characters stored in array num have
  // already been converted into the correct ASCII format
  fnu = 0;
  for (i=0; i<nnum; i++)
  {
    fnu ++;
    // penalize for "bad" characters
    if ( (num[i] == 'U') || (num[i] == '(')
      || (num[i] == ')') )  fnu -= 20;
  }



  if ( (fal * 7) > (fnu * 4) )
//      if ( fal > (nalp - 2) )
  {
    // Guessing that we have alphanumeric message
    screen.show_pocaddr(pocaddr,fn_num);
    shown++;

    // now print out stored words
    for (i=0; i<nalp; i++) screen.show_char(alp[i]);
  }
  else
  {
    // Guessing numeric... (Tone only pages also end up here).
    if (shownumeric)
    {
      screen.show_pocaddr(pocaddr,fn_num);

      shown++;
      screen.color(LIGHTRED);
      for (i=0; i<nnum; i++) screen.show_char(num[i]);

      // Hey... why not label it if it's a tone only page?
      if (wordc == 0)
      {
	screen.color(LIGHTRED);
	screen.show_str("TONE ONLY");
      }

    }
  }
}

void poc_class::reset()
{

    // have we just shown a message? then do crlf; flush message if it
    // passed filtering test...
    if (shown > 0)
    {
      screen.show_crlf();
      screen.flush_filt();
      shown = 0;
    }
    srcn = 0;
    srca = 0;
    nalp = 0;
    nnum = 0;
    wordc = 0;

    // this also helps and makes sure we don't accidentally
    // trigger a tone only page
    lwad = 0;

}


poc_class::poc_class()
{
  wordc = 0;
  nalp = 0;
  nnum = 0;
  shown = 0;
  lwad = 0;
  srcn = 0;
  srca = 0;
}

// This routine processes a steady raw stream of pocsag codewords after
// the sync / idle  has been stripped out. The parameter fn2 passed to
// it is the number of words since the last sync sequence. fn2 is used
// to determine the frame number in which an address codeword has been
// sent (each frame consists of two words, so frame number = fn2 / 2).
void poc_class::proc_word(int fn2)
{
  static int i,sr=0,type = 0;
  static int lc=0, errl, du;

  errl = 0;

  // run error correcting routine
  errl = ecd();

  if (errl < 2) pocbit = 170;

  if (ob[0] == 49)	// MSB bit = 1 means message
  {
    lwad = 0;

    if (type > 0) screen.color(YELLOW); else screen.color(LIGHTRED);
    if (errl > 2) screen.color(LIGHTGRAY);

    if (wordc < 7)
    {
      for (i=1; i<=20; i++)
      {
	sr = sr >> 1;
	if (ob[i] == 49) sr=sr+0x40;
	srca++;
	srcn++;

	if (srca >= 7)	// store alpha char
	{
	  if (errl > 2) sr ^= 0x1000;  // keep error correcting info also
	  if (nalp < 25) alp[nalp] = sr;
	  nalp++;
	  srca = 0;
	}

	if (srcn >= 4)  // store numeric char
	{
	  du = (sr >> 3) & 0x0f;
	  if (nnum < 40) num[nnum] = nume[du];
	  nnum++;
	  srcn = 0;
	}
      }
      wordc++;
    }

    if (wordc == 7)  // this message is > 7 words long : it must be alpha
    {
      type = 5;

      // first print out address...
      screen.show_pocaddr(pocaddr,fn_num);
      shown++;

      // now print out stored words
      for (i=0; i<nalp; i++) screen.show_char(alp[i]);
      wordc++;
    }
    else if (wordc > 7)
    {
      // keep on printing extended alpha or numeric messages
      for (i=1; i<=20; i++)
      {
	sr = sr >> 1;
	if (ob[i] == 49) sr=sr+0x40;
	srca++;
	srcn++;
	if ((srca == 7) && (type == 5))	// show alpha char
	{
	  screen.show_char(sr);
	  srca = 0;
	}
	else if ((srcn == 4) && (type == 0))  // show numeric char
	{
	  if (shownumeric)
	  {
	    screen.show_char(nume[sr]);
	    srcn = 0;
	  }
	}
      }
    }
  }
  else			// MSB bit = 0 means address
  {

    if (wordc < 7)
    {
      // check & show message of less than seven words
      //
      // INCLUDES TONE - ONLY GHOSTING PROBLEM FIX
      //
      // message with more than a word in them are OK; If we had a Tone
      // Only address then wordc is zero but lwad would be set - so we
      // call show_short to display the tone only address. Previously
      // this routine was always called when wordc was zero which made
      // the "fake" tone only display
      if (wordc > 0) show_short();
      else if (lwad) show_short();
    }

    // reset in preperation of next message
    reset();

    // the way I have it, MSB of address is bit 1; address info in the
    // word is 18 bits;
    pocaddr= 0l;
    for (i=18; i>0; i--)	// get 18 MSB bits of address
    {
      pocaddr = pocaddr >> 1;
      if (ob[i] == 49) pocaddr ^= 0x100000l;
    }

    // to complete the capcode, we put the frame number into the 3
    // lsb bits
    pocaddr += (long) ((fn2 >> 1) & 0x07);

    // tag capcode as bad if uncorrectable error in address word
    if (errl > 2) pocaddr ^= 0x400000l;

    // get function number --- unfortunately doesn't seem to tell you
    // whether message is alpha or numeric
    if (ob[20] == '1') fn_num = 1; else fn_num = 0;
    if (ob[19] == '1') fn_num ^= 0x02;

    type = 5;
    lc = 0;
    lwad = 1;
  }

}


void poc_class::frame(char gin)
{
  static unsigned int fs0=0x7CD2,fs1=0x15D8,sr0=0,sr1=0;
  static int st=0,bc=0,cc,nh=0;

  /* update register */
  sr0 = sr0 << 1;
  if ( (sr1 & 0x8000) != 0) sr0 = sr0 ^0x01;
  sr1 = sr1 << 1;
  if (gin == 49) sr1 = sr1 ^ 0x01;

  if (gin == 'X')  // reset
  {
    st = 0;
    wordc = 0;
    nalp = 0;
    nnum = 0;
    lwad = 0;

    // just in case we finish a message, don't get any more address
    // codewords, and drop out of POCSAG mode...
    if (shown > 0)
    {
      screen.show_crlf();
      screen.flush_filt();
    }
    shown = 0;
  }


  if (st == 0)
  {
    // find pocsag sync sequence - sync up if there are less than
    // 4 mismatched bits**********************************
    nh = nones(sr0 ^ fs0) + nones(sr1 ^ fs1);
    if ( nh < 5 )
    {
      st = 1;
      bc = 16;
      cc = 0;
    }
  }
  else
  {
    /* format, process 16 by 32 bit paging block */
    ob[cc] = gin;
    cc++;
    if (cc == 32)
    {
      // find idle codeword; strip it out
      nh = nones(sr0 ^ 0x7A89) + nones(sr1 ^ 0xC197);
      if ( nh < 3 )
      {
	pocbit = 170;	// keep from switching back to flex if idle

	// at this point it is possible we still have a short message
	// in that we haven't even begun to show on screen yet...
	// This will be true for all messages with wordc < 8.
	// Next line finds them and flushes them out of the woodwork
	if ( (wordc >0) && (wordc <= 7)) show_short();

	// in any case... IDLE means message is terminated
	reset();
      }
      else
      {
	// dump one block through with word position relative to
	// sync word (determines frame number which is the 3 lsb
	// bits of POCSAG capcode).
	proc_word(16-bc);
      }

      bc--;	// decrement block count
      cc = 0;
    }
    if (bc == 0)
    {
      // if block count is zero go back to looking for sync word
      st = 0;

    }
  }

}


void setupecc()
{
  unsigned int srr = 0x3b4,i,n,j,k;

  /* calculate all information needed to implement error correction */
  for (i=0; i<=20; i++)
  {
    ecs[i] = srr;
    if ( (srr & 0x01) != 0) srr = (srr >> 1) ^ 0x3B4; else srr = srr >> 1;
  }

  /* bch holds a syndrome look-up table telling which bits to correct */
  // first 5 bits hold location of first error; next 5 bits hold location
  // of second error; bits 12 & 13 tell how many bits are bad
  for (i=0; i<1024; i++) bch[i] = 0;

  /* two errors in data */
  for (n=0; n<=20; n++)
  {
    for (i=0; i<=20; i++)
    {
      j = (i << 5) + n;
      k = ecs[n] ^ ecs[i];
      bch[k] = j + 0x2000;
    }
  }

  /* one error in data */
  for (n=0; n<=20; n++)
  {
    k = ecs[n];
    j = n + (0x1f << 5);
    bch[k] = j + 0x1000;
  }

  /* one error in data and one error in ecc portion */
  for (n=0; n<=20; n++)
  {
    for (i=0; i<10; i++)  /* ecc screwed up bit */
    {
      k = ecs[n] ^ (1 << i);
      j = n + (0x1f << 5);
      bch[k] = j + 0x2000;
    }
  }

  /* one error in ecc */
  for (n=0; n<10; n++)
  {
    k = 1 << n;
    bch[k] = 0x3ff + 0x1000;
  }

  /* two errors in ecc */
  for (n=0; n<10; n++)
  {
    for (i=0; i<10; i++)
    {
      if (i != n)
      {
	k = (1 << n) ^ (1 << i);
	bch[k] = 0x3ff + 0x2000;
      }
    }
  }

}

// ALL command line arguements processed here
// Note: this is obsolete now. All these options and more are read
//       in from the pocflex.ini file. This routine is here for
//       backwards compatability (I thought I'd never see the day).
void proc_arg(char ar[])
{
  static int te,tar;

  tar = ar[1];
  if ( tar > 64) tar = tar & 0x5f;      // convert char to upper case
  if ( ar[0] == '-') te = 0; else te = 1; // if minus sign disable option

  if (tar == 'L')
  {
    // if +L start log; otherwise ignore (program default = log off)
    if (te == 1) screen.startlog();
  }
  else if (tar == 'N') shownumeric = te;
  else if (tar == 'M') showmisc    = te;
  else if (tar == 'P') rcvpolarity = te;
  else if (tar == 'T') timestamp   = te;
  else if (tar == 'I') twolevelint = te ^ 0x01;
  else if (tar == 'C') kill_lf     = te & 0x01;

  // if first char is a number - set this up to be startup video mode
  if ( (ar[0] > 48) && (ar[0] < 58 ) )
  {
    screenmode = (int) ar[0] - 48;
  }

  // if first char is c - user is trying to specify a certain com port
  // with a number 1 to 4 as the second character
  if ( (ar[0] & 0x5f) == 'C')
  {
    sport = ar[1] - 48;
    // catch bogus com port settings
    if (sport < 1) sport = 1;
    if (sport > 4) sport = 1;
  }
}

int disp_mode(int c)
{
  // list of 2 standard text video modes plus 5 VESA modes
  static int vmode[10]={0x01,0x03,0x108,0x109,0x10a,0x10b,0x10c};
  static int nmode=7,ic,st;

  ic = c - 1;

  if (ic < 0) ic = 0; else if (ic >= nmode) ic = nmode - 1;

  st = screen.mode(vmode[ic]);

  if (st != 0)
  {
    screen.show_crlf();
    screen.color(RED);
    screen.show_str("Error encountered changing video mode.");
    screen.show_crlf();
    screen.show_crlf();
    return(1);
  }
  return(0);
}

// reads in pocflex.ini configuration file
void read_pocflexini()
{
  FILE *in;
  int ltype;
  char lini[80];

  in = fopen("pocflex.ini","rt");
  if (in == NULL)
  {
    screen.color(RED);
    screen.show_str("ERROR - could not open pocflex.ini file.");
    screen.show_crlf();
    screen.show_str("Using default / command line arguements only.");
    screen.show_crlf();
    screen.show_str("Press any key to continue...");
    screen.show_crlf();
    getch();
    // put in a getch ?????????????
  }
  else
  {
    ltype = 0;

    // read in, examine every line in the file
    while (fgets(lini,80,in) != NULL)
    {
      if ( (strlen(lini) > 0) && (lini[0] != ';'))
      {
	strupr(lini);	// convert to upper case just in case
	if (lini[0] == '(')
	{
	  if (strstr(lini,"(GENERAL)") != NULL) ltype = 1;
	}
	else
	{
	  if (ltype == 1)
	  {
	    // caution : process video mode changes!!!!!!
	    sscanf(lini,"SHOWNUMERIC = %i",&shownumeric);
	    sscanf(lini,"SHOWMISC = %i",&showmisc);
	    sscanf(lini,"RCVPOLARITY = %i",&rcvpolarity);
	    sscanf(lini,"TWOLEVELINT = %i",&twolevelint);
	    sscanf(lini,"SCREENMODE  = %i",&screenmode);
	    sscanf(lini,"SPORT = %i",&sport);
	    sscanf(lini,"TIMESTAMP = %i",&timestamp);
	    sscanf(lini,"KILL_LF = %i",&kill_lf);
	    sscanf(lini,"PRN_ECHO = %i",&prn_echo);
	    sscanf(lini,"LPT_PORT = %i",&lpt_port);

	  }
	}
      }
    }

    fclose(in);
    if (sport < 1) sport = 1;
    if (sport > 4) sport = 4;
  }

}

// startup routine / reads in .ini file setups / shows startup text
void get_startinfo()
{
  FILE *in;
  char lini[80];
  int ltype,l2,l,ll;


  // READ in filter.ini file...
  in = fopen("filter.ini","rt");
  if (in == NULL)
  {
    screen.color(RED);
    screen.show_str("ERROR - could not open filter.ini file");
    screen.show_crlf();
  }
  else
  {
    ltype = 0;
    while (fgets(lini,80,in) != NULL)
    {
      // ignore comments, empty lines
      if ( (strlen(lini) > 0) && (lini[0] != ';'))
      {
	strupr(lini);
	if (lini[0] == '(')
	{
	  if (strstr(lini,"(GENERAL)") != NULL) ltype = 1;
	  else if(strstr(lini,"(BEEP)") != NULL) ltype = 2;
	  else if(strstr(lini,"(STRINGS)") != NULL) ltype = 3;
	  else if(strstr(lini,"(CAPCODES)") != NULL) ltype = 4;
	  else ltype = 0;
	}
	else
	{
	  if (ltype == 1)
	  {
	    sscanf(lini,"TEXTSCAN = %i",&textscan);
	    sscanf(lini,"SCANNUMERIC = %i",&scannumeric);
	    sscanf(lini,"FILTFILE = %i",&filtfile);
	    sscanf(lini,"SCANADDR = %i",&scanaddr);
	    sscanf(lini,"WINSIZE = %i",&winsize);
	  }
	  else if (ltype == 2)
	  {
	    sscanf(lini,"BEEPFREQ = %i",&beepfreq);
	    sscanf(lini,"BEEPLEN = %i",&beeplen);
	  }
	  else if (ltype == 3)
	  {
	    // find location of last " character
	    l2 = strlen(lini);
	    l2 --;
	    while( (l2 > 0) && (lini[l2] != 34) ) l2--;
	    // make sure we have at least one character in message
	    // and a " character as the first character
	    if (l2 > 40) l2 = 40;
	    if ( (l2 > 1) && (lini[0] == 34) )
	    {
	      for (l=1; l < l2; l++)
	      {
		textstr[numscan][l-1] = lini[l];
	      }
	      textstr[numscan][l2-1] = 0;  // null terminate string
	      textstrlen[numscan] = strlen(textstr[numscan]);
	      if (numscan < maxtextstr) numscan++;
	    }
	  }
	  else if (ltype == 4)
	  {
	    // see if we have POCSAG or short FLEX capcode (7 digits)
	    if (     ((lini[0] == '{') && (lini[8] == '}'))
		 ||  ((lini[0] == '[') && (lini[8] == ']'))   )
	    {
	      // first character tells us whether its pocsag or flex
	      if (lini[0] == '{') addr[numaddr][0] = 0;
			     else addr[numaddr][0] = 1;

	      // store capcode
	      for (l2 = 1; l2<=7; l2++) addr[numaddr][l2] = lini[l2];
	      l2 = 0;
	      // store description
	      ll = lini[9];
	      while ( (l2 < 29) && (ll != 0) && (ll != 10) && (ll != 13))
	      {
		addr[numaddr][l2+10] = lini[l2+9];
		l2++;
		ll = lini[l2+9];
	      }
	      // null terminate description
	      addr[numaddr][l2+10] = 0;

	      if (numaddr < maxaddr) numaddr++;
	    }
	    else if ((lini[0] == '[') && (lini[8] == ']') )
	    {
	      // handle FLEX long addresses (9 digits)
	      addr[numaddr][0] = 2;

	      // store capcode
	      for (l2 = 1; l2<=9; l2++) addr[numaddr][l2] = lini[l2];

	      l2 = 0;
	      // store description
	      ll = lini[11];
	      while ( (l2 < 29) && (ll != 0) && (ll != 10) && (ll != 13))
	      {
		addr[numaddr][l2+10] = lini[l2+11];
		l2++;
		ll = lini[l2+11];
	      }
	      // null terminate description
	      addr[numaddr][l2+10] = 0;

	      if (numaddr < maxaddr) numaddr++;
	    }
	  }
	}

      }
    }
    fclose(in);
  }

  // setup special display window if needed
  if (textscan && (winsize > 0)) screen.start_spwin(winsize);

  screen.show_crlf();
  screen.color(LIGHTCYAN);
  screen.show_str("* * * * *  POCSAG/FLEX Pager Receiving Program  * * * * *");
  screen.show_crlf();
  screen.show_crlf();
  screen.show_str("Rev. 0.005 (C)opyleft 1998");
  screen.show_crlf();
  screen.show_str("Interface: ");
  if (twolevelint) screen.show_str("2 Level FSK (Hamcomm type)");
	else screen.show_str("4 LEVEL FSK");
  screen.show_str(" on com port ");
  screen.show_char(48+sport);
  screen.show_crlf();
  screen.show_crlf();

  if (numscan >= maxtextstr)
  {
    screen.color(RED);
    screen.show_str("Warning: possibly too many text strings...");
    screen.show_crlf();
  }

  if (numaddr >= maxaddr)
  {
    screen.color(RED);
    screen.show_str("Warning: possibly too many pager addresses to look for...");
    screen.show_crlf();
  }

  screen.color(LIGHTCYAN);
  if (scanaddr)
  {
    screen.show_str("Pager address filter: ON     ");
  }
  else
  {
    screen.show_str("Pager address filter: OFF    ");
  }

  if (textscan)
  {

    screen.color(LIGHTCYAN);
    screen.show_str("Text filter: ON    ");

    // uncomment this to show text search strings
//    for (l=0; l<numscan; l++)
//    {
//      screen.show_char('"');
//      screen.show_str(textstr[l]);
//      screen.show_char('"');
//      screen.show_str(", ");
//    }

    if (filtfile)
    {
      mktemp(ffname);	// already initialized with FILTERXXXXXX template

      ffout = fopen(ffname,"wt");
      if (ffout == NULL)
      {
	screen.color(RED);
	screen.show_str("Error opening filtered output file. Filtered output won't be echoed to file.");
	filtfile = 0;
      }
      else
      {
	fflush(ffout);
	filtfile = 1;
      }
    }

  }
  else
  {
    screen.show_str("Text filter: OFF   ");
  }

  if ( (scanaddr == 0) && (textscan == 0)) filtfile = 0;

  if (filtfile)
  {
    screen.show_str("Filt log file: ON     ");
  }
  else
  {
    screen.show_str("Filt log file: OFF    ");
  }
  screen.show_crlf();

  // set up output tone frequency - PIT will be left set to the proper
  // output frequency
  sound(beepfreq);
  nosound();

}

void main (int argc, char *argv[] ) // int argc)
{
  unsigned int i=0,cw1=49,cw0=48,ldsr=0,dsr=0,ef=0,chh,lcw=0,du=0,lst=0;
  signed   int ct5=0,ct12=0,ct24=0,dinc=0; // pocsag clock run in counters
  unsigned int n,irqv,comport;
  static double clk=0.0,rer=0.0;

  setupecc();

  PHASEA.ppp='A';	// tag each phase
  PHASEB.ppp='B';
  PHASEC.ppp='C';
  PHASED.ppp='D';

  // read in configuration file
  read_pocflexini();

  for (du = 1; du<argc; du++)
  {
    proc_arg(argv[du]);
  }

  for (i=0; i<64; i++) rcver[i] = 0.0;

  rcv_clkt = rcv_clkt_hi;

  /* dt is the number of expected clock ticks per bit */
  dt1600 =  1.0/(1600.0*838.8e-9);
  dt3200 =  1.0/(3200.0*838.8e-9);
  dtdtdt = dt1600;

  // set to 80 by 25 mode if mode given on command line screws up
  if (disp_mode(screenmode) != 0) disp_mode(2);

  screen.cls();
  get_startinfo();
  screen.color(LIGHTGRAY);

  /* set up serial port and related parameters                          */
  n = inportb (0x21);
  if ( (sport == 1) | (sport == 3))
  {
    irqv = 0x0c;
    oldfuncc = getvect(irqv);    /* save COM  Vector                    */
    setvect (irqv, com1int);     /* 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, com1int);     /* Capture COM  vector                 */
    outportb(0x21, n & 0xf7);
    if (sport == 2) comport = 0x2f8; else comport = 0x2e8;
  }

  // set up serial port register addresses
  comrxtx = comport;
  comier  = comport + 0x01;
  comiir  = comport + 0x02;
  comdfr  = comport + 0x03;
  commcr  = comport + 0x04;
  comssr  = comport + 0x05;
  commsr  = comport + 0x06;

  oldtimer = getvect(0x08);    // subvert system timer interrupt
  setvect (0x08, newtimer);

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

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

  while (ef == 0)
  {

    if (i != cpstn)
    {
      clk += fdata[i];

      cw0 = ldata[i] >> 4;

      cw1 = rcv[cw0];

      // if two level interface: force symbol to be either 0 or 3 and
      // process as if 4 level interface where used. Phases B & D then
      // always get a long stream of '0's and don't produce any output.
      if (twolevelint)
      {
	if (cw1 > 0) cw1 = 3;
      }

      if (rcvpolarity) cw1 ^= 0x03; // if in invert mode - invert both bits

      // this shows that spinner thingie on the screen
      if ( cw1 == 3)
      {
	if (lst == 0)
	{
	  du = (du+1) & 0x03;
	  pokeb(0xb800,0x0000,spin1[du]);
	}
	lst = 3;
      }
      else if (cw1 == 0)
      {
	if (lst == 3)
	{
	  du = (du+1) & 0x03;
	  pokeb(0xb800,0x0000,spin1[du]);
	}
	lst = 0;
      }

      if ((cw1 >> 1) != lcw)
      {
	// look for POCSAG preamble sequence...
	if ( (dinc > 421)  && (dinc < 571)  ) ct24++;
	    else if (ct24 > 5) ct24 -= 3;
	if ( (dinc > 842)  && (dinc < 1142) ) ct12++;
	    else if (ct12 > 5) ct12 -= 3;
	if ( (dinc > 1976) && (dinc < 2674) ) ct5++;
	    else if (ct5 > 5)  ct5  -= 3;

	if (ct24 > 180)
	{
	  ct24=0;
	  screen.showmo(0x80);
	  dtdtdt = 496.4;
	  if (pocbit == 0)
	  {
	    exc = 0.0;
	    clk = 0.0;
	  }
	  rcv_clkt = rcv_clkt_po;
	  pocbit = 600;
//	  screen.show_char('+');
	}
	else if (ct12 > 180)
	{
	  ct12=0;
	  screen.showmo(0x40);
	  dtdtdt = 993.0;
	  if (pocbit == 0)
	  {
	    exc = 0.0;
	    clk = 0.0;
	  }
	  rcv_clkt = rcv_clkt_po;
	  pocbit = 600;
//	  screen.show_char('+');
	}
	else if (ct5  > 180)
	{
	  ct5 =0;
	  screen.showmo(0x20);
	  dtdtdt = 2327.4;
	  if (pocbit == 0)
	  {
	    exc = 0.0;
	    clk = 0.0;
	  }
	  rcv_clkt = rcv_clkt_po;
	  pocbit = 600;
//	  screen.show_char('+');
	}

	dinc = fdata[i];
	lcw = cw1 >> 1;
      }
      else
      {
	// didn't have transition across center
	dinc+= fdata[i];
      }

//      xct = exc + 0.5 * dtdtdt;  /* exc is current boundary */
      while ( clk >= (exc + 0.5 * dtdtdt))
      {

	clk = clk - dtdtdt;

	if (pocbit == 0)
	{
	  frame_flex(cw1);

	  rcver[ircver] = rer;
	  ircver++;
	  if (ircver > 63) ircver = 0;
	}
	else
	{
	  if ( cw1 > 1) pocsag.frame('0'); else pocsag.frame('1');

	  // decrement pocsag bit counter; if zero we're back to flex
	  // note : pocbit is reset by pocsag processing routines whenever
	  // a good word has been received
	  pocbit--;
	  if (pocbit == 0)
	  {
	    // well - we have just dropped out of POCSAG mode
	    screen.showmo(0);
	    pocsag.frame('X');		// reset pocsag routine
	    dtdtdt = dt1600;		// reset rcv clock to 1600 baud
	    rcv_clkt = rcv_clkt_hi;	// widen up rcv clk again

	    // we waited for a few 100 POCSAG bits or so before we decided
	    // to drop out... but at this point a FLEX transmission may
	    // already be underway. Solution? Set back the rcv ring buffer
	    // a few hundred samples to make up for the lost time.
	    // 200 for now - should be good for 150 1200 baud bits
	    if (i < 200) i = buflen - 202 + i;
		else i -= 200;

	  }
	}
      }
      /* clk now holds new boundary position. update exc slowly... */
      /* for now only use center transitions to adjust rcv clock   */
      if ( cw1 > 1) dsr = 0; else dsr = 1;
      // was 0.02 ....
      rer = clk - exc;
      if (dsr != ldsr) exc = exc + rcv_clkt * (rer);
      ldsr = dsr;

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

    // process keyboard commands
    if (kbhit() != 0)
    {
      chh = getch();
      if (chh == 32) ef++;
      else if (chh == 27) ef++;
      else if ( (chh & 0x5f) == 'L') screen.pauselog();
      else if ( (chh > 48) && (chh <= 57) ) disp_mode(chh-48);
      else if ( (chh & 0x5f) == 'N')
      {
	shownumeric ^= 0x01;
	if (shownumeric) pokeb(0xb800,139,0x1E); else pokeb(0xb800,139,0x17);
      }
      else if ( (chh & 0x5f) == 'M')
      {
	showmisc    ^= 0x01;
	if (showmisc) pokeb(0xb800,141,0x1e); else pokeb(0xb800,141,0x17);
      }
      else if ( (chh & 0x5F) == 'P')
      {
	rcvpolarity ^= 0x01;
	if (rcvpolarity) pokeb(0xb800,137,0x1E); else pokeb(0xb800,137,0x17);
      }
      else if (chh == 0)	// control character
      {
	chh = getch();
	if (chh == 72) screen.shrink();	// up arrow - shrink special win
	if (chh == 80) screen.grow();	// dn arrow - grow special win
      }
    }

  }


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

  // And finally - disable serial port interrupt from reaching PIC.
  outportb(commcr,0x00);

  // make sure sound is off (in case you stopped program during a beep)
  nosound();

  // give a few line feeds if printer echo is on
  if (prn_echo > 0)
  {
    print_char(10);
    print_char(13);
    print_char(10);
    print_char(13);
  }

}


syntax highlighted by Code2HTML, v. 0.9.1