*/
Stuck? Need help? Ask questions on our forums.
*/

View \ASYNC.PAS

ASYNC11 ver 1.1

Submitted By: WEBMASTER
Rating: (Not rated) (Rate It)


{****************************************************************************}
{*                                                                          *}
{*  ASYNC11 - Interrupt-driven asyncronous communications for Turbo Pascal  *}
{*                  Version 1.1 - Wedensday May 04, 1989                    *}
{*             Copyright (C) 1989, Rising Edge Data Services                *}
{*                         Author : Mark Schultz                            *}
{*                                                                          *}
{*--------------------------------------------------------------------------*}
{*                                                                          *}
{*  The routines that follow comprise a Turbo Pascal UNIT that will fully   *}
{*  impliment interrupt-driven serial communications on a fully PC-compat-  *}
{*  able computer (those machines that use the 8250 or equivalent UART      *}
{*  mapped at the standard addresses).  Full simultaneous buffering for     *}
{*  both input and output with variable buffer sizes for each port is       *}
{*  provided.  Unlike many other libraries of a similar nature, up to 4     *}
{*  ports may be active simultaneously (easily modified for more).          *}
{*                                                                          *}
{*  For further details, consult the procedure/function headers within the  *}
{*  program or check the accompanying ASYNC11.DOC file.                     *}
{*                                                                          *}
{*  This product is copyrighted (C) 1988 by Rising Edge Software, Inc. and  *}
{*  it's author, Mark Schultz.  Permission is granted for non-commercial    *}
{*  use and distribution.  Refer to the file ASYNC11.DOC for details.       *}
{*                                                                          *}
{*  Version History:                                                        *}
{*                                                                          *}
{*  V1.1 - Corrected errors that dealt with the handling of the 8259.  Also *}
{*         added compensation for "bugs" that are present in some 8250's    *}
{*         and equivalent gate arrays (Thanks to Ralph Schraven, Toshiba    *}
{*         Europa GmbH for tracking these problems down).                   *}
{*                                                                          *}
{*  V1.0 - First release                                                    *}
{*                                                                          *}
{*--------------------------------------------------------------------------*}
{*                                                                          *}
{*  Status byte definition (C_Status):                                      *}
{*                                                                          *}
{*  7   6   5   4   3   2   1   0                                           *}
{*  |   |   |   |   |   |   |   |____ Input buffer empty                    *}
{*  |   |   |   |   |   |   |________ Input buffer full                     *}
{*  |   |   |   |   |   |____________ Output buffer empty                   *}
{*  |   |   |   |   |________________ Output buffer full                    *}
{*  |   |   |   |____________________ Input buffer overflow                 *}
{*  |   |   |________________________ Output buffer overflow                *}
{*  |   |____________________________ Hard handshake active (xmit stopped)  *}
{*  |________________________________ Soft handshake active (xmit stopped)  *}
{*                                                                          *}
{*  Control byte definition (C_Ctrl):                                       *}
{*                                                                          *}
{*  7   6   5   4   3   2   1   0                                           *}
{*  |   |   |   |   |   |   |   |____ Enable RTS handshake                  *}
{*  |   |   |   |   |   |   |________ Enable CTS handshake                  *}
{*  |   |   |   |   |   |____________ Enable software handshake             *}
{*  |   |   |   |   |________________                                       *}
{*  |   |   |   |____________________                                       *}
{*  |   |   |________________________                                       *}
{*  |   |____________________________                                       *}
{*  |________________________________                                       *}
{*                                                                          *}
{****************************************************************************}

{$R-,V-,B-}
{$M 12400,9001,10000}
Unit ASYNC;

INTERFACE

{----------------------------------------------------------------------------}

Const
  C_MinBaud = 50;
  C_MaxBaud = 115200;

  { Base port addresses & interrupt usage }

  C_MaxPort = 4;
  C_MaxCom : Byte = C_MaxPort;
  C_PortAddr : Array[1..C_MaxPort] Of Word = ($03F8,$02F8,$03E8,$02E8);
  C_PortInt : Array[1..C_MaxPort] Of Byte = (4,3,4,3);

{----------------------------------------------------------------------------}

Type
  C_PointerArray = Array[1..C_MaxPort] Of Pointer;
  C_WordArray = Array[1..C_MaxPort] Of Word;
  C_ByteArray = Array[1..C_MaxPort] Of Byte;
  C_CharArray = Array[1..C_MaxPort] Of Char;
  C_BooleanArray = Array[1..C_MaxPort] Of Boolean;

{----------------------------------------------------------------------------}

Var
  C_InBufPtr,C_OutBufPtr : C_PointerArray;    { Input/output buffer pointers }
  C_InHead,C_OutHead     : C_WordArray;       { Input/output head pointers }
  C_InTail,C_OutTail     : C_WordArray;       { Input/output tail pointers }
  C_InSize,C_OutSize     : C_WordArray;       { Input/output buffer sizes }
  C_RTSOn,C_RTSOff       : C_WordArray;       { RTS assert/drop buffer points }
  C_StartChar,C_StopChar : C_CharArray;       { Soft hndshake start/stop char }
  C_Status,C_Ctrl        : C_ByteArray;       { STATUS and CONTROL registers }
  C_XL3Ptr               : C_ByteArray;
  C_PortOpen             : C_BooleanArray;    { Port open/close flags }
  C_Temp : Word;                              { Used for debugging }

{----------------------------------------------------------------------------}

{ Procedure headers - required for UNITization }

Function  ComReadCh(ComPort:Byte) : Char;
Function  ComReadChW(ComPort:Byte) : Char;
Procedure ComWriteCh(ComPort:Byte; Ch:Char);
Procedure ComWriteChW(ComPort:Byte; Ch:Char);
Procedure SetDTR(ComPort:Byte; Assert:Boolean);
Procedure SetRTS(ComPort:Byte; Assert:Boolean);
Procedure SetOUT1(ComPort:Byte; Assert:Boolean);
Procedure SetOUT2(ComPort:Byte; Assert:Boolean);
Function  CTSStat(ComPort:Byte) : Boolean;
Function  DSRStat(ComPort:Byte) : Boolean;
Function  RIStat(ComPort:Byte) : Boolean;
Function  DCDStat(ComPort:Byte) : Boolean;
Procedure SetRTSMode(ComPort:Byte; Mode:Boolean; RTSOn,RTSOff:Word);
Procedure SetCTSMode(ComPort:Byte; Mode:Boolean);
Procedure SoftHandshake(ComPort:Byte; Mode:Boolean; Start,Stop:Char);
Procedure ClearCom(ComPort:Byte; IO:Char);
Function  ComBufferLeft(ComPort:Byte; IO:Char) : Word;
Procedure ComWaitForClear(ComPort:Byte);
Procedure ComWrite(ComPort:Byte; St:String);
Procedure ComWriteln(ComPort:Byte; St:String);
Procedure ComWriteWithDelay(ComPort:Byte; St:String; Dly:Word);
Procedure ComReadln(ComPort:Byte; Var St:String; Size:Byte; Echo:Boolean);
Function  ComExist(ComPort:Byte) : Boolean;
Function  ComTrueBaud(Baud:Longint) : Real;
Procedure ComParams(ComPort:Byte; Baud:LongInt; WordSize:Byte; Parity:Char; StopBits:Byte);
Function  OpenCom(ComPort:Byte; InBufferSize,OutBufferSize:Word) : Boolean;
Procedure CloseCom(ComPort:Byte);
Procedure CloseAllComs;

{----------------------------------------------------------------------------}

IMPLEMENTATION

Uses DOS,CRT;

{$L C:\pascal\forum\ASYNC.OBJ}

Const
  C_IER = 1;                           { 8250 register offsets }
  C_IIR = 2;
  C_LCR = 3;
  C_MCR = 4;
  C_LSR = 5;
  C_MSR = 6;
  C_SCR = 7;

Var
  C_OldINTVec : C_PointerArray;        { Storage for old hardware INT vectors }
  X : Byte;                            { Used by initialization code }

{****************************************************************************}
{*                                                                          *}
{*  Procedure INT_Handler; External;                                        *}
{*                                                                          *}
{*  Hardware interrupts 3 and 4 (vectors $0B and $0C) are pointed to        *}
{*  this routine.  It is for internal use only and should NOT be called     *}
{*  directly.  Written in assembly language (see ASYNC11.ASM).              *}
{*                                                                          *}
{****************************************************************************}

Procedure INT_Handler; External;

{****************************************************************************}
{*                                                                          *}
{*  Procedure ComReadCh(ComPort:Byte) : Char; External;                     *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*                                                                          *}
{*  Returns character from input buffer of specified port.  If the buffer   *}
{*  is empty, the port # invalid or not opened, a Chr(0) is returned.       *}
{*  Written in assembly language for best possible speed (see ASYNC11.ASM)  *}
{*                                                                          *}
{****************************************************************************}

Function ComReadCh(ComPort:Byte) : Char; External;

{****************************************************************************}
{*                                                                          *}
{*  Function ComReadChW(ComPort:Byte) : Char; External;                     *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*                                                                          *}
{*  Works like ComReadCh, but will wait until at least 1 character is       *}
{*  present in the specified input buffer before exiting.  Thus, ComReadChW *}
{*  works much like the ReadKey predefined function.  Written in assembly   *}
{*  language to maximize performance (see ASYNC11.ASM)                      *}
{*                                                                          *}
{****************************************************************************}

Function ComReadChW(ComPort:Byte) : Char; External;

{****************************************************************************}
{*                                                                          *}
{*  Procedure ComWriteCh(ComPort:Byte; Ch:Char); External                   *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*  Ch:Char       ->  Character to send                                     *}
{*                                                                          *}
{*  Places the character [Ch] in the transmit buffer of the specified port. *}
{*  If the port specified is not open or nonexistent, or if the buffer is   *}
{*  filled, the character is discarded.  Written in assembly language to    *}
{*  maximize performance (see ASYNC11.ASM)                                  *}
{*                                                                          *}
{****************************************************************************}

Procedure ComWriteCh(ComPort:Byte; Ch:Char); External;

{****************************************************************************}
{*                                                                          *}
{*  Procedure ComWriteChW(ComPort:Byte; Ch:Char); External;                 *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*  Ch:Char       ->  Character to send                                     *}
{*                                                                          *}
{*  Works as ComWriteCh, but will wait until at least 1 free position is    *}
{*  available in the output buffer before attempting to place the character *}
{*  [Ch] in it.  Allows the programmer to send characters without regard to *}
{*  available buffer space.  Written in assembly language to maximize       *}
{*  performance (see ASYNC11.ASM)                                           *}
{*                                                                          *}
{****************************************************************************}

Procedure ComWriteChW(ComPort:Byte; Ch:Char); External;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SetDTR(ComPort:Byte; Assert:Boolean);                         *}
{*                                                                          *}
{*  ComPort:Byte    ->  Port # to use (1 - C_MaxCom)                        *}
{*                      Call ignored if out-of-range                        *}
{*  Assert:Boolean  ->  DTR assertion flag (TRUE to assert DTR)             *}
{*                                                                          *}
{*  Provides a means to control the port's DTR (Data Terminal Ready) signal *}
{*  line.  When [Assert] is TRUE, the DTR line is placed in the "active"    *}
{*  state, signalling to a remote system that the host is "on-line"         *}
{*  (although not nessesarily ready to receive data - see SetRTS).          *}
{*                                                                          *}
{****************************************************************************}

Procedure SetDTR(ComPort:Byte; Assert:Boolean);

Var
  P,X : Integer;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then Exit;
  P := C_PortAddr[ComPort];

  X := Port[P+C_MCR];
  If Assert Then
    X := X Or $01
  Else
    X := X And $FE;
  Port[P+C_MCR] := X;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SetRTS(ComPort:Byte; Assert:Boolean)                          *}
{*                                                                          *}
{*  ComPort:Byte    ->  Port # to use (1 - C_MaxCom)                        *}
{*                      Call ignored if out-of-range                        *}
{*  Assert:Boolean  ->  RTS assertion flag (Set TRUE to assert RTS)         *}
{*                                                                          *}
{*  SetRTS allows a program to manually control the Request-To-Send (RTS)   *}
{*  signal line.  If RTS handshaking is disabled (see C_Ctrl definition     *}
{*  and the the SetRTSMode procedure), this procedure may be used.  SetRTS  *}
{*  should NOT be used if RTS handshaking is enabled.                       *}
{*                                                                          *}
{****************************************************************************}

Procedure SetRTS(ComPort:Byte; Assert:Boolean);

Var
  P,X : Integer;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then Exit;
  P := C_PortAddr[ComPort];

  X := Port[P+C_MCR];
  If Assert Then
    X := X Or $02
  Else
    X := X And $FD;
  Port[P+C_MCR] := X;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SetOUT1(ComPort:Byte; Assert:Boolean)                         *}
{*                                                                          *}
{*  ComPort:Byte    ->  Port # to use (1 - C_MaxCom)                        *}
{*                      Call ignored if out-of-range                        *}
{*  Assert:Boolean  ->  OUT1 assertion flag (set TRUE to assert OUT1 line)  *}
{*                                                                          *}
{*  SetOUT1 is provided for reasons of completeness only, since the         *}
{*  standard PC/XT/AT configurations do not utilize this control signal.    *}
{*  If [Assert] is TRUE, the OUT1 signal line on the 8250 will be set to a  *}
{*  LOW logic level (inverted logic).  The OUT1 signal is present on pin 34 *}
{*  of the 8250 (but not on the port itself).                               *}
{*                                                                          *}
{****************************************************************************}

Procedure SetOUT1(ComPort:Byte; Assert:Boolean);

Var
  P,X : Integer;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then Exit;
  P := C_PortAddr[ComPort];

  X := Port[P+C_MCR];
  If Assert Then
    X := X Or $04
  Else
    X := X And $FB;
  Port[P+C_MCR] := X;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SetOUT2(ComPort:Byte; Assert:Boolean)                         *}
{*                                                                          *}
{*  ComPort:Byte    ->  Port # to use (1 - C_MaxCom)                        *}
{*                      Call ignored if out-of-range                        *}
{*  Assert:Boolean  ->  OUT2 assertion flag (set TRUE to assert OUT2 line)  *}
{*                                                                          *}
{*  The OUT2 signal line, although not available on the port itself, is     *}
{*  used to gate the 8250 <INTRPT> (interrupt) line and thus acts as a      *}
{*  redundant means of controlling 8250 interrupts.  When [Assert] is TRUE, *}
{*  the /OUT2 line on the 8250 is lowered, which allows the passage of the  *}
{*  <INTRPT> signal through a gating arrangement, allowing the 8250 to      *}
{*  generate interrupts.  Int's can be disabled bu unASSERTing this line.   *}
{*                                                                          *}
{****************************************************************************}

Procedure SetOUT2(ComPort:Byte; Assert:Boolean);

Var
  P,X : Integer;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then Exit;
  P := C_PortAddr[ComPort];

  X := Port[P+C_MCR];
  If Assert Then
    X := X Or $08
  Else
    X := X And $F7;
  Port[P+C_MCR] := X;
End;

{****************************************************************************}
{*                                                                          *}
{*  Function CTSStat(ComPort:Byte) : Boolean                                *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*                    Call ignored if out-of-range                          *}
{*  Returns status of Clear-To-Send line (TRUE if CTS asserted)             *}
{*                                                                          *}
{*  CTSStat provides a means to interrogate the Clear-To-Send hardware      *}
{*  handshaking line.  In a typical arrangement, when CTS is asserted, this *}
{*  signals the host (this computer) that the receiver is ready to accept   *}
{*  data (in contrast to the DSR line, which signals the receiver as        *}
{*  on-line but not nessesarily ready to accept data).  An automated mech-  *}
{*  ansim (see CTSMode) is provided to do this, but in cases where this is  *}
{*  undesirable or inappropriate, the CTSStat function can be used to int-  *}
{*  terrogate this line manually.                                           *}
{*                                                                          *}
{****************************************************************************}

Function CTSStat(ComPort:Byte) : Boolean;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then
    CTSStat := False
  Else
    CTSStat := (Port[C_PortAddr[ComPort]+C_MSR] And $10) > 0;
End;

{****************************************************************************}
{*                                                                          *}
{*  Function DSRStat(ComPort:Byte) : Boolean                                *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*                    Call ignored if out-of-range                          *}
{*  Returns status of Data Set Ready (DSR) signal line.                     *}
{*                                                                          *}
{*  The Data Set Ready (DSR) line is typically used by a remote station     *}
{*  to signal the host system that it is on-line (although not nessesarily  *}
{*  ready to receive data yet - see CTSStat).  A remote station has the DSR *}
{*  line asserted if DSRStat returns TRUE.                                  *}
{*                                                                          *}
{****************************************************************************}

Function DSRStat(ComPort:Byte) : Boolean;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then
    DSRStat := False
  Else
    DSRStat := (Port[C_PortAddr[ComPort]+C_MSR] And $20) > 0;
End;

{****************************************************************************}
{*                                                                          *}
{*  Function RIStat(ComPort:Byte) : Boolean                                 *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*                    Call ignored if out-of-range                          *}
{*                                                                          *}
{*  Returns the status of the Ring Indicator (RI) line.  This line is       *}
{*  typically used only by modems, and indicates that the modem has detect- *}
{*  ed an incoming call if RIStat returns TRUE.                             *}
{*                                                                          *}
{****************************************************************************}

Function RIStat(ComPort:Byte) : Boolean;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then
    RIStat := False
  Else
    RIStat := (Port[C_PortAddr[ComPort]+C_MSR] And $40) > 0;
End;

{****************************************************************************}
{*                                                                          *}
{*  Function DCDStat(ComPort:Byte) : Boolean                                *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom)                          *}
{*                    Call ignored if out-of-range                          *}
{*                                                                          *}
{*  Returns the status of the Data Carrier Detect (DCD) line from the rem-  *}
{*  ote device, typically a modem.  When asserted (DCDStat returns TRUE),   *}
{*  the modem indicates that it has successfuly linked with another modem   *}
{*  device at another site.                                                 *}
{*                                                                          *}
{****************************************************************************}

Function DCDStat(ComPort:Byte) : Boolean;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Then
    DCDStat := False
  Else
    DCDStat := (Port[C_PortAddr[ComPort]+C_MSR] And $80) > 0;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SetRTSMode(ComPort:Byte; Mode:Boolean; RTSOn,RTSOff:Word)     *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom).                         *}
{*                    Request ignored if out of range or unopened.          *}
{*  Mode:Boolean  ->  TRUE to enable automatic RTS handshake                *}
{*  RTSOn:Word    ->  Buffer-usage point at which the RTS line is asserted  *}
{*  RTSOff:Word   ->  Buffer-usage point at which the RTS line is dropped   *}
{*                                                                          *}
{*  SetRTSMode enables or disables automated RTS handshaking.  If [MODE] is *}
{*  TRUE, automated RTS handshaking is enabled.  If enabled, the RTS line   *}
{*  will be DROPPED when the # of buffer bytes used reaches or exceeds that *}
{*  of [RTSOff].  The RTS line will then be re-asserted when the buffer is  *}
{*  emptied down to the [RTSOn] usage point.  If either [RTSOn] or [RTSOff] *}
{*  exceeds the input buffer size, they will be forced to (buffersize-1).   *}
{*  If [RTSOn] > [RTSOff] then [RTSOn] will be the same as [RTSOff].        *}
{*  The actual handshaking control is located in the interrupt driver for   *}
{*  the port (see ASYNC11.ASM).                                             *}
{*                                                                          *}
{****************************************************************************}

Procedure SetRTSMode(ComPort:Byte; Mode:Boolean; RTSOn,RTSOff:Word);

Var
  X : Byte;

Begin
  If (ComPort<1) Or (ComPort>C_MaxPort) Or (Not C_PortOpen[ComPort]) Then Exit;

  X := C_Ctrl[ComPort];
  If Mode Then X := X Or $01 Else X := X And $FE;
  C_Ctrl[ComPort] := X;

  If Mode Then
    Begin
      If (RTSOff >= C_InSize[ComPort]) Then RTSOff := C_InSize[ComPort] - 1;
      If (RTSOn > RTSOff) Then RTSOff := RTSOn;
      C_RTSOn[ComPort] := RTSOn;
      C_RTSOff[ComPort] := RTSOff;
    End;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SetCTSMode(ComPort:Byte; Mode:Boolean)                        *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom).                         *}
{*                    Request ignored if out of range or unopened.          *}
{*  Mode:Boolean  ->  Set to TRUE to enable automatic CTS handshake.        *}
{*                                                                          *}
{*  SetCTSMode allows the enabling or disabling of automated CTS handshak-  *}
{*  ing.  If [Mode] is TRUE, CTS handshaking is enabled, which means that   *}
{*  if the remote drops the CTS line, the transmitter will be disabled      *}
{*  until the CTS line is asserted again.  Automatic handshake is disabled  *}
{*  if [Mode] is FALSE.  CTS handshaking and "software" handshaking (pro-   *}
{*  vided by the SoftHandshake procedure) ARE compatable and may be used    *}
{*  in any combination.  The actual logic for CTS handshaking is located    *}
{*  in the communications interrupt driver (see ASYNC11.ASM).               *}
{*                                                                          *}
{****************************************************************************}

Procedure SetCTSMode(ComPort:Byte; Mode:Boolean);

Var
  X : Byte;

Begin
  If (ComPort<1) Or (ComPort>C_MaxPort) Or (Not C_PortOpen[ComPort]) Then Exit;

  X := C_Ctrl[ComPort];
  If Mode Then X := X Or $02 Else X := X And $FD;
  C_Ctrl[ComPort] := X;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure SoftHandshake(ComPort:Byte; Mode:Boolean; Start,Stop:Char)    *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom).                         *}
{*                    Request ignored if out of range or unopened.          *}
{*  Mode:Boolean  ->  Set to TRUE to enable transmit software handshake     *}
{*  Start:Char    ->  START control character (usually ^Q)                  *}
{*                    Defaults to ^Q if character passed is >= <Space>      *}
{*  Stop:Char     ->  STOP control character (usually ^S)                   *}
{*                    Defaults to ^S if character passed is >= <Space>      *}
{*                                                                          *}
{*  SoftHandshake controls the usage of "Software" (control-character)      *}
{*  handshaking on transmission.  If "software handshake" is enabled        *}
{*  ([Mode] is TRUE), transmission will be halted if the character in       *}
{*  [Stop] is received.  Transmission is re-enabled if the [Start] char-    *}
{*  acter is received.  Both the [Start] and [Stop] characters MUST be      *}
{*  CONTROL characters (i.e. Ord(Start) and Ord(Stop) must both be < 32).   *}
{*  Also, <Start> and <Stop> CANNOT be the same character.  If either one   *}
{*  of these restrictions are violated, the defaults (^Q for <Start> and ^S *}
{*  for <Stop>) will be used.  Software handshaking control is implimented  *}
{*  within the communications interrupt driver (see ASYNC11.ASM).           *}
{*                                                                          *}
{****************************************************************************}

