#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#include <i86.h>

#define FALSE 0
#define TRUE ~FALSE

#define EOP    0x7f000000
#define NOP    0xffffffff
#define ESCAPE 0x1B
#define WAIT   0x00000000
#define OUTPUT 0x01000000
#define TOGGLE 0x02000000
#define DELAY  0x03000000
#define REPEAT 0x04000000
#define KEY    0x05000000
#define SYSTEM 0x06000000
#define NOTATE 0x07000000
#define NAM    0x08000000
#define KILL   0x09000000

#define OpMASK 0xff000000
#define PoMASK 0x00ff0000
#define PaMASK 0x0000ffff

#define OVF  0xc000
#define UNF  0x4000
#define NEG  0x8000

unsigned short int ParsePatt(char *);
unsigned char apply(unsigned char, unsigned short int);
unsigned char invert(unsigned char, unsigned short int);
unsigned long int test(unsigned char, unsigned short int);
unsigned long int inport( long int );
unsigned long int rdport( long int );
unsigned long int outport( long int, long int );
char * DispPatt( unsigned short int );
long int ParseCmd(char *);
void process_thread ( unsigned long int );
unsigned long int calibrate();
void about(void);
long int setBase(void);
float deint(unsigned int);
unsigned short int enint(float);

long int oports[2] = {0x378, 0x278};
long int noports = 2;

long int iports[2] = {0x379, 0x279};
long int niports = 2;

volatile unsigned long int * tokens;
long int ntokens;
long int maxtokens = 5;
#define inctokens 5;

volatile unsigned long int * threads;
long int nthreads;
long int maxthreads = 5;
#define incthreads 5;

volatile char ** names;

volatile char ** strings;
long int nstrings;
long int maxstrings = 5;
#define incstrings 5;

unsigned long int * pcs;
unsigned long int * times;

FILE * infile;
long int srcline = 0;
FILE * logfile;
char V = 0;    /* Verbose flag */
char D = 0;    /* Dump input flag */
char B = 0;    /* deBug flag */

FILE * initfile;
unsigned short ctl_pattern, inv_pattern;

char keychar;

