(*********************************************)
(*                                           *)
(*      TERM.PAS          May 1992           *)
(*                                           *)
(*  TERM is a simple terminal emulator which *)
(*  features XMODEM, YMODEM, and YMODEM-G    *)
(*  file transfer                            *)
(*                                           *)
(*  Do NOT select YMODEM-G when using a null *)
(*  modem cable unless you are certain that  *)
(*  RTS & CTS are reversed -- which is       *)
(*  usually not true.                         *)
(*                                           *)
(*  This program is donated to the Public    *)
(*  Domain by MarshallSoft Computing, Inc.   *)
(*  It is provided as an example of the use  *)
(*  of the Personal Communications Library.  *)
(*                                           *)
(*********************************************)

{$I DEFINES.PAS}

program term;
uses term_io, modem_io, xymodem, xypacket, crc, crt, PCL4P;

Var
  ResetFlag : Boolean;
  Port : Integer;
  SioBuffer : array[0..2047] of Byte;

function MatchBaud(BaudRate : LongInt) : Integer;
Label 999;
const
   BaudRateArray : array[1..10] of LongInt =
       (300,600,1200,2400,4800,9600,19200,38400,57600,115200);
var
   i : Integer;
begin
   for i := 1 to 10 do if BaudRateArray[i] = BaudRate then
      begin
        MatchBaud := i - 1;
        goto 999
      end;
   (* no match *)
   MatchBaud := -1;
999: end;

procedure MyHalt( Code : Integer );
var
   RetCode : Integer;
begin
   if Code < 0 then SayError( Code,'Halting' );
   if ResetFlag then RetCode := SioDone(Port);
   writeln('*** HALTING ***');
   Halt;
end;

(* main program *)

label 500;

const
  NAK = $15;
  WrongBaud1 = 'Cannot recognize baud rate';
  WrongBaud2 = 'Must be 300,600,1200,2400,4800,9600,19200,38400,57600, or 155200';

var
  Filename : String20;
  c : Char;
  BaudRate : LongInt;
  BaudCode : Integer;
  Protocol : Char;
  Buffer  : BufferType;
  RetCode : Integer;
  TheByte : Char;
  i       : Integer;
  MenuMsg : String40;
  StatusMsg : String40;
  ResultMsg : String20;
  GetNameMsg: String40;
  OneKflag : Boolean;
  NCGbyte  : Byte;
  BatchFlag: Boolean;
  Flag : Boolean;
  Version : Integer;
