home *** CD-ROM | disk | FTP | other *** search
/ POINT Software Programming / PPROG1.ISO / pascal / swag / comm.swg / 0068_OOP Async Package.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-03-26  |  27.9 KB  |  658 lines

  1. {$O-} {This unit may >>>>NOT<<<< be overlaid}
  2. {$X+} {Extended syntax is ok}
  3. {$F+} {Allow far calls}
  4. {$A+} {Word Align Data}
  5. {$G+} {286 Code optimization - if you're using an 8088 get a real computer}
  6. {$R-} {Disable range checking}
  7. {$S+} {Enable Stack Checking}
  8. {$I-} {Disable IO Checking}
  9. {$Q-} {Disable Overflow Checking}
  10. {$D-} {Turn off debugging - use only if you modify this unit and get a bug}
  11. {$L-} {Turn off local symbols - again this unit has been thouroughly debuged}
  12. {$V-} {Turn off strict VAR strings}
  13. {$B-} {Allow short circuit boolean evaluations}
  14. {$T-} {Turn off typed @ operators}
  15.  
  16. Unit Async;
  17.  
  18. {This unit was written by Patrick Hunlock, 10/19/1994     }
  19. {Copyright @ 1994 by Patrick Hunlock - all rights reserved}
  20.  
  21. {Software License Agreement: If you use this unit you are bound by it's terms}
  22.  
  23. {You may use this unit in your programs without royalties. If you use this
  24.  unit my name and the copyright notice must appear beneath your name and
  25.  copyright notice even if you have made significant alterations to the
  26.  source code in this unit.  This source code may only be transmitted via
  27.  the telephone system.  It may not be distributed on any magnetic, optical,
  28.  or any future computer media format without prior consent of the copyright
  29.  holder.  No charge may ever be levied for the receipt of this source code
  30.  other than normal phone charges and/or normal connect charges on a BBS which
  31.  charges for access.  Any charges above and beyond what would be considered
  32.  normal access charges to download this source is considered a SALE of this
  33.  source code which is expressly prohibited by this License Agreement.
  34.  You may distribute this source code via the telephone system (I.E.
  35.  BBS or computer network) in it's original form only, you may not add or
  36.  subtract to/from it in any way shape or form.}
  37.  
  38. {This ASYNC unit implements a >COMPLETE< multi-port serial interface wrapped
  39.  in a turbo pascal object.  Baud rates up to 110,000 are supported.  Up to
  40.  four ports may be opened at a time, up to two ports may be active at any
  41.  given time (four can be active so long as they do not share interrupts).
  42.  Some considerations.  A com port which shares the same interrupt as a
  43.  serial mouse will over-ride the mouse driver for the duration of the
  44.  program.  If you have a mouse on Com1, avoid using Com3 in your program.
  45.  Likewise if you have a mouse on Com2, avoid using Com4 in your program.
  46.  A mouse on a BUS card will not be affected by this unit.  Default addresses
  47.  for the 4 com ports dictate that com1 & com3, and com2 & com4 share an
  48.  interrupt on the system bus.  As such while you can open both ports, make
  49.  sure that only - ONE of the ports on the shared interrupt has been .ENABLED
  50.  at any one time.  See the .ENABLE and .DISABLE procedures for more
  51.  information.}
  52.  
  53. Interface
  54.  
  55. Type
  56.  
  57.    Com_Port      = Object
  58.                          CPort   : Byte;          {Com Port for this port}
  59.  
  60.                       {Initializes the buffers and the object}
  61.                       Function Init(ComPort: Byte; RBufSize,TBufSize: Word): Byte;
  62.                       {Sets the baud,parity,wordsize, and stopbits}
  63.                       Procedure SetParam(Baud: Longint; WordSize: Byte;
  64.                                          Parity: Char; StopBits: Byte);
  65.                       {Used for shared interrupts}
  66.                       Procedure Disable;
  67.                       {Enable a com port for use}
  68.                       Procedure Enable;
  69.                       {Returns true if there is data waiting}
  70.                       Function Waiting: Boolean;
  71.                       {Returns a character waiting in the buffer}
  72.                       Function Read: Char;
  73.                       {Waits for a character if necessary}
  74.                       Function Readw: Char;
  75.                       {Writes a character to the transmit buffer}
  76.                       Procedure Write(C: CHar);
  77.                       {Passes a string to the transmit buffer}
  78.                       Procedure WriteS(S: String);
  79.                       {Returns true if modem is "on-line" re DCD status}
  80.                       Function OnLine: Boolean;
  81.                       {Disconnects the modem connection}
  82.                       Procedure Hangup;
  83.                       {Sends a Break Signal to the other computer}
  84.                       Procedure Break;
  85.                       {Terminates the comm port}
  86.                       Procedure Done;
  87.                    End;
  88.  
  89. Implementation
  90.  
  91. Uses DOS,       {Turbo Pascal's DOS Unit}
  92.      CRT;       {Turbo Pascal's CRT Unit}
  93.  
  94. Const
  95.                                      {Com1,Com2,Com3,Com4}
  96.    PortBases : Array[1..4] Of Word = ($3F8,$2F8,$3E8,$2E8);
  97.    Interrupts: Array[1..4] Of Byte = (   4,   3,   4,   3);
  98.  
  99.    Disable_Interrupts = $FA;       {Used in INLINE statements}
  100.    Enable_Interrupts  = $FB;       {Used in INLINE statements}
  101.  
  102. Type
  103.    {Buf Type is never actually >declared< in this unit.  It is created only
  104.     as a pointer to an area of memory which can actually only be 10 to 64000
  105.     bytes long.  However by creating the pointer in this way, the contents
  106.     of the pointer memory can be accessed like an array of chars simplifying
  107.     the coding of this unit.  If you only create a 10000 byte buffer then
  108.     it's valid to read BUFTYPE[0] through BufTYPE[10000] anything higher
  109.     is dangerous since no memory was set aside for this buffer.  However
  110.     the unit knows how much memory was set asside and it never references
  111.     any data outside of the "safe" range}
  112.    BufType = Array[0..64000] Of Char;
  113.  
  114.    {ComBufferType is the actual buffer used by the COM_ISR routine and the
  115.     record which actually makes COM_PORT work.  In reality COM_PORT is just
  116.     a shell which is wrapped around this record to give the illusion of
  117.     OOP.  Take away COM_PORT, write your own procedures to reference
  118.     ComBufferType and your program will work just fine}
  119.  
  120.    ComBufferType = Record
  121.                       Active       : Boolean;  {True if this buffer is active}
  122.                       R_Buffer     : ^BufType; {Recieve buffer pointer       }
  123.                       R_Head,R_Tail: Word;     {Buffer Head and Buffer Tail  }
  124.                       R_Size       : Word;     {Size of the recieve buffer   }
  125.                       T_Buffer     : ^BufTYpe; {Transmit Buffer Pointer      }
  126.                       T_Head,T_Tail: Word;     {Transmit Buffer Head & Tail  }
  127.                       T_Size       : Word;     {Size of the transmit buffer  }
  128.                       UART_Data    : Word;     {Uart data address            }
  129.                       UART_IER     : Word;     {Uart interrupt enable registr}
  130.                       UART_IIR     : Word;     {Uart interrupt identification}
  131.                       UART_LCR     : Word;     {UArt Line Control Register   }
  132.                       UART_MCR     : Word;     {UArt Modem COntrol Register  }
  133.                       UART_LSR     : Word;     {UArt Line Status Register    }
  134.                       UART_MSR     : Word;     {UArt Modem Status Register   }
  135.                       OLD_MCR      : Byte;     {Old Modem control register   }
  136.                       Org_Vector   : Pointer;  {Original interrupt vector    }
  137.                    End;
  138.  
  139. Var
  140.    Bufs: Array[1..4] Of ComBufferType; {This declares 4 com buffers for  }
  141.                                        {coms 1 - 4                       }
  142.  
  143. Procedure COM_ISR; Interrupt;
  144.  
  145. {COM_ISR is the main interrupt procedure which handles all the serial IO.
  146.  This procedure is called >AUTOMATICALLY< by DOS whenever data arrives at
  147.  the com port - or when it is clear to send data.  >SEVERE< restrictions
  148.  as to what you can and can not add to this procedure apply.  You can not
  149.  use WRITELN.  You can not reference any turbo pascal objects.  This unit
  150.  may not be overlaid.  Etc, Etc, Etc}
  151.  
  152. Const Ktr: Byte = 0;  {These are STATIC variables so pascal doesn't }
  153.       IIR: Byte = 0;  {constantly have to redeclare them on the heap}
  154.  
  155. Begin
  156.    For Ktr:= 1 to 4 Do begin
  157.       With Bufs[Ktr] Do Begin
  158.          If Active Then Begin
  159.             iir:= Port[UART_IIR];
  160.             While Not Odd(IIR) Do Begin
  161.                Case (iir SHR 1) Of
  162.                   0: iir:= Port[UART_MSR]; {Modem status change}
  163.                   1: If T_Head = T_Tail Then Begin    {Ok to transmit      }
  164.                         {Transmit buffer empty - disable transmit interrupt}
  165.                         Port[UART_IER]:= Port[UART_IER] And Not 2;
  166.                      End Else Begin
  167.                         Port[UART_DATA]:= Byte(T_Buffer^[T_Head]);
  168.                         Inc(T_Head);
  169.                         If T_Head > T_Size Then T_Head:= 0;
  170.                      End;
  171.                   2: Begin                  {Recieve buffer}
  172.                         R_Buffer^[R_Tail]:= Char(Port[Uart_Data]);
  173.                         Inc(R_Tail);
  174.                         If R_Tail > R_Size Then R_Tail:= 0;
  175.                         If (R_Tail = R_Head) Then Begin
  176.                            Inc(R_Head); {Overflow}
  177.                            If R_Head > R_Size Then R_Head:= 0;
  178.                         End;
  179.                      End;
  180.                   3: iir:= Port[UART_LSR]; {Line status change}
  181.                End;
  182.                iir:= Port[UART_IIR];
  183.             End;
  184.          End;
  185.       End;
  186.    End;
  187.    Port[$20]:= $20;  {We're done processing the interrupt}
  188. End;
  189.  
  190. Function Com_Port.Init(ComPort: Byte; RBufSize,TBufSize: Word): Byte;
  191.  
  192. {Init is the standard object initialization routine. You must pass the
  193.  comport (1-4) you want the object to be associated with and the buffer
  194.  size (10-64000 bytes) you want the buffer to be.  Init returns the following
  195.  codes based upon it's success or failure......
  196.  
  197.     0 - Com Port Initialized
  198.     1 - ComPort is out of range - not 1, 2, 3, or 4.
  199.     2 - ComPort is already active and in use.
  200.     3 - Buffer size is either to small (10 bytes or more) or to large
  201.         (greater than 64000) - Recieve Buffer
  202.     4 - Transmit buffer size out of range (See #3)
  203.  
  204.  Init only sets up the buffers and prepares the interrupt vector.  You must
  205.  follow this with a call to setparams (set baud rate, parity, etc), then
  206.  a call to enable.  See enable and disable especially if you are going to
  207.  use multiple comm ports simultaniously.
  208. }
  209.  
  210. Var InUse: Boolean;     {Scratch variable to check for active interrupts}
  211.     Ktr  : Byte;        {Counter Variable                               }
  212.  
  213. Begin
  214.    {Set the initial state of the return code to OK}
  215.    Init:= 0;
  216.    {Check the comport validity}
  217.    If (ComPort < 1) Or (ComPort > 4) Then Begin
  218.       Init:= 1;
  219.       Exit;
  220.    End;
  221.    {Check to see if the comport is already in use by another object}
  222.    If Bufs[ComPort].Active Then Begin
  223.       Init:= 2;
  224.       Exit;
  225.    End;
  226.    {Check to make sure the buffer size is valid}
  227.    If (RBufSize > 64000) Or (RBufSize < 10) Then Begin
  228.       Init:= 3;
  229.       Exit;
  230.    End;
  231.    If (TBufSize > 64000) Or (TBufSize < 10) Then Begin
  232.       Init:= 4;
  233.       Exit;
  234.    End;
  235.  
  236.    {Begin main setup}
  237.  
  238.    CPort:= ComPort;                        {Store the comport for future use}
  239.    Getmem(Bufs[ComPort].R_Buffer,RBufSize);{Allocate memory for the buffer  }
  240.    Bufs[ComPort].R_Size:= RBufSize;        {Store the size of the buffer    }
  241.    GetMem(Bufs[ComPort].T_Buffer,TBufSize);{Allocate transmit buffer memory }
  242.    Bufs[ComPort].T_Size:= TBufSize;        {Store the size of the buffer    }
  243.  
  244.    {This next section sets up the PORT addresses used by the comport
  245.     requested.  The base addresses are stored in PORTBASES, a constant
  246.     declared at the top of the implenetation section of this unit.  Your
  247.     program may need to change the address and/or interrupts found in
  248.     that section for serial cards with unusual addresses and interrupts.
  249.     Since PortBases and Interrupts are typed constant arrays you can
  250.     easilly add an object method to change the address or interrupt for
  251.     a given comm port}
  252.  
  253.     Bufs[ComPort].UART_DATA:= PortBases[ComPort]+0;
  254.     Bufs[ComPort].UART_IER := PortBases[ComPort]+1;
  255.     Bufs[ComPort].UART_IIR := PortBases[ComPort]+2;
  256.     Bufs[ComPort].UART_LCR := PortBases[ComPort]+3;
  257.     Bufs[ComPort].UART_MCR := PortBases[ComPort]+4;
  258.     Bufs[ComPort].UART_LSR := PortBases[ComPort]+5;
  259.     Bufs[ComPort].UART_MSR := PortBases[ComPort]+6;
  260.  
  261.  
  262.    {This next section sees if there is already an interrupt vector set for
  263.     a shared interrupt com port.  For instance COM1 and COM3 both use
  264.     interrupt 4 and COM2 and COM4 use interrupt 3.  If we are setting up
  265.     COM1, but COM3 is already up and running we don't need to do anything
  266.     since the interrupt service routine (Procedure COM_ISR) will
  267.     automatically check the com ports on it's interrupt}
  268.  
  269.    InUse:= False;
  270.    For Ktr:= 1 to 4 Do
  271.       If (Interrupts[Ktr] = Interrupts[ComPort]) And Bufs[Ktr].Active Then
  272.          InUse:= True;
  273.  
  274.    {This next section is run if, and only if a shared interrupt is not
  275.     currently running.}
  276.  
  277.    InLine(Disable_Interrupts);
  278.  
  279.    If Not InUse Then Begin
  280.       {Get the old DOS interrupt vector, save it then change it to point
  281.        to the COM_ISR procedure in this unit}
  282.       Port[$21] := Port[$21] Or (1 SHL Interrupts[ComPort]);
  283.       GetIntVec(8+Interrupts[ComPort],Bufs[ComPort].Org_Vector);
  284.       SetIntVec(8+Interrupts[ComPort],@COM_ISR);
  285.       Port [$21] := Port [$21] AND NOT (1 SHL Interrupts[ComPort]);
  286.    End;
  287.  
  288.    Bufs[ComPort].Old_MCR:= Port[Bufs[ComPort].UART_MCR]; {Store MCR        }
  289.    Port[Bufs[ComPort].UART_LCR]:= 3; {Parity to none and turn off the break}
  290.    PORT[Bufs[ComPort].UART_IER]:= 1; {Enable data recieved interrupts      }
  291.  
  292.    InLine(Enable_Interrupts);
  293.  
  294.    Bufs[ComPort].Active:= True;      {Let COM_ISR know to check this port  }
  295. End;
  296.  
  297. Procedure Com_Port.SetParam(Baud: Longint; WordSize: Byte;
  298.                              Parity: Char; StopBits: Byte);
  299.  
  300. Const
  301.       MaxBaud      = 115200;  {Maximum baud rate                            }
  302.  
  303. Var Divisor: Word;
  304.     lcr    : Byte;
  305.  
  306. {Sets the baud rate, wordsize, parity, and stop bits used by the port.  The
  307.  most common setting especially with high speed modems is 38400 baud, word
  308.  size is 8 bits (one byte), 'N' or no parity, and one stop bit.  Compuserve
  309.  uses 7 bits and even parity.}
  310.  
  311. Begin
  312.  
  313.    {This next section sets the baud rate based on the divisor of MAXBAUD}
  314.  
  315.    If Baud < 50 Then Baud:= 50;
  316.    If Baud > MaxBaud Then Baud:= MaxBaud;
  317.    Divisor:= MaxBaud Div Baud;
  318.    InLine(Disable_Interrupts);
  319.    Port [Bufs[CPort].uart_lcr ]:= Port[Bufs[Cport].uart_lcr] Or $80;
  320.    Portw[Bufs[CPort].uart_Data]:= divisor;
  321.    Port [Bufs[CPort].uart_lcr] := Port[Bufs[CPort].uart_lcr] And NOT $80;
  322.    InLine(Enable_INterrupts);
  323.  
  324.    {This next section sets the parity}
  325.  
  326.    Case upcase(Parity) Of
  327.       'N': lcr:= $00 or $03;
  328.       'E': lcr:= $18 or $02;
  329.       'O': lcr:= $08 Or $02;
  330.       'S': lcr:= $38 Or $02;
  331.       'M': lcr:= $28 OR $02;
  332.       Else
  333.          Lcr:= $00 or $03;
  334.    End;
  335.    If StopBits = 2 Then lcr:= Lcr OR $04;
  336.  
  337.    InLine(Disable_Interrupts);
  338.    Port[Bufs[CPort].Uart_lcr]:= Port[Bufs[CPort].uart_lcr] And $40 Or LCR;
  339.    InLine(Enable_Interrupts);
  340. End;
  341.  
  342. Procedure Com_Port.Done;
  343.  
  344. {Use this procedure when you are done with your program.  You >MUST< run
  345.  this procedure for each COM variable you have initialized.  If you don't
  346.  if any data comes in to the comm port DOS will still try to go to the
  347.  place where the COM_ISR >>>USED<<< to be!  Meaning your computer >COULD<
  348.  crash.  Since this is an OOP approach exit-proc wasn't used since there
  349.  could be any number of variables open and running.  Therefore it is YOUR
  350.  responsibility to call this procedure for each COM_PORT object you have
  351.  created}
  352.  
  353. Var InUse: Boolean;     {Scratch variable to test for shared interrupt}
  354.     Ktr  : byte;        {Counter variable                             }
  355.  
  356. Begin
  357.    {Check for shared interrupt usage}
  358.  
  359.    InUse:= False;
  360.    For Ktr:= 1 to 4 Do
  361.       If (Interrupts[Ktr] = Interrupts[CPort]) And Bufs[Ktr].Active Then
  362.          InUse:= True;
  363.  
  364.    InLine(Disable_interrupts);
  365.  
  366.    {Restore the old Modem Control Register and disable incomming data
  367.     interrupts}
  368.    Port[Bufs[CPort].UART_MCR] := Bufs[CPort].Old_MCR;
  369.    Port[Bufs[CPort].UART_IER] := 0;
  370.  
  371.  
  372.    If Not InUse Then Begin
  373.       {Remove the interrupt only if another object is not using it}
  374.       Port[$21] := Port[$21] Or ($01 SHR Interrupts[CPort]);
  375.       SetIntVec(8+Interrupts[CPort],Bufs[CPort].Org_Vector);
  376.    End;
  377.  
  378.    InLine(Enable_Interrupts);
  379.  
  380.    CPort:= 0;                {Set CPort variable to 0     }
  381.  
  382.    {Release the buffer memory and set the active flag to false}
  383.    Freemem(Bufs[CPort].R_Buffer,Bufs[CPort].R_Size);
  384.    Freemem(Bufs[CPort].T_Buffer,Bufs[CPort].T_Size);
  385.    Bufs[CPort].Active:= False;
  386. End;
  387.  
  388. Function Com_Port.Read: Char;
  389.  
  390. {If a character is available on the buffer, this procedure will return the
  391.  char.  If there is no character, char 0 (#0) will be returned.  If you
  392.  expect #0 to be passed as part of the data call this routine only if
  393.  function waiting returns true, or use readw - which will wait for
  394.  a character from the com port if nothing is available}
  395.  
  396. Begin
  397.    With Bufs[CPort] Do Begin
  398.       If R_Head = R_Tail Then Begin  {Nothing in the buffer}
  399.          Read:= #0;
  400.       End Else Begin                 {Data waiting in the buffer}
  401.          Read:= R_Buffer^[R_Head];          {Get the waiting character}
  402.          Inc(R_Head);                       {Increment the queue pointer}
  403.          If R_Head > R_Size Then R_Head:= 0; {Check for out of range}
  404.       End;
  405.    End;
  406. End;
  407.  
  408. Function Com_Port.Readw: Char;
  409.  
  410. {Waits for a character from the comm port if none are available.  Passes
  411.  back the first character it finds in the recieve buffer}
  412.  
  413.  Begin
  414.     With Bufs[CPort] Do Begin
  415.        While R_Head = R_Tail Do;
  416.        Readw:= R_Buffer^[R_Head];
  417.        Inc(R_Head);
  418.        If R_Head > R_Size THen R_Head:= 0;
  419.     End;
  420.  End;
  421.  
  422. Function Com_Port.Waiting: Boolean;
  423.  
  424. {This function returns TRUE if there is data waiting in the recieve buffer}
  425.  
  426. Begin
  427.    Waiting:= (Bufs[CPort].R_Head <> Bufs[CPort].R_Tail);
  428. End;
  429.  
  430. Procedure Com_Port.Enable;
  431.  
  432. {This should be the third command you run after .INIT and .SETPARAM.  Enable
  433.  and Disable are provided for multi-port use.  If you are using com1 and
  434.  com2 together you can leave both ports enabled at the same time, likewise
  435.  with com3 and com4.  However ports that share interrupts (Com1 & Com3)
  436.  (com2 & com4) can not both be enabled at the same time.  This is the reason
  437.  I wrote this unit because the most popular pascal com libraries
  438.  (particularly the async libraries by rising sun which are otherwise
  439.  extrodinarilly compitent packages) could not handle shared interrupts.
  440.  This unit can, but it cheats by allowing you to "suspend" one of the ports
  441.  on the shared interrupt.  While a port is suspended (disabled) you can not
  442.  send or recieve data from that port.  Other considerations - a mouse on
  443.  com1 will not work with this package when you use com3, likewise a mouse
  444.  on com2 will not work when you run com4 this is because this package
  445.  installs it's own interrupts - overwriting the mouse ports (although the
  446.  mouse will begin working again once you call .DONE}
  447.  
  448. Begin
  449.    InLine(Disable_interrupts);
  450.    Port[Bufs[CPort].Uart_MCR]:= 11;
  451.    InLine(Enable_Interrupts);
  452. End;
  453.  
  454. Procedure Com_Port.Disable;
  455.  
  456. {Call this procedure only if you are about to enable another port which
  457.  uses the same interrupt, see COM_PORT.Enable for more information}
  458.  
  459. Begin
  460.    InLine(Disable_interrupts);
  461.    Port[Bufs[CPort].Uart_MCR]:= 3;
  462.    InLine(Enable_Interrupts);
  463. End;
  464.  
  465. Procedure Com_Port.Write(C: Char);
  466.  
  467. {This procedure places a character on the transmit buffer}
  468.  
  469. Begin
  470.    With Bufs[CPort] Do Begin
  471.       T_Buffer^[T_Tail]:= C;
  472.       Inc(T_Tail);
  473.       If T_Tail > T_Size Then T_Tail:=0;
  474.       If (T_Tail = T_Head) Then Begin
  475.          Inc(T_Head); {Overflow}
  476.          If T_Head > T_Size Then T_Head:=0;
  477.       End;
  478.       InLine(Disable_interrupts);
  479.       {Tell the modem to alert us when it is OK to send data}
  480.       Port[UART_IER]:= Port[UART_IER] Or 2;
  481.       InLine(Enable_Interrupts);
  482.    End;
  483. End;
  484.  
  485. Procedure Com_Port.WriteS(S: String);
  486.  
  487. {Passes a string to the transmit buffer}
  488.  
  489. Var Ktr: Byte;
  490.  
  491. Begin
  492.    For Ktr:= 1 to Length(S) Do
  493.       Write(S[Ktr]);
  494. End;
  495.  
  496. Procedure Com_Port.Break;
  497.  
  498. {This procedure sends a >BREAK< signal down the line.  It is usefull
  499.  primarilly for mainframe and unix based systems, DOS based machines
  500.  do not look for or respond to the break signal.}
  501.  
  502. Var
  503.    Org_Data: byte;
  504.  
  505. Begin
  506.    InLine(Disable_Interrupts);
  507.    With Bufs[CPort] Do Begin
  508.       Org_Data:= Port[UART_LCR];   {Save the contents of the LCR            }
  509.       Port[UART_LCR]:= 255;        {Load up the Line Control Register       }
  510.       Delay(3);                    {CRT unit - Delay 3 thousands of a second}
  511.       Port[UART_LCR]:= Org_Data;   {Restore the Line Control Register       }
  512.    End;
  513.    InLine(Enable_Interrupts);
  514. End;
  515.  
  516. Function Com_Port.OnLine: Boolean;
  517.  
  518. {This function returns TRUE if the Modem Status Register indicates a Data
  519.  carrier detect signal.  Note that some modems always return true even when
  520.  not connected, an AT command is needed to force DCD to return the true state
  521.  of the modem.  Also note that some direct serial connections (I.E. no modem
  522.  but hardwired to another machine, may not return the correct DCD stat or
  523.  may be false even when connected - this is particularly true of three wire
  524.  direct serial connections (pins 2, 3, and 7 wired all others unwired)}
  525.  
  526. Begin
  527.     OnLine := (Port[Bufs[CPort].UART_MSR] And $80) > 0;
  528. End;
  529.  
  530. Procedure Com_Port.Hangup;
  531.  
  532. {This procedure disconects the modem by lowering the DTR signal.  Note that
  533.  some modems may not be affected by this procedure based on their AT
  534.  configurations.  Direct serial lines are not usually affected by this
  535.  signal.  Your best bet is to issue this command then send '+++' to the modem
  536.  and wait five seconds and then issue 'ATH<return>'}
  537.  
  538. Var Org_MCR: Byte;  {Scratch var to store the original MCR stuff}
  539.  
  540. Begin
  541.   With Bufs[CPort] Do Begin
  542.      Org_Mcr := Port[UART_MCR];
  543.      Port[UART_MCR]:= Org_MCR Or $FE;  {Lower the DTR signal  }
  544.      Delay(100);                       {Delay 100 ms          }
  545.      Port[UART_MCR]:= Org_MCR;         {Restore the DTR Signal}
  546.   End;
  547. End;
  548.  
  549. Var Ktr: Byte;
  550.  
  551. Begin
  552.  
  553.    {Initialize the 4 comm buffers to an inactive state, this code is run
  554.     the moment you start the program, automatically}
  555.  
  556.    For Ktr:= 1 to 4 Do Begin
  557.       Bufs[Ktr].Active  := False;
  558.       Bufs[Ktr].R_Buffer:= Nil;
  559.       Bufs[Ktr].R_Head  := 0;
  560.       Bufs[Ktr].R_Tail  := 0;
  561.       Bufs[Ktr].R_Size  := 0;
  562.       Bufs[Ktr].T_Buffer:= Nil;
  563.       Bufs[Ktr].T_Head  := 0;
  564.       Bufs[Ktr].T_Tail  := 0;
  565.       Bufs[Ktr].T_Size  := 0;
  566.    End;
  567. End.
  568.  
  569. {This area is ignored by turbo pascal}
  570.  
  571. Sample program:
  572.  
  573. Program Sample;
  574.  
  575. Uses Async,CRT;
  576.  
  577. Var
  578.    C: Char;
  579.    Com1: Com_Port;
  580.  
  581. Begin
  582.    Com1.Init(1,10000,10000);            {Set up the buffers & Interrupt    }
  583.    Com1.SetParam(38400,8,'N',1);        {Set up the baud,ws,parity,&stopbts}
  584.    Com1.Enable;                         {Enable the com port               }
  585.    C:= ' ';                             {Initialize the scratch variable   }
  586.    WriteLn('Press ESC to exit dumb terminal');
  587.    Loop
  588.       If Keypressed Then Begin
  589.         C:= Readkey;
  590.         If C <> #27 Then Com1.Write(C);
  591.      End;
  592.      If Com1.Waiting Then Write(Com1.Read);
  593.   Until C = #27;
  594.   Com1.Done;
  595. End.
  596. }
  597.  
  598. {An explination of the UART data areas   The PORT address is offset by the
  599.  numbers.  So if you're on com1 and com1 as at 3f8, the uart_IER address is
  600.  at port address 3f8+1, uart_iir is 3f8+2, etc}
  601.  
  602.       uart_data    = 0;       {Uart Data Offset                             }
  603.                                  {Transmit/Recieve Data area                }
  604.       uart_ier     = 1;       {Uart Interrupt Enable Register               }
  605.                                  {Bits 7-4 always 0                         }
  606.                                  {Bit    3  1 = enable change in modem stat }
  607.                                  {Bit    2  1 = enable line-status interrupt}
  608.                                  {Bit    1  1 = enable transmit reg empty   }
  609.                                  {Bit    0  1 = data available interrupt    }
  610.       uart_iir     = 2;       {Uart Interrupt Identification Register       }
  611.                                  {Bits 7-3 always 0                         }
  612.                                  {Bits 2-1 01 = transmit - register empty   }
  613.                                  {         10 = data available              }
  614.                                  {         11 = line status                 }
  615.                                  {Bit    0  1 = No Interrupt pending        }
  616.                                  {          0 = Interrupt Pending           }
  617.       uart_lcr     = 3;       {Uart Line Control Register}
  618.                                  {Bit    7  0 = Normal, 1=Address Baud rate }
  619.                                  {Bit    6  0 = break disabled, 1 enabled   }
  620.                                  {Bit    5  0 = Don't force parity          }
  621.                                  {          1 = if bit 4-3 = 01 parity = 1  }
  622.                                  {              if bit 4-3 = 11 parity = 0  }
  623.                                  {              if bit   3 = 0 no parity    }
  624.                                  {Bit    4  0 = odd parity,1=even parity    }
  625.                                  {Bit    3  0 = no parity, 1=parity         }
  626.                                  {Bit    2  0 = 1 stop bit                  }
  627.                                  {          1 = 1.5 stop bits if 5bits/char }
  628.                                  {              or 2 stop bits if 6-8 bits  }
  629.                                  {Bits 1-0 00 = 5 bits/character            }
  630.                                  {         01 = 6 bits/character            }
  631.                                  {         10 = 7 bits/character            }
  632.                                  {         11 = 8 bits/character            }
  633.       uart_mcr     = 4;       {Uart Modem Control Register                  }
  634.                                  {Bits 7-5 always 0                         }
  635.                                  {Bit    4  0=normal,1=loop back test       }
  636.                                  {Bit    3  1=interrupts to system bus      }
  637.                                  {Bit    2  user designated output          }
  638.                                  {Bit    1  1=active rts                    }
  639.                                  {Bit    0  1=active dtr                    }
  640.       uart_lsr     = 5;       {Uart line status register                    }
  641.                                  {Bit    7  always 0                        }
  642.                                  {Bit    6  1=transmit shift reg is empty   }
  643.                                  {Bit    5  1=transmit hold reg is empty    }
  644.                                  {Bit    4  1=break recieved                }
  645.                                  {Bit    3  1=framing error recieved        }
  646.                                  {Bit    2  1=parity error recieved         }
  647.                                  {Bit    1  1=overrun error recieved        }
  648.                                  {Bit    0  1=data received                 }
  649.       uart_msr     = 6;       {Uart Modem Status Register                   }
  650.                                  {Bit    7  1=recieve line signal detect    }
  651.                                  {Bit    6  1=ring indicator                }
  652.                                  {Bit    5  1=data signal ready             }
  653.                                  {Bit    4  1=clear to send                 }
  654.                                  {Bit    3  1=recieve line signal change    }
  655.                                  {Bit    2  1=ring indicator has changed    }
  656.                                  {Bit    1  1=dsr has changed state         }
  657.                                  {Bit    0  1=cts has changed state         }
  658.