void main(int argc, char ** argv)
{
   long int o, i;
   char buff[80], * infilename;

   if (argc > 1)
   {
      for (i=1; i<argc; i++)
      {
         if (!strnicmp( argv[i], "-v", 2))
         {
            V = TRUE;
         }
         
         else if (!strnicmp( argv[i], "-d", 2))
         {
            D = TRUE;
         }
         
         else if (!strnicmp( argv[i], "-b", 2))
         {
            B = TRUE;
         }
         
         else if (!strnicmp( argv[i], "-?", 2) | !strnicmp( argv[i], "/?", 2))
         {
			   about();
            exit(0);
         }
         
         else if (argv[i][0] != '-')
         {
            infilename = strdup(argv[i]);
            if (!strstr(infilename, "."))
            {
            	if (!(infilename = realloc(infilename, strlen(infilename)+4)))
            	{
            		fprintf(stderr, "Unable to resize free memory! (infilename)\n");
					exit(-1);
            	}
				strcat(infilename, ".plc");
            }
            if (!(infile = fopen( infilename, "r")))
            {
               fprintf(stderr, "Unable to open %s for read!\n\n", infilename);
               exit(-1);
            }
         }
      }
   }

   if (setBase() == -1)
   {
      fprintf(stderr, "Unable to locate printer port!\n");
      exit(-1);  
   }
   
   if (!(logfile = fopen( "PLCC.log", "w" )))
   {
      fprintf( stderr, "Unable to open PLCC.log for write!\n\n");
      exit(-1);
   }

   if (!infile)
      infile = stdin;

   if (!(initfile = fopen("PLCC.INI", "r")))
   {
      fprintf(stderr, "Unable to open PLCC.INI. Running calibration!\n");
      if(calibrate())
      {
        if (!(initfile = fopen("PLCC.INI", "r")))
        {
           fprintf(stderr, "Unable to open PLCC.INI after calibration!\n");
           exit(-1);
        }
      }
      else
      {
          fprintf(stderr, "Could not complete calibration!\n");
          exit(-1);
      }
   }
   
   if (fscanf(initfile, "%02x %04x", &ctl_pattern, &inv_pattern) != 2)
   {
      fprintf(stderr, "Error reading PLCC.INI - incomplete data.\n");
      exit(-1);
   }

   tokens = malloc( maxtokens * sizeof( unsigned long int ));
   ntokens = 0;
   threads = malloc( maxthreads * sizeof( unsigned long int ));
   nthreads = 0;
   threads[nthreads] = NOP;
   strings = malloc( maxstrings * sizeof( char * ));
   nstrings = 0;
   names = malloc( maxthreads * sizeof( char * ));
   names[nthreads][0] = '\0';

   while (1)
   {
      fgets(buff, 80, infile);
      srcline++;
      o = ParseCmd(buff);
      if (o != NOP)
      {
         if (o == NAM)
         {
            ;
         }
		 else
		 {		 	
            tokens[ntokens++] = o;
            if (ntokens >= maxtokens)
            {
               maxtokens += inctokens;
               tokens = realloc( (void *) tokens, maxtokens * sizeof( unsigned long int ) );
            }

            if (tokens[ntokens-1] == EOP)
            {
               if (++nthreads >= maxthreads)
               {
                  maxthreads += incthreads;
                  threads = realloc( (void *) threads, maxthreads * sizeof( unsigned long int ) );
				      names = realloc( (void *) names, maxthreads * sizeof( char * ) );
               }
               threads[nthreads] = NOP;
			      names[nthreads] = strdup("");;
            }
            else
            {
               if (threads[nthreads] == NOP)
               {
                  threads[nthreads] = ntokens-1;
               }
            }

            if ( tokens[ntokens - 2] == EOP &&  tokens[ntokens - 1] == EOP )
            {
               nthreads--;
               ntokens--;
               break;
            }
		 }
      }
   }
   
   if (D)
   {
      printf("\n%ld Tokens:\n", ntokens);
      for (i=0; i<ntokens; i++)
      {
         o = tokens[i];
         printf("%02ld: OP = %02x, Port = %02x, Patt = %04x\n", i, (short int)(o >> 24), (short int)((o & PoMASK)>>16), (unsigned short int)(o & PaMASK));
         if ((o & OpMASK) == SYSTEM)
            printf("File: %s\n", strings[(o & PoMASK) >> 16]);
      }
      getch();

      printf("\n%ld Threads:\n", nthreads);
      for (i=0; i<nthreads; i++)
      {
         o = tokens[threads[i]];
         printf(" Thread %2ld starts at %02ld, with OP = %02x, Port = %02x, Patt = %04x\n", 
           i, threads[i], (short int)(o >> 24), (short int)((o & PoMASK)>>16), (unsigned short int)(o & PaMASK));
      }
      getch();
   }

   if (V)
      fclose(logfile);

   pcs = malloc( nthreads * sizeof( unsigned long int ));
   times = malloc( nthreads * sizeof( unsigned long int ));

   for (i=0; i<nthreads; i++)
   {
      pcs[i] = threads[i];    /* init program counters to starts of threads */
      times[i] = NULL;        /* init next-event times to NULL */
   }
   keychar = '\0';

   while (1)
   {
      if (kbhit())
      {
         keychar = getch();
         if (keychar == ESCAPE)
            break;
      }

      for (i=0; i<nthreads; i++)
      {
         process_thread(i);
      }

      keychar = '\0';
   }

}