Procedure SoftHandshake(ComPort:Byte; Mode:Boolean; Start,Stop:Char);

Var
  X : Byte;

Begin
  If (ComPort<1) Or (ComPort>C_MaxPort) Or (Not C_PortOpen[ComPort]) Then Exit;

  X := C_Ctrl[ComPort];
  If Mode Then
    Begin
      X := X Or $04;
      If Start=Stop Then Begin Start := ^Q; Stop := ^S; End;
      If Start>#32 Then Start := ^Q;
      If Stop>#32 Then Stop := ^S;
      C_StartChar[ComPort] := Start;
      C_StopChar[ComPort] := Stop;
    End
  Else
    X := X And $FB;
  C_Ctrl[ComPort] := X;
End;

{****************************************************************************}
{*                                                                          *}
{*  Procedure ClearCom(ComPort:Byte); IO:Char)                              *}
{*                                                                          *}
{*  ComPort:Byte  ->  Port # to use (1 - C_MaxCom).                         *}
{*                    Request ignored if out of range or unopened.          *}
{*  IO:Char       ->  Action code; I=Input, O=Output, B=Both                *}
{*                    No action taken if action code unrecognized.          *}
{*                                                                          *}
{*  ClearCom allows the user to completely clear the contents of either     *}
{*  the input (receive) and/or output (transmit) buffers.  The "action      *}
{*  code" passed in <IO> determines if the input (I) or output (O) buffer   *}
{*  is cleared.  Action code (B) will clear both buffers.  This is useful   *}
{*  if you wish to cancel a transmitted message or ignore part of a         *}
{*  received message.                                                       *}
{*                                                                          *}
{****************************************************************************}

Procedure ClearCom(ComPort:Byte; IO:Char);

Var
  P,X : Word;

Begin
  If (ComPort<1) Or (ComPort>C_MaxCom) Or (Not C_PortOpen[ComPort]) Then Exit;

  IO := Upcase(IO);
  P := C_PortAddr[ComPort];

  Inline($FA);
  If (IO='I') Or (IO='B') Then
    Begin
      C_InHead[ComPort] := 0;
      C_InTail[ComPort] := 0;
      C_Status[ComPort] := (C_Status[ComPort] And $EC) Or $01;
      X := Port[P] + Port[P+C_LSR] + Port[P+C_MSR] + Port[P+C_IIR];
    End;
  If (IO='O') Or (IO='B') Then
    Begin
      C_OutHead[ComPort] := 0;
      C_OutTail[ComPort] := 0;
      C_Status[ComPort] := (C_Status[ComPort]