/*
**                    ---  term.c ---
**
**  EXAMPLE CODE: Terminal emulator. Can transfer files using
**  XMODEM, YMODEM, and YMODEM-G protocols.
**
**  See TERM.H for configuration parameters.
**
**  Link with TERM_IO, MODEM_IO, DIR, CRC, DOS, XYMODEM, and XYPACKET.
**  See TERM makefiles.
**
**  Do NOT select YMODEM-G when using a null modem cable unless you are
**  certain that RTS & CTS are reversed ( this is usually not true ).
**
**  This example program (not the PCL4C library) is donated to
**  the Public Domain by MarshallSoft Computing, Inc. It is
**  provided as an example of the use of the PCL4C.
**
*/

#include <stdio.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include "pcl4c.h"
#include "ascii.h"
#include "term.h"

#define FALSE 0
#define TRUE !FALSE
#define NORMAL 0x07
#define INVERSE 0x70
#define MESSAGE_POS 48

void MyCrtWrite();
void MyStart();
void ProcessESC();
void ShowProtocol();
void ShowMessage();
void MyExit();
void SayFilename();
void ErrorCheck();

/*** Global variables ***/

int Port;               /* current COM port [0..3] */
char Filename[15];      /* file name buffer */
char Buffer[1024];      /* block buffer */
char RxBuf[2048];       /* receive buffer */
char *BaudRate[10] =  {"300","600","1200","2400","4800","9600",
                       "19200","38400","57600","115200"};
int BaudCode;           /* baud rate code ( index into BaudRate[] ) */
char *ModelText[4] = {"Small","Compact","Medium","Large"};
char DTAbuffer[128];
char NCGchar  = NAK;
int OneKflag  = FALSE;
int BatchFlag = FALSE;
char Protocol = 'X';

/*** main program ***/

main(argc,argv)
int argc;
char *argv[];
{int DataFlag = FALSE;
 char c;
 int i, k;
 int n, rc;
 int Delta;         /* delta port status */
 int Status;        /* port status */
 char Version;
 char temp[81];
 /* right number of parameters ? */
 if(argc!=3)
     {printf("Usage: 'TERM port baud' -- example 'TERM 1 9600'\n");
      exit(1);
     }
 /* get port number from command line */
 Port = atoi(argv[1]) - 1;
 if((Port<0) || (Port>3))
     {printf("Port must be 1 to 4\n");
      exit(1);
     }
 /* get baud rate from command line */
 BaudCode = BaudMatch(argv[2]);
 if(BaudCode<0)
     {printf("Cannot recognize baud rate = %s\n",argv[2]);
      exit(1);
     }
 /* setup transmit & receive buffer */
 ErrorCheck( SioRxBuf(Port,RxBuf,Size2048) );
 /* set parms & reset (initialize) COM port */
 ErrorCheck( SioParms(Port,NoParity,OneStopBit,WordLength8) );
 MyStart(Port,BaudCode);
 /* set DTR and RTS */
 SioDTR(Port,'S');
 SioRTS(Port,'S');
 /* init CRC table */
 InitCRC();
 /* initialize screen */
 Scroll(0,0,24,79,0,NORMAL);
 /* display status message */
 sprintf(temp," COM%d %s %c 'ESC for menu'  ",1+Port,BaudRate[BaudCode],Protocol);
 ShowMessage(0,temp);
 Position(1,0);
 /* display some info */
 puts("TERM 5/1/92");
 Version = SioInfo('V');
 printf("Library = %d.%d\n",Version/16,Version%16);
 printf("Memory Model = %s\n",ModelText[3&SioInfo('M')] );
#if RTS_CTS_CONTROL
 SioFlow(Port,7*ONE_SECOND);
 printf("Flow Control enabled. CTS = ");
 if(SioCTS(Port)) puts("ON");
 else puts("OFF");
#endif
 /* Set FIFO level */
 if( SioFIFO(Port,LEVEL_14) ) puts("INS16550 detected");
 /* clear PCL4C receive buffer */
 ErrorCheck( SioRxFlush(Port) );

 /* see TERM.H for definition of AT_COMMAND_SET */
#if AT_COMMAND_SET
 /* wait for Modem to say its ready */
 printf("Waiting for Modem DSR.");
 while( !SioDSR(Port) )
     {
      if(SioKeyPress()||SioBrkKey()) MyExit(0,"Aborted by user");
      putchar('.');
      SioDelay(18);
     }
 putchar('\n');
 /* initialize (Hayes compatible) modem */
 SendTo(Port,"!AT!!~");
 SendTo(Port,"!AT E1 S7=60 S11=60 V1 X1 Q0 S0=1!");
 if(WaitFor(Port,"OK")) printf("\nMODEM READY\n");
 else printf("\nWARNING: Expected OK not received\n");
#endif

 /* enter terminal loop */
 SioRxFlush(Port);
 while(1)
     {/* Control-BREAK ? */
      if(SioBrkKey()) MyExit(0,"User pressed Ctrl-BREAK");
      /* was key pressed ? */
      if(SioKeyPress())
          {/* read key press */
           i = SioKeyRead();
           if((char)i==ESC)
               {/* process user's request */
                ProcessESC();
                ShowMessage(strlen(BaudRate[BaudCode])+9,"'ESC for Menu' ");
                continue;
               }
           else PutChar(Port,i);
          }
      /* was break detected ? */
      if( SioBrkSig(Port,'D') ) DisplayLine("BREAK detected ",NULL,0);
      /* any incoming over serial port ? */
      i = GetChar(Port,0);
      if(i>-1)
          {/* good character */
           if(DataFlag&((i<0x20)||(i>0x7e)))
               {MyCrtWrite('^');
                MyCrtWrite('@'+i);
               }
           else MyCrtWrite(i);
          }
      /* any change in DCD or DSR ? */
      Delta = SioModem(Port,DeltaDCD|DeltaDSR);
      if(Delta)
          {/* display new status */
           Status = SioModem(Port,(char)(DCD|DSR));
           if(!Status&DeltaDCD) MyExit(0,"Dropped DSD");
           if(!Status&DeltaDSR) MyExit(0,"Dropped DSR");
          }
     } /* end -- key pressed */
}