void process_thread( unsigned long int i )
{
   unsigned long int exec, o, j;
   unsigned char x;
   unsigned short int Found;
   
   exec = 1;
   while (exec)
   {
      switch ((o = tokens[pcs[i]]) & OpMASK)
      {
         case WAIT:
            if (test(inport((o & PoMASK)>>16), (o & PaMASK)))
               pcs[i]++;
            else
               exec = 0;
            break;

         case OUTPUT:
            outport((o & PoMASK)>>16, (x = (int)apply(rdport((o & PoMASK)>>16), (o & PaMASK))));
            if (D)
               printf("Outputting %02x\n", x);
            pcs[i]++;
            break;

         case TOGGLE:
            outport((o & PoMASK)>>16, (x = (int)invert(rdport((o & PoMASK)>>16), (o & PaMASK))));
            if (D)
               printf("Outputting %02x\n", x);
            pcs[i]++;
            break;

         case DELAY:
            if (times[i] == NULL)
            {
               times[i] = clock() + (int)(deint(o & PaMASK)*1000.0);
               exec = 0;
            }
            else
            {
               if (clock() >= times[i])
               {
                  times[i] = NULL;
                  pcs[i]++;
               }
               else
                  exec = 0;
            }
            break;

         case KEY:
            if (keychar == (o & PoMASK)>>16)
            {
               printf("KEY (%ld) triggered on %c\n", i, keychar);
/*               keychar = '\0';	*/
               pcs[i]++;
            }
            else
               exec = 0;
            break;

         case SYSTEM:
            system((const char *)strings[(o & PoMASK)>>16]);
            pcs[i]++;
            break;

         case NOTATE:
            printf("%s\n", strings[(o & PoMASK)>>16]);
            pcs[i]++;
            break;

         case KILL:
			   Found = FALSE;
            for (j=0; j<nthreads; j++)
			   {
				   if (0 == strcmp((const char *)(strings[(o & PoMASK)>>16]), (const char *)(names[j])))
				   {
/*					   printf("Matched %s\n", strings[(o & PoMASK)>>16]);	   */
/*		               printf("Killing thread#%ld, %s\n", j, names[j]);	*/
				       pcs[j] = threads[j];
                       times[j] = NULL;
			          Found = TRUE;
				   }
			   }
            if (!Found)
               printf("Tried to Kill non-existing thread '%s'\n", strings[(o & PoMASK)>>16] );
            pcs[i]++;
            break;

         case EOP:
            pcs[i] = threads[i];
            exec = 0;
            break;

         default:
            pcs[i]++;
            break;
      }
   }
}


unsigned short int ParsePatt (char * buff)
{
   unsigned short i, w;
   char * p;

   p = buff;
   w = 0;
   for (i=0; i<8; i++)
   {
      w <<= 1;          /* prepare for (next) input bit */
      switch (p[i])
      {
         case '1':
            w |= 0x100; /* enable current bit in mask byte */
            w |= 1;     /* set bit in data byte */
            break;

         case '0':
            w |= 0x100; /* enable current bit in mask byte */
            w |= 0;     /* clear bit in data byte */
            break;

         case '\0':
         case '\r':
         case '\n':
         case '\t':
            w >>= 1;    /* short pattern: undo shift and force exit */
            i = 7;
            break;

         default:       /* "don't care" = any character other than {0,1} */
                        /*    leave enable bit = 0, data bit = 0  */
            break;
      }
   }
   return(w);
}