begin   (* main program *)
  InitCRC;
  TextMode(BW80);
  ClrScr;
  Window(1,1,80,24);
  ResetFlag := FALSE;
  Protocol := 'X';
  OneKflag := FALSE;
  NCGbyte := NAK;
  BatchFlag := FALSE;
  MenuMsg := 'Q)uit P)rotocol S)end R)eceive: ';
  GetNameMsg := 'Enter filename: ';
  StatusMsg := 'COM? X  "ESC for menu" ';
  (* fetch PORT # from command line *)
  if ParamCount <> 2 then
    begin
      writeln('USAGE: "TERM <port> <buadrate>" ');
      halt;
    end;
  Val( ParamStr(1),Port, RetCode );
  if RetCode <> 0 then
    begin
      writeln('Port must be 1 to 4');
      Halt;
    end;
  (* COM1 = 0, COM2 = 1, COM3 = 2, COM4 = 3 *)
  Port := Port - 1;
  Val( ParamStr(2),BaudRate, RetCode );
  if RetCode <> 0 then
    begin
      writeln(WrongBaud1);
      writeln(WrongBaud2);
      Halt;
    end;
  BaudCode := MatchBaud(BaudRate);
  if BaudCode < 0 then
    begin
      writeln(WrongBaud1);
      writeln(WrongBaud2);
      halt;
    end;
  (* patch up status message *)
  StatusMsg[4] := chr($31+Port);
  Insert(ParamStr(2),StatusMsg,8);
  WriteMsg(StatusMsg,40);
  if (Port<COM1) or (Port>COM4) then
    begin
      writeln('Port must be 1 to 4');
      Halt
    end;
  (* setup 2K receive buffer *)
  RetCode := SioRxBuf(Port, Ofs(SioBuffer), Seg(SioBuffer), Size2K);
  if RetCode < 0 then MyHalt( RetCode );
  (* reset port *)
  RetCode := SioReset(Port,BaudCode);
  (* if error then try one more time *)
  if RetCode <> 0 then RetCode := SioReset(Port,BaudCode);
  (* Was port reset ? *)
  if RetCode <> 0 then
    begin
      writeln('Cannot reset COM',Port+1);
      MyHalt( RetCode );
    end;
  (* Port successfully reset *)
  ResetFlag := TRUE;
  ClrScr;
  (* show logon message *)
  WriteLn('TERM 5/1/92');
  Version := SioInfo('V');
  WriteLn('Library Version ',Version div 16,'.',Version mod 16);
  (* specify parity, # stop bits, and word length for port *)
  RetCode := SioParms(Port, NoParity, OneStopBit, WordLength8);
  if RetCode < 0 then MyHalt( RetCode );
  RetCode := SioRxFlush(Port);
  if RetCode < 0 then MyHalt( RetCode );
  (* set FIFO level if have INS16550 *)
  RetCode := SioFIFO(Port, LEVEL_8);
  if RetCode > 0 then writeln('INS16550 detected');
  (* set DTR & RTS *)
  RetCode := SioDTR(Port,SetPort);
  RetCode := SioRTS(Port,SetPort);
{$IFDEF RTS_CTS_CONTROL}
  (* enable RTS/CTS flow control *)
  RetCode := SioFlow(Port,3*18);
  WriteLn('Hardware flow control enabled');
  Write('CTS = ');
  if SioCTS(Port) > 0 then WriteLn('ON') else WriteLn('OFF');
{$ENDIF}

{$IFDEF AT_COMMAND_SET}
  (* send initialization string to modem *)
  SendTo(Port,'!AT!!~');
  SendTo(Port,'!AT E1 S7=60 S11=60 V1 X1 Q0 S0=1!');
  if WaitFor(Port,'OK') then writeln('MODEM ready')
  else writeln('WARNING: Expected OK not received');
{$ENDIF}

  (* begin terminal loop *)
  WriteMsg(StatusMsg,40);
  LowVideo;
  while TRUE do
    begin (* while TRUE *)
      (* did user press Ctrl-BREAK ? *)
      if SioBrkKey then
        begin
          writeln('User typed Ctl-BREAK');
          RetCode := SioDone(Port);
          Halt;
        end;
      (* anything incoming over serial port ? *)
      RetCode := SioGetc(Port,0);
      if RetCode < -1 then MyHalt( RetCode );
      if RetCode > -1 then write(chr(RetCode));
      (* has user pressed keyboard ? *)
      if KeyPressed then
        begin (* keypressed *)
          (* read keyboard *)
          TheByte := ReadKey;
          (* quit if user types ESC *)
          if TheByte = chr($1b) then
            begin (* ESC *)
              WriteMsg(MenuMsg,1);
              ReadMsg(ResultMsg,32,1);
              c := UpCase(ResultMsg[1]);
              case c of
                'Q':  (* QUIT *)
                   begin
                     WriteLn;
                     WriteLn('TERMINATING: User pressed <ESC>');
                     RetCode := SioDone(Port);
                     Halt;
                   end;
                'P':  (* PROTOCOL *)
                   begin
                     WriteMsg('X) xmodem, Y) ymodem, G) ymodem-g:  ',1);
                     ReadMsg(ResultMsg,35,1);
                     c := UpCase(ResultMsg[1]);
                     case c of
                       'X': (* XMODEM *)
                          begin
                            Protocol := 'X';
                            OneKflag := FALSE;
                            NCGbyte := NAK;
                            BatchFlag := FALSE;
                            WriteMsg('Protocol = XMODEM',1);
                          end;
                       'Y': (* YMODEM *)
                          begin
                            Protocol := 'Y';
                            OneKflag := TRUE;
                            NCGbyte := Ord('C');
                            BatchFlag := TRUE;
                            WriteMsg('Protocol = YMODEM',1);
                          end;
                       'G': (* YMODEM-G *)
                          begin
                            Protocol := 'G';
                            OneKflag := TRUE;
                            NCGbyte := Ord('G');
                            BatchFlag := TRUE;
                            WriteMsg('Protocol = YMODEM-G',1);
                          end;
                     end; (* case *)
                     StatusMsg[6] := Protocol;
                     WriteMsg(StatusMsg,40)
                   end;
                'S': (* Send *)
                   begin
                     WriteMsg(GetNameMsg,1);
                     ReadMsg(Filename,16,20);
                     if Length(FileName) = 0 then goto 500;
                     Flag := TxyModem(Port,Filename,Buffer,OneKflag,BatchFlag);
                     if BatchFlag then
                       begin
                         (* send empty filename *)
                         Filename := '';
                         RetCode := SioDelay(5);
                         Flag := TxyModem(Port,Filename,Buffer,OneKflag,BatchFlag);
                       end
                     end; (* Send *)
                  'R': (* Receive *)
                     begin
                       if BatchFlag then
                         repeat
                           WriteMsg('Ready for next file',1);
                           Filename := '';
                           Flag := RxyModem(Port,Filename,Buffer,NCGbyte,BatchFlag);
                         until KeyPressed or (Length(Filename) = 0)
                       else
                         begin (* not BatchFlag *)
                           WriteMsg(GetNameMsg,1);
                           ReadMsg(Filename,16,20);
                           If Length(Filename) = 0 then exit;
                           Flag := RxyModem(Port,Filename,Buffer,NCGbyte,BatchFlag);
                         end
                     end (* Receive *)
                   else WriteMsg('Bad response',1);
                   end; (* case *)
                   500:
                end; (* ESC *)
              (* send out over serial line *)
              RetCode := SioPutc(Port, TheByte );
              if RetCode < 0 then MyHalt( RetCode );
            end (* keypressed *)
      end (* while TRUE *)
end.