/*** write to screen except for bottom line ***/

void MyCrtWrite(ch)
char ch;
{/* write character */
 SioCrtWrite(ch);
 /* scroll all but bottom line */
 if(GetRow()==24)
    {Scroll(0,0,23,79,1,NORMAL);
     Position(23,0);
    }
}

/*** make multiple attempts to reset port ***/

void MyStart(Port,BaudCode)
int Port;
int BaudCode;
{int i, rc;
 /* try up to 3 times to reset COM port */
 for(i=0;i<3;i++)
     {printf("Resetting COM%d at %s baud\n",Port+1,BaudRate[BaudCode]);
      if( (rc = SioReset(Port,BaudCode))==0) return;
      if(rc<0) MyExit(rc,"Error resetting port");
      SioDone(Port);
      /* display errors */
      if(rc&OverrunError) puts("Overrun Error");
      if(rc&ParityError)  puts("Parity Error");
      if(rc&FramingError) puts("Framing Error");
      if(rc&BreakDetect)  puts("Break Detect");
     }
 exit(1);
}

/*** find baud rate string in table ***/

int BaudMatch(ptr)
char *ptr;
{int i;
 /* find baud rate in table */
 for(i=0;i<10;i++) if(strcmp(BaudRate[i],ptr)==0) return(i);
 return(-1);
}

/*** user pressed Escape */