long int ParseCmd(char * bff)
{
   unsigned short int port;
   float fnum, fport;
   long int op, x;
   char * p;
   char buff[80], chr;

   if ((p = strstr(bff, "\n")))
      *p = '\0';

   strcpy( buff, bff);
   p = strtok(buff, " ");
   if (!p)
      return( NOP );

   if (V)
      fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);

   if (!strnicmp(p, "WAIT", 1))         /* WAIT <port> <pattern>  */
   {
      op = WAIT;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to WAIT\n");
         op  = NOP;
         goto break_WAIT;
      }
      if ((x = sscanf(p, "%d", &port)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <port> in WAIT\n");
      }
      op |= (port & 0xff) << 16;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to WAIT\n");
         op  = NOP;
         goto break_WAIT;
      }
      op |= ParsePatt(p);
      if (V)
         fprintf(logfile, "WAIT   %d %s\n", port, DispPatt(op & 0xffff));
      break_WAIT:;
   }
 
   else if (!strnicmp(p, "OUTPUT", 1))         /* OUTPUT <port> <pattern>  */
   {
      op = OUTPUT;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to OUTPUT\n");
         op  = NOP;
         goto break_OUTPUT;
      }
      if ((x = sscanf(p, "%d", &port)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <port> in OUTPUT\n");
      }
      op |= (port & 0xff) << 16;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to OUTPUT\n");
         op  = NOP;
         goto break_OUTPUT;
      }
      op |= ParsePatt(p);
      if (V)
         fprintf(logfile, "OUTPUT %d %s\n", port, DispPatt(op & 0xffff));
      break_OUTPUT:;
   }

   else if (!strnicmp(p, "TOGGLE", 1))         /* TOGGLE <port> <pattern>  */
   {
      op = TOGGLE;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to TOGGLE\n");
         op  = NOP;
         goto break_TOGGLE;
      }
      if ((x = sscanf(p, "%d", &port)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <port> in TOGGLE\n");
      }
      op |= (port & 0xff) << 16;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to TOGGLE\n");
         op  = NOP;
         goto break_TOGGLE;
      }
      op |= ParsePatt(p);
      if (V)
         fprintf(logfile, "TOGGLE %d %s\n", port, DispPatt(op & 0xffff));
      break_TOGGLE:;
   }

   else if (!strnicmp(p, "DELAY", 1))         /* DELAY <time>  */
   {
      op = DELAY;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to DELAY\n");
         op  = NOP;
         goto break_DELAY;
      }
      if ((x = sscanf(p, "%f", &fnum)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <time> in DELAY\n");
      }
      op |= (enint(fnum));
      if (V)
         fprintf(logfile, "DELAY %6.1f\n", fnum);
      break_DELAY:;
   }

   else if (!strnicmp(p, "REPEAT", 1))         /* REPEAT <offset> <period>  */
   {
      op = REPEAT;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to REPEAT\n");                          
         op  = NOP;
         goto break_REPEAT;
      }
      if ((x = sscanf(p, "%f", &fport)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <offset> or <period> in REPEAT\n");   
         op  = NOP;
         goto break_REPEAT;
      }
      op |= enint(fport) << 16;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to REPEAT\n");                          
         op  = NOP;                                                                       
         goto break_REPEAT;
      }
      if ((x = sscanf(p, "%f", &fnum)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <period> in REPEAT\n");               
         op  = NOP;
         goto break_REPEAT;
      }
      op |= enint(fnum);
      if (V)
         fprintf(logfile, "REPEAT %6.1f %6.1f\n", fport, fnum);
      break_REPEAT:;
   }

   else if (!strnicmp(p, "KEY", 2))         /* KEY <char>  */
   {
      op = KEY;
      p = strtok(NULL, " ");
      if (!p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to KEY\n");                          
         op  = NOP;
         goto break_KEY;
      }
      if ((x = sscanf(p, "%c", &chr)) != 1)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: could not scan <character> in KEY\n");   
         op  = NOP;
         goto break_KEY;
      }
      op |= (long int)chr<<16;
      if (V)
         fprintf(logfile, "KEY %c\n", chr);
      break_KEY:;
   }

   else if (!strnicmp(p, "SPAWN", 1))  /* spawn a DOS job, usually for a sound file */
   {
      op = SYSTEM;
      p = buff+strlen(buff)+1;
      if (!*p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to SYSTEM\n");                          
         op  = NOP;
         goto break_SPAWN;
      }
      strings[nstrings] = strdup(p);
      op |= nstrings << 16;
      if (V)
         fprintf(logfile, "SYSTEM  (%ld) \"%s\"\n", nstrings, strings[nstrings]);

      if (++nstrings >= maxstrings)
      {
         maxstrings += incstrings;
         strings = realloc( (void *) strings, maxstrings * sizeof( char * ) );
      }
      break_SPAWN:;
   }

   else if (!strnicmp(p, "NOTATE", 2))  /* Notate - print message to console */
   {
      op = NOTATE;
      p = buff+strlen(buff)+1;
      if (!*p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to NOTATE\n");                          
         op  = NOP;
         goto break_NOTATE;
      }
      strings[nstrings] = strdup(p);
      op |= nstrings << 16;
      if (V)
         fprintf(logfile, "NOTATE  (%ld) \"%s\"\n", nstrings, strings[nstrings]);

      if (++nstrings >= maxstrings)
      {
         maxstrings += incstrings;
         strings = realloc( (void *) strings, maxstrings * sizeof( char * ) );
      }
      break_NOTATE:;
   }

   else if (!strnicmp(p, "KILL", 2))  /* kilL - terminate and restart named thread */
   {
      op = KILL;
      p = buff+strlen(buff)+1;
      if (!*p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to KILL\n");                          
         op  = NOP;
         goto break_KILL;
      }
      strings[nstrings] = strdup(p);
      op |= nstrings << 16;
      if (V)
         fprintf(logfile, "KILL  (%ld) \"%s\"\n", nstrings, strings[nstrings]);

      if (++nstrings >= maxstrings)
      {
         maxstrings += incstrings;
         strings = realloc( (void *) strings, maxstrings * sizeof( char * ) );
      }
      break_KILL:;
   }

   else if (!strnicmp(p, "NAME", 2))  /* naMe this thread */
   {
      op = NAM;
      p = buff+strlen(buff)+1;
      if (!*p)
      {
         if(!V)
            fprintf(logfile, "%03ld: %-40.40s |  ", srcline, bff);
         fprintf(logfile, "Error: too few args to NAME\n");                          
         op  = NOP;
         goto break_NAME;
      }
      names[nthreads] = strdup(p);
      op |= nthreads << 16;
      if (V)
         fprintf(logfile, "NAME  (%ld) \"%s\"\n", nthreads, names[nthreads]);
      break_NAME:;
   }

   else if (!strnicmp(p, "$", 1))         /* $$$  end of thread  */
   {
      op = EOP;
      if (V)
         fprintf(logfile, "\n");
   }

   else         /* / <remarks text>  */
   {
      op = NOP;
      if (V)
         fprintf(logfile, "\n");
   }

   return( op);
}


char * DispPatt( unsigned short int patt )
{
   static char buff[10];
   unsigned short int i, m1, m2;

   m1 = 0x8000;
   m2 = 0x80;
   for (i=0; i<8; i++)
   {
      if (patt & m1)
         buff[i] = (patt & m2) ? '1' : '0';
      else
         buff[i] = '.';
      m1 >>= 1;
      m2 >>= 1;
   }
   buff[i] = '\0';
   return(buff);
}


unsigned long int inport( long int pt )
{
   if (pt >= niports)
   {
      fprintf(stderr, "Attempt to input from logical port %ld, > max of %ld\n", pt, niports-1);
      exit(-1);
   }

   outp(iports[pt] + 1, ctl_pattern);
   if (B && V)
      printf("inputting %02x | %02x\n", inp(iports[pt]), inp(iports[pt] + 1));
   return(0xffff & (((inp(iports[pt]) & 0xf8) | (inp(iports[pt] + 1) & 0x07)) ^ inv_pattern));
}


unsigned long int rdport( long int pt )
{
   if (pt >= noports)
   {
      fprintf(stderr, "Attempt to read-back from logical port %ld, > max of %ld\n", pt, noports-1);
      exit(-1);
   }

   return(inp(oports[pt])); 
}


unsigned long int outport( long int pt, long int val )
{
   if (pt >= noports)
   {
      fprintf(stderr, "Attempt to write to logical port %ld, > max of %ld\n", pt, noports-1);
      exit(-1);
   }

   return(outp(oports[pt], val)); 
}


unsigned char apply (unsigned char state, unsigned short int patt)
{
   return ((state & ~(char)(patt >> 8)) | (char)(patt & 0xff));
}


unsigned char invert (unsigned char state, unsigned short int patt)
{
   return (state ^ (char)(patt & 0xff));
}


