#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,®s,®s);
hrmi[0] = regs.x.cx;
// read real time clock - get date
regs.h.ah = 0x04;
int86(0x1A,®s,®s);
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,®s,®s);
num_row = 25;
num_col = 80;
}
else
{
regs.x.ax = 0x0001; // 40 by 25 text mode is so silly there's
int86(0x10,®s,®s); // 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,®s,®s);
// 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,®s,®s);
orig_mode = regs.h.al;
// set to page zero
regs.x.ax = 0x0500;
int86(0x10,®s,®s);
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,®s,®s);
// 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",×tamp);
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