void ProcessESC()
{int i;
 int rc;
 int c1, c2;
 char Answer[2]; /* array for 1 char answer */
 int row, col;
 /* user pressed <ESC> */
 Answer[0] = '?';
 Answer[1] = '\0';
 DisplayLine("Q)uit P)rotocol S)end R)eceive: ",Answer,1);
 if(strlen(Answer)) switch(toupper(Answer[0]))
     {
      case 'P':
#if RTS_CTS_CONTROL
          DisplayLine("X) xmodem Y) ymodem G) ymodem-g: ",Answer,1);
#else
          DisplayLine("X) xmodem Y) ymodem: ",Answer,1);
#endif
          if(strlen(Answer)) switch( toupper(Answer[0]) )
               {
                case 'X':
                    Protocol = 'X';
                    ShowProtocol();
                    OneKflag = FALSE;
                    BatchFlag = FALSE;
                    NCGchar = NAK;
                    DisplayLine("Protocol = XMODEM",NULL,1);
                    break;
                case 'Y':
                    Protocol = 'Y';
                    ShowProtocol();
                    OneKflag = TRUE;
                    BatchFlag = TRUE;
                    NCGchar = 'C';
                    DisplayLine("Protocol = YMODEM",NULL,1);
                    break;
#if RTS_CTS_CONTROL
                case 'G':
                    Protocol = 'G';
                    ShowProtocol();
                    OneKflag = TRUE;
                    BatchFlag = TRUE;
                    NCGchar = 'G';
                    DisplayLine("Protocol = YMODEM-G",NULL,1);
                    break;
#endif
                default:
                    DisplayLine("Must answer X, Y, or G",NULL,1);
                    break;
               }
          break;
      case 'Q':
          MyExit(0,"User pressed ESC");
          break;
      case 'R':
          ShowMessage(strlen(BaudRate[BaudCode])+9,"'CTRL-X aborts'");
          /* XMODEM / YMODEM receive */
          if(BatchFlag)
               {do
                    {/* receive files till get empty filename */
                     RxyModem(Port,Filename,Buffer,NCGchar,BatchFlag);
                     if(SioKeyPress()) break;
                    } while(Filename[0]!='\0');
               }
          else /* not Batch */
               {DisplayLine("Enter filename:",Filename,15);
                if(strlen(Filename)==0) break;
                RxyModem(Port,Filename,Buffer,NCGchar,BatchFlag);
               }
          break;
      case 'S':
          ShowMessage(strlen(BaudRate[BaudCode])+9,"'CTRL-X aborts'");
          DisplayLine("Enter filename:",Filename,15);
          if(strlen(Filename)==0) break;
          if(BatchFlag)
               {/* YMODEM send */
                setDTA(DTAbuffer);
                if(FindFirst(Filename))
                  {SayFilename(&DTAbuffer[30]);
                   TxyModem(Port,&DTAbuffer[30],Buffer,OneKflag,BatchFlag);
                   while(FindNext())
                     {SioDelay(4);
                      SayFilename(&DTAbuffer[30]);
                      TxyModem(Port,&DTAbuffer[30],Buffer,OneKflag,BatchFlag);
                     }
                   /* send empty filename */
                   Filename[0] = '\0';
                   SioDelay(5);
                   TxyModem(Port,Filename,Buffer,OneKflag,BatchFlag);
                  }
               }
          else
               {/* XMODEM send */
                TxyModem(Port,Filename,Buffer,OneKflag,BatchFlag);
               }
          break;
     default:
          DisplayLine("Must answer Q, P, S, or R",NULL,0);
          break;
    } /* end switch */
}

/*** show protocol choosen ***/

void ShowProtocol()
{int SaveRow;
 int SaveCol;
 SaveRow = GetRow();
 SaveCol = GetCol();
 Position(24,MESSAGE_POS+strlen(BaudRate[BaudCode])+7);
 AttrWrite(Protocol,INVERSE);
 Position(SaveRow,SaveCol);
}

/*** show chosen message ***/

void ShowMessage(Pos,Msg)
int Pos;
char *Msg;
{int i;
 int SaveRow;
 int SaveCol;
 int Col;
 SaveRow = GetRow();
 SaveCol = GetCol();
 Col = MESSAGE_POS+Pos;
 for(i=0;i<strlen(Msg);i++)
   {Position(24,Col++);
    AttrWrite(Msg[i],INVERSE);
   }
 Position(SaveRow,SaveCol);
}

/*** exit program ***/

void MyExit(code,msg)
int code;
char *msg;
{int rc;
 if(code<0) SioError(code);
 /* Assert UART break & sign off */
 SioBrkSig(Port,'A');
 printf("\nTERMINATING: %s\n",msg);
 SioDelay(ONE_SECOND/2);
 SioDone(Port);
 exit(0);
}

/*** display file name in status area ***/

void SayFilename(Text)
char *Text;
{char Temp[40];
 strcpy(Temp,"Sending ");
 strcat(Temp,Text);
 DisplayLine(Temp,NULL,0);
 SioDelay(ONE_SECOND/2);
}

/*** check for error ***/

void ErrorCheck(Code)
int Code;
{/* trap PCL error codes */
 if(Code<0)
     {SioError(Code);
      exit(1);
     }
} /* end ErrorCheck */