unsigned long int test (unsigned char word, unsigned short int patt)
{
   return ((word & (char)(patt >> 8)) == (char)(patt & 0xff));
}


unsigned long int calibrate()
{
    unsigned long int good, i, j;
    
    if (!(initfile = fopen("PLCC.ini", "w")))
    {
        fprintf(stderr, "Could not open PLCC.INI for output!\n");
        exit(-1);
    }

    printf("\nInstall test connector on parallel port.\n");
    printf("<enter> to continue\n"); getch();    
    
    for (ctl_pattern=0; ctl_pattern<8; ctl_pattern++)
    {
        outport(0, 0);
        inv_pattern = 0;
        inv_pattern = inport(0);
        if(B)
            printf("Ctl = %02x, Pat = 00, Inp = %02lx, Inv = %02lx\n", ctl_pattern, inport(0), inv_pattern);        
        good = 1;
        for (i=0, j=1; i<8 && good==1; i++, j=(j<<1)+1)
        {
            outport(0, j);
            if (j != inport(0))
                good = 0;
            if (B)
                printf("            Pat = %02x, Inp = %02lx, Inv = %02lx\n", j, inport(0), (j ^ inport(0)));
        }
        if(good)
                break;
    }
    
    if (good)
    {
       printf("\nCalibration successful.\n");
       fprintf(initfile, "%02x\n%04x\n", ctl_pattern, inv_pattern);
    }
    else
        printf("\nUnable to determine proper masking!\n");
    fclose(initfile);

    printf("\nRemove test plug from parallel port, and reconnect IO card.\n");
    printf("<enter> to continue, <ESC> to quit\n"); 
    if (getch() == ESCAPE)
      exit(0);
    return(good);
}


void about()
{
	printf("PLCC [-v | \/v] [-d | \/d] [-? | \/?] [\<file\>]\n\n");
	printf("   -v = Verbose flag:\n");
	printf("        Full source of script file printed to file PLCC.log\n\n");
	printf("   -d = Dump flag:\n");
	printf("        Tokens displayed for script file, line by line,\n");
	printf("        and the first entry of each thread.\n\n");
	printf("   -?   Prints this message.\n\n");
	printf("   \<file\> is the name of the script source file. If no extension\n");
	printf("        is specified, \".PLC\" is assumed.\n\n");
}

long int setBase()
{
   unsigned short __far * p;
   unsigned short b;
   
   p = MK_FP(0x40, 0x08);
   b = *p & 0xffff;

   oports[0] = b;
   iports[0] = b+1;   
   return(0);
}   

unsigned short int enint(float v)
{
   unsigned short int ex;
   float mn;
   
   mn = v;
   ex = 0;
   
   if (mn == 0.0)
      return(0x0000);
   
   if (mn < 0.0)
      return(NEG);
      
   if (mn > 16383.0)
      return(OVF);
   
   if (mn >= 1638.35)
      return((unsigned short int)mn);
   else
   {
      mn *= 10.0;
      ex += 0x4000;
      if (mn >= 1638.35)
         return(ex | (unsigned short int)mn);
      else
      {
         mn *= 10.0;
         ex += 0x4000;
         if (mn >= 1638.35)
            return(ex | (unsigned short int)mn);
         else
         {
            mn *= 10.0;
            ex += 0x4000;
            if (mn >= 1.0)
               return(ex | (unsigned short int)mn);
            else
               return(UNF);
         }
      }
   }
}   

float deint(unsigned int d)
{
   unsigned short int ex;
   float mn;
   
   if (d == 0)
      return(0.0);
      
   mn = (d & 0x3fff);
   ex = (d & 0xc000);
   
   if (mn == 0.0)
   {
      switch (ex)
      {
         case OVF:
            return(1e38);
            break;
            
         case NEG:
            return(-1.0);
            break;
            
         case UNF:
            return(1e-38);
            break;
      }
   }
   return(mn / pow(10,(ex>>14)));
}   
