home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / PROG / PASCAL / COMM_TP5.ZIP / COMM_TP4.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-11-15  |  62.7 KB  |  574 lines

  1. UNIT Comm_TP4;                                                                  {-This is the comment column }
  2.                                                                                 { which will be used to tell }
  3. {         Comm_TP4.PAS Ver. 1.50 - RS-232 Support for IBM Compatibles         } { what the program is doing  }
  4. {                             (c) Copyright, 1989                             } { where its not self-evident }
  5. {                              Kevin R. Bulgrien                              } {                            }
  6. {                                October, 1989                                } { Yes, it is wider than 80   }
  7. {                                                                             } { columns so you will need   }
  8. { See the accompanying file COMM_TP4.DOC for specific information regarding   } { to use compressed print to }
  9. { the distribution policies and usage information for this source code file.  } { print it out.              }
  10. {                                                                             } {                            }
  11. { Written by:  Kevin R. Bulgrien           Version 1.50 completed 11/13/89    } { I hope you don't complain  }
  12. {                                                                             } { too much, because I think  }
  13. { Contact at:  LeTourneau University       LeTourneau University BBS          } { the code documentation is  }
  14. {              Microcomputer Services      2400/1200/300 Baud                 } { much easier to read when   }
  15. {              P.O. Box 7001               (214) 237-2742                     } { it is not intermingled     }
  16. {              Longview, TX  75607                                            } { with the code.             }
  17. {                                                                             } {                            }
  18. { This program works with Turbo Pascal 4.0 and 5.x.  See Comm_TP4.DOC for the } { No code extends beyond the }
  19. { instructions.  Comm_TP3, by the same author, works under Turbo Pascal 3.0,  } { 80th column, so a simple   }
  20. { and Comm_TC2 works with Turbo C.  Upcoming is a Turbo Assembler Comm_TA1.   } { program could be used to   }
  21. {                                                                             } { delete this column from    }
  22. { This software directly accesses the 8250 UART as well as the 8259 interrupt } { the source code.           }
  23. { controller hardware.  Though they are IBM's standard, it is possible that   } {                            }
  24. { some manufacturers could use different hardware to perform these functions. } {                            }
  25. {                                                                             } {                            }
  26. {$S-} { Interrupt handlers should not be compiled with stack checking enabled } {-Else the system may crash! }
  27.                                                                                 {                            }
  28. {$DEFINE ErrorChecking}                                                         {-Enable/Disable Error Check }
  29. {$DEFINE NoMessageCode}                                                         {-Enable/Disable Error Msgs  }
  30. {$DEFINE FWriteCOM}                                                             {-WriteCOM is a Func or Proc }
  31.                                                                                 {                            }
  32. INTERFACE                                                                       {                            }
  33.                                                                                 {                            }
  34. USES DOS, CRT;                                                                  {                            }
  35.                                                                                 {                            }
  36. CONST                                                                           {                            }
  37.   MaxPorts = 2;                                                                 {-Max # of COM ports to use  }
  38.   MaxInSize = 255;                                                              {-Maximum input buffer size  }
  39.   MaxOutSize = 511;                                                             {-Maximum output buffer size }
  40.                                                                                 {                            }
  41.   THR = 0;    { Transmit Holding Register  }                                    {-These constants are used   }
  42.   RHR = 0;    { Receive Holding Register   }                                    { to make the code readable. }
  43.   DLL = 0;    { Divisor Latch Register LSB }                                    { They are the offsets from  }
  44.   IER = 1;    { Interrupt Enable Register  }                                    { the base address of the    }
  45.   DLM = 1;    { Divisor Latch Register MSB }                                    { 8250 UART. If, for example,}
  46.   IIR = 2;    { Interrupt ID Register      }                                    { you need the value of the  }
  47.   LCR = 3;    { Line Control Register      }                                    { #3 Line Status Register,   }
  48.   MCR = 4;    { Modem Control Register     }                                    { you could get it using:    }
  49.   LSR = 5;    { Line Status Register       }                                    {                            }
  50.   MSR = 6;    { Modem Status Register      }                                    {  PORT [COMPort [3] + LSR]  }
  51.                                                                                 {                            }
  52.   { The following declarations are crucial to the operation of this program. }  { Improper setting of the    }
  53.   { I would advise you not to change the information unless you are sure you }  { IRQNmbr array may cause a  }
  54.   { know what you are doing.  See the .DOC file for further information. For }  { system crash!              }
  55.   { standard MaxPorts settings of 1 - 4, move the comment bracket as needed. }  {                            }
  56.                                                                                 {                            }
  57.   COMNmbr  : ARRAY [1..MaxPorts] OF BYTE    = (     1,     2); {,     3,     4 );-Define the COM port number }
  58.   COMPort  : ARRAY [1..MaxPorts] OF WORD    = ( $03F8, $02F8); {, $03E8, $02E8 );-Base addresses 8250 Regs   }
  59.   IRQNmbr  : ARRAY [1..MaxPorts] OF BYTE    = (     4,     3); {,     4,     3 );-IRQ numbers of the ports   }
  60.   ChainInt : ARRAY [1..MaxPorts] OF BOOLEAN = ( FALSE, FALSE); {, FALSE, FALSE );-When port interrupt done,  }
  61.                                                                                 { jump to OldIntVector [x]?  }
  62. TYPE                                                                            {                            }
  63.   BaudType = (B110,B150,B300,B600,B1200,B2400,B4800,B9600,B19200,B38400);       {-Baud rates supported       }
  64.   ParityType = (None, Odd, Null, Even, MarkOff, Mark, SpaceOff, Space);         {-Parity types supported     }
  65.   ProcNameType = STRING [20];                                                   {-Used by error checking code}
  66.                                                                                 {                            }
  67. VAR                                                                             {                            }
  68.   Framing, Overrun, Parity, Break : ARRAY [1..MaxPorts] OF WORD;                {-Port error counters        }
  69.   OutBuffer : ARRAY [1..MaxPorts, 0..MaxOutSize] OF BYTE;                       {-Port output buffers        }
  70.   InBuffer : ARRAY [1..MaxPorts, 0..MaxInSize] OF BYTE;                         {-Port input buffers         }
  71.   CTS, DSR, RI, CD : ARRAY [1..MaxPorts] OF BOOLEAN;                            {-Port input line status     }
  72.   OutHead, OutTail : ARRAY [1..MaxPorts] OF WORD;                               {-Output buffer pointers     }
  73.   InHead, InTail : ARRAY [1..MaxPorts] OF WORD;                                 {-Input buffer pointers      }
  74.   IntInstalled : ARRAY [1..MaxPorts] OF BOOLEAN;                                {-TRUE if interrupt in place }
  75.   DTR_RTS : ARRAY [1..MaxPorts] OF BOOLEAN;                                     {-Allowed to alter DTR/RTS?  }
  76.   ErrorCode, ErrorPort : BYTE;                                                  {-Error type code & the port }
  77.   {$IFNDEF NoMessageCode}                                                       { which had the error.       }
  78.     ErrMsgX, ErrMsgY : BYTE;                                                    {-Error message coordinates  }
  79.     ShowMessages : BOOLEAN;                                                     {-FALSE disables the error   }
  80.   {$ENDIF}                                                                      { messages/TRUE enables them.}
  81.                                                                                 {                            }
  82. PROCEDURE DisableInts; INLINE ($FA);                                            {-Disable hardware interrupts}
  83. PROCEDURE EnableInts; INLINE ($FB);                                             {-Enable hardware interrupts }
  84. PROCEDURE Set_DTR_RTS (Com : BYTE; Status : BOOLEAN);                           {                            }
  85. PROCEDURE SetupCOMPort (Com, Baud, DataBits, Parity, StopBits : BYTE);          {                            }
  86. PROCEDURE InstallInt (Com : BYTE);                                              {                            }
  87. PROCEDURE RemoveInt (Com : BYTE);                                               {                            }
  88. PROCEDURE EmptyBuffer (Buffer : BYTE; TrueInFalseOut : BOOLEAN);                {                            }
  89.                                                                                 {                            }
  90. {$IFDEF FWriteCOM}                                                              {                            }
  91. FUNCTION WriteCOM (Com : BYTE; Data : STRING) : BOOLEAN;                        {                            }
  92. {$ELSE}                                                                         {                            }
  93. PROCEDURE WriteCOM (Com : BYTE; Data : STRING);                                 {                            }
  94. {$ENDIF}                                                                        {                            }
  95.                                                                                 {                            }
  96. PROCEDURE IWriteCOM (Com : BYTE; Data : STRING);                                {                            }
  97. FUNCTION  ReadCOM (Com : BYTE) : CHAR;                                          {                            }
  98. FUNCTION  TimedReadCOM (Com : BYTE; VAR Data : CHAR) : BOOLEAN;                 {                            }
  99.                                                                                 {                            }
  100. IMPLEMENTATION                                                                  {                            }
  101.                                                                                 {                            }
  102. VAR                                                                             {                            }
  103.   OldIntVector : ARRAY [1..MaxPorts] OF POINTER;                                {-Original COMx int. vectors }
  104.   IRQMask, Loop : BYTE;                                                         {                            }
  105.   ExitSave : POINTER;                                                           {-Saves original ExitProc    }
  106.                                                                                 {                            }
  107. { This procedure sets the global error identification variables ErrorCode and ErrorPort.  It also prints an  }
  108. { appropriate error message if the $DEFINE NoMessageCode directive was not present at compile time and if    }
  109. { ShowMessages is TRUE at run-time.  The error messages are displayed at the current cursor position when    }
  110. { ErrMsgX and/or ErrMsgY = 0 (Default).  If the calling program needs the messages at a specific location,   }
  111. { it must set ErrMsgX and ErrMsgY to the desired screen coordinates.  Error messages consist of a beep, the  }
  112. { procedure or function in which the error took place, the port number, and a description of the problem     }
  113. { encountered.  At all times, ErrorCode and ErrorPort can also be used to find error conditions.  ErrorCode  }
  114. { is 0 for no errors, or a value from 1-5 describing the error.  ErrorPort is the handler number which had   }
  115. { the problem.  These values are valid for the last serial operation requested.                              }
  116.                                                                                 {                            }
  117. PROCEDURE MakeError (Code, Port : BYTE; ProcName : ProcNameType);               {                            }
  118. BEGIN                                                                           {                            }
  119.   ErrorCode := Code;                                                            {-Set the global error type  }
  120.   ErrorPort := Port;                                                            { and port variables.        }
  121.   {$IFNDEF NoMessageCode}                                                       {                            }
  122.     IF ShowMessages                                                             {                            }
  123.       THEN BEGIN                                                                {                            }
  124.              IF (ErrMsgX > 0) THEN GOTOXY (ErrMsgX, WHEREY);                    {-Print error messages. A 0  }
  125.              IF (ErrMsgY > 0) THEN GOTOXY (WHEREX, ErrMsgY);                    { coordinate uses current    }
  126.              WRITE (ProcName, ' ERROR: ', #7);                                  { cursor position.           }
  127.              CASE Code OF                                                       {                            }
  128.                1 : WRITELN ('Invalid port # ', Port);                           { 1 <= Good Port <= MaxPorts }
  129.                2 : WRITELN ('Port # ', Port, 'already installed');              {-Use InstallInt once/port   }
  130.                3 : WRITELN ('Port # ', Port, 'not installed yet');              {-RemoveInt w/o InstallInt   }
  131.                4 : WRITELN ('Timeout writing port # ', Port);                   {-WriteCOM error             }
  132.                5 : WRITELN ('Timeout reading port # ', Port);                   {-TimedReadCOM error         }
  133.              END;                                                               {                            }
  134.            END;                                                                 {                            }
  135.   {$ENDIF}                                                                      {                            }
  136. END;                                                                            {                            }
  137.                                                                                 {                            }
  138. { This function is used to make sure that the requested ports are valid and if the interrupt handlers are    }
  139. { properly installed or uninstalled.  It calls MakeError to set the global error variables ErrorCode and     }
  140. { ErrorPort, and to print error messages.  Status should be set to 0 if the port handler should not be       }
  141. { installed yet, and 1 if it is supposed to be installed already.  Use a status of -1 if the installation    }
  142. { status is not critical.  ProcName is used to pass the procedure or function name to MakeError so it can be }
  143. { used in an error message. A TRUE is returned if everything checks out okay, otherwise a FALSE is returned. }
  144.                                                                                 {                            }
  145. FUNCTION ValidPort (Port:BYTE; Status:INTEGER; ProcName:ProcNameType):BOOLEAN;  {                            }
  146. BEGIN                                                                           {                            }
  147.   ErrorCode := 0;                                                               {-Default of no errors found }
  148.   IF (Port < 1) OR (Port > MaxPorts)                                            {-Check requested port # for }
  149.     THEN MakeError (1, Port, ProcName)                                          { validity.                  }
  150.     ELSE IF (Status >= 0) AND (ORD (IntInstalled [Port]) <> Status)             {-Check port installation    }
  151.            THEN MakeError (2+Status, Port, ProcName);                           { state with needed status.  }
  152.   ValidPort := (ErrorCode = 0);                                                 {-Returns TRUE if no errors  }
  153. END;                                                                            {                            }
  154.                                                                                 {                            }
  155. { This procedure changes the setting of DTR & RTS if the global variable DTR_RTS is set to TRUE.  The port   }
  156. { and the desired state are passed as parameters.  TRUE for on, FALSE for off.  Since hardware handshaking   }
  157. { requirements vary according to the hardware being used, you may have to rewrite Set_DTR_RTS to accommodate }
  158. { the hardware.  Bit 0 controls DTR and bit 1 controls RTS.  Writing a 0 to the bit will turn the line on.   }
  159.                                                                                 {                            }
  160. PROCEDURE Set_DTR_RTS (Com : BYTE; Status : BOOLEAN);                           {                            }
  161. BEGIN                                                                           {                            }
  162.   {$IFDEF ErrorChecking}                                                        {                            }
  163.     IF NOT ValidPort (Com, -1, 'Set_DTR_RTS') THEN EXIT;                        {-Optional error trapping    }
  164.   {$ENDIF}                                                                      {                            }
  165.   IF DTR_RTS [Com]                                                              {-Provided to allow modem    }
  166.     THEN IF Status                                                              { programs to prevent the    }
  167.            THEN PORT [COMPort [Com]+MCR] := PORT [COMPort [Com]+MCR] AND $FC    { modem from being hung up   }
  168.            ELSE PORT [COMPort [Com]+MCR] := PORT [COMPort [Com]+MCR] OR $03;    { during port setups due to  }
  169. END;                                                                            { dropping DTR and/or RTS.   }
  170.                                                                                 {                            }
  171. { This procedure sets up a selected serial port to use specified parameters. Com specifies the port to set   }
  172. { up.  The Baud parameter must be in the range 0 to 9, and is not range checked, but its maximum valid value }
  173. { is determined by the number of entries in BaudTable.  BaudType documents the baud rates supported by       }
  174. { BaudTable.  ParityType is provided to document the parity settings allowed.  Use ORD() to get the correct  }
  175. { value to pass: ORD(B110) returns the BYTE that selects 110 baud and ORD(None) gives the value that selects }
  176. { no parity.  1.5 stop bits are used when StopBits = 2 AND DataBits = 5, otherwise StopBits will set the     }
  177. { indicated number of stop bits in the range 1 to 2.  DataBits may be set with 5 to 8 for the number of data }
  178. { bits to use.  Mark parity means that the parity bit is always set to 0. Space parity means that the parity }
  179. { bit is always set to 1.  MarkOff, SpaceOff, NONE, & NULL all disable parity but are here for completeness. }
  180.                                                                                 {                            }
  181. PROCEDURE SetupCOMPort (Com, Baud, DataBits, Parity, StopBits : BYTE);          {                            }
  182. CONST BaudTable : ARRAY [0..9] OF WORD = ($0417, $0300, $0180, $00C0, $0060,    { Set baud rate of 8250 when }
  183.                                           $0030, $0018, $000C, $0006, $0003);   { written to DLL & DLM.      }
  184. VAR                                                                             {                            }
  185.   Temporary : BYTE;                                                             {                            }
  186. BEGIN                                                                           {                            }
  187.   {$IFDEF ErrorChecking}                                                        {                            }
  188.     IF NOT ValidPort (Com, -1, 'SetupCOMPort') THEN EXIT;                       {-Optional error trapping    }
  189.   {$ENDIF}                                                                      {                            }
  190.   Set_DTR_RTS (Com, FALSE);                                                     {                            }
  191.   PORT [COMPort [Com] + LCR] := PORT [COMPort [Com] + LCR] OR $80;              {-Set DLL & DLM active       }
  192.   PORT [COMPort [Com] + DLL] := LO (BaudTable [Baud]);                          {-Set the baud rate with the }
  193.   PORT [COMPort [Com] + DLM] := HI (BaudTable [Baud]);                          { predefined divisor values. }
  194.   Temporary := (DataBits - 5) AND $03 OR (((StopBits - 1) SHL 2) AND $04);      {-Set data bits, stop bits,  }
  195.   PORT [COMPort [Com] + LCR] := Temporary OR ((Parity SHL 3) AND $38);          { and parity protocol.       }
  196.   Set_DTR_RTS (Com, TRUE);                                                      {                            }
  197. END;                                                                            {                            }
  198.                                                                                 {                            }
  199. { This procedure handles all interrupts from the 8250 communications chip.  All interrupt types are at least }
  200. { minimally supported.  Enough code is present to help you know how to modify the code for your specific     }
  201. { requirements.  Incoming data is ignored if the buffer is full, otherwise it is placed into the InBuffer    }
  202. { circular queue. Data to be transmitted by interrupt is taken from the OutBuffer queue.  The transmitter is }
  203. { the only interrupt which has to be manually invoked.  Do so by placing the first character of each trans-  }
  204. { mission into the THR.  It automatically shuts off when all the data in the buffer has been sent. The other }
  205. { interrupt types will take care of themselves once enabled. Modem/Port input lines may be monitored by this }
  206. { handler.  BOOLEAN arrays CTS, DSR, RI, and CD always show current status of these lines IF the interrupt   }
  207. { handler is active and the Modem Status Change interrupt has been enabled.  A TRUE indicates the signal is  }
  208. { active.  CD and RI are very helpful for modem related programs.  Line Status errors are counted.  It is up }
  209. { to you add any corrective action.  All ports with interrupts pending on the IRQ level which invoked this   }
  210. { handler are processed regardless of which port generated the actual interrupt. This is simple to implement,}
  211. { yet it preserves the interrupt priority handling between ports.                                            }
  212.                                                                                 {                            }
  213. {$F+}                                                                           {-Interrupt handlers MUST be }
  214. PROCEDURE IntHandler; INTERRUPT;                                                { FAR calls.                 }
  215. VAR                                                                             {                            }
  216.   Com,                                                                          {-Port being serviced        }
  217.   IRQ,                                                                          {-IRQ # of this interrupt    }
  218.   IMR,                                                                          {-Interrupt Mask Register    }
  219.   Temp : BYTE;                                                                  {-Temporary for misc. uses   }
  220. BEGIN                                                                           {                            }
  221.   IRQ := 0;                                                                     {                            }
  222.   IMR := PORT [$21];                                                            {-Backup IMR for later use   }
  223.   PORT [$20] := $0B;                                                            {-Request 8259 ISR read next }
  224.   Temp := PORT [$20];                                                           {-Get the 8259 ISR value     }
  225.   WHILE (Temp AND 1 = 0) DO                                                     {-Upon loop completion, IRQ  }
  226.     BEGIN                                                                       { will contain the IRQ # of  }
  227.       Temp := Temp SHR 1;                                                       { the interrupt in progress. }
  228.       INC (IRQ);                                                                {                            }
  229.     END;                                                                        {                            }
  230.   PORT [$21] := IMR OR IRQMask;                                                 {-Disable Comm_TP4 interrupts}
  231.   EnableInts;                                                                   {-Allow other interrupts     }
  232.   FOR Com := 1 TO MaxPorts DO                                                   {-Process all ports with the }
  233.    IF (IRQNmbr [Com] = IRQ) AND (IntInstalled [Com])                            { same IRQ level which made  }
  234.     THEN                                                                        { this interrupt happen.     }
  235.      BEGIN                                                                      {                            }
  236.       PORT [COMPort [Com] + LCR] := PORT [COMPort [Com] + LCR] AND $7F;         {-Set THR, RHR & IER active  }
  237.       CASE PORT [COMPort [Com] + IIR] AND $07 OF                                {-Identify interrupt type    }
  238.        0 : BEGIN                                                                {                            }
  239.             Temp := PORT [COMPort [Com] + MSR];                                 {    MODEM STATUS CHANGES    }
  240.             CD  [Com] := $80 AND Temp <> 0;                                     {       Carrier Detect       }
  241.             CTS [Com] := $10 AND Temp <> 0;                                     {       Clear To Send        }
  242.             DSR [Com] := $20 AND Temp <> 0;                                     {       Data Set Ready       }
  243.             RI  [Com] := $40 AND Temp <> 0;                                     {       Ring Indicator       }
  244.            END;                                                                 {                            }
  245.        2 : BEGIN                                                                {                            }
  246.             IF (OutHead [Com] = OutTail [Com])                                  {  TRANSMIT REGISTER EMPTY   }
  247.              THEN                                                               {                            }
  248.               PORT [COMPort [Com] + IER] := PORT [COMPort [Com] + IER] AND $FD  {  If no more data to send,  }
  249.              ELSE                                                               {  shut off the transmitter. }
  250.               BEGIN                                                             {  Otherwise, send the next  }
  251.                PORT [COMPort [Com] + THR] := OutBuffer [Com, OutHead [Com]];    {  byte, and remove it from  }
  252.                OutHead [Com] := (OutHead [Com] + 1) MOD (MaxOutSize + 1);       {  the buffer.               }
  253.               END;                                                              {                            }
  254.            END;                                                                 {                            }
  255.        4 : BEGIN                                                                {                            }
  256.             IF (InTail [Com] + 1) MOD (MaxInSize + 1) <> InHead [Com]           {   RECEIVE REGISTER FULL    }
  257.              THEN                                                               {                            }
  258.               BEGIN                                                             { If the buffer is not full, }
  259.                InBuffer [Com, InTail [Com]] := PORT [COMPort [Com] + RHR];      { add the character and set  }
  260.                InTail [Com] := (InTail [Com] + 1) MOD (MaxInSize + 1);          { the queue buffer pointer.  }
  261.               END                                                               { Otherwise, the character   }
  262.              ELSE                                                               { is read but not stored.    }
  263.               BEGIN                                                             {                            }
  264.                IF (PORT [COMPort [Com] + RHR] = $00) THEN { DO Nothing };       {                            }
  265.               END;                                                              {                            }
  266.            END;                                                                 {                            }
  267.        6 : BEGIN                                                                { LINE STATUS CHANGE & ERROR }
  268.              Temp := PORT [COMPort [Com] + LSR] AND $1E;                        {   Just count the errors    }
  269.              IF (Temp AND $02 <> 0) THEN INC (Overrun [Com]);                   {       Overrun Error        }
  270.              IF (Temp AND $04 <> 0) THEN INC (Parity [Com]);                    {       Parity Error         }
  271.              IF (Temp AND $08 <> 0) THEN INC (Framing [Com]);                   {       Framing Error        }
  272.              IF (Temp AND $10 <> 0) THEN INC (Break [Com]);                     {       Break Interrupt      }
  273.            END;                                                                 {                            }
  274.       END;                                                                      {                            }
  275.      END;                                                                       {                            }
  276.   DisableInts;                                                                  {-Accessing 8259 hardware    }
  277.   PORT [$21] := IMR;                                                            {-Enable Comm_TP4 interrupts }
  278.   PORT [$20] := $20;                                                            {-Notify 8259 that interrupt }
  279. END;                                                                            { has been completed.        }
  280. {$F-}                                                                           {                            }
  281.                                                                                 {                            }
  282. { This procedure installs and enables the specified serial port interrupt.  All port input line monitoring   }
  283. { variables are set to match the actual line states.  The old serial port interrupt vector is saved so it    }
  284. { can be reinstalled when we remove our serial port interrupt.  DTR and RTS are forced to the ready state.   }
  285. { The 8250 interrupts are enabled by ORing the MCR with $08.  To enable all four 8250 interrupt types, write }
  286. { $0F to the IER.  (Receive Buffer Full, Line Status, & Modem Status interrupts are enabled by writing $0D   }
  287. { to the IER).  ORing $EF with PORT [$21] enables IRQ4 (COM1 or COM3), while $F7 enables IRQ3 (COM2 or COM4).}
  288. { Hardware interrupts should be disabled during the installation process since the 8259 ports are being set. }
  289. { Error checking is always in place since a crash could occur if it is used when a the handler for the same  }
  290. { port has already been installed.                                                                           }
  291.                                                                                 {                            }
  292. PROCEDURE InstallInt (Com : BYTE);                                              {                            }
  293. VAR                                                                             {                            }
  294.   Temporary : BYTE;                                                             {                            }
  295. BEGIN                                                                           {                            }
  296.   IF ValidPort (Com, 0, 'InstallInt')                                           {-Error checking important!  }
  297.     THEN                                                                        {                            }
  298.       BEGIN                                                                     {                            }
  299.         DisableInts;                                                            {-Accessing 8259 hardware    }
  300.         Temporary := PORT [COMPort [Com]+MSR];                                  {                            }
  301.         CD  [Com] := ($80 AND Temporary <> 0);                                  {-Carrier Detect status      }
  302.         CTS [Com] := ($10 AND Temporary <> 0);                                  {-Clear to Send status       }
  303.         DSR [Com] := ($20 AND Temporary <> 0);                                  {-Data Set Ready status      }
  304.         RI  [Com] := ($40 AND Temporary <> 0);                                  {-Ring Indicator status      }
  305.         Temporary := PORT [COMPort [Com] + LSR];                                {-Reset interrupts that were }
  306.         Temporary := PORT [COMPort [Com] + RHR];                                { waiting to be processed.   }
  307.         Temporary := 1 SHL IRQNmbr [Com];                                       {-If other port using same   }
  308.         IF (IRQMask AND Temporary) = 0                                          { IRQ then nothing must be   }
  309.           THEN BEGIN                                                            { done to 8259 or vectors.   }
  310.                  IRQMask := IRQMask OR Temporary;                               {-Update interrupt record    }
  311.                  GETINTVEC ($08 + IRQNmbr [Com], OldIntVector [Com]);           {-Save old interrupt vector  }
  312.                  SETINTVEC ($08 + IRQNmbr [Com], @IntHandler);                  {-Install Comm_TP4 vector    }
  313.                  PORT [$21] := PORT [$21] AND NOT Temporary;                    {-Enable 8259 IRQ handling   }
  314.                END;                                                             {                            }
  315.         PORT [COMPort [Com] + MCR] := PORT [COMPort [Com] + MCR] OR $08;        {-Enable 8250 interrupt line }
  316.         PORT [COMPort [Com] + LCR] := PORT [COMPort [Com] + LCR] AND $7F;       {-Set THR/RHR/IER active     }
  317.         PORT [COMPort [Com] + IER] := $01;                                      {-Enable 8250 interrupts     }
  318.         IntInstalled [Com] := TRUE;                                             {-The interrupt is installed }
  319.         Set_DTR_RTS (Com, TRUE);                                                {-DTR/RTS on so the other    }
  320.         EnableInts;                                                             { device knows we are ready  }
  321.       END;                                                                      { to receive data.           }
  322. END;                                                                            {                            }
  323.                                                                                 {                            }
  324. { This procedure removes the specified serial port interrupt and reinstalls the original interrupt vectors.  }
  325. { DTR & RTS are set OFF and 8250 interrupt line is disabled by ANDing the MCR with $F7.  All 8250 interrupt  }
  326. { types are disabled by writing $00 to the IER.  ORing $10 with PORT [$21] disables IRQ4 (COM1 or COM3),     }
  327. { while $08 disables IRQ3 (COM2 or COM4).  Hardware interrupts must be disabled since the 8259 ports are     }
  328. { being set.  Some error checking is always in place since attempting RemoveInt on a handler which has not   }
  329. { been installed can cause the computer to eventually crash.                                                 }
  330.                                                                                 {                            }
  331. PROCEDURE RemoveInt (Com : BYTE);                                               {                            }
  332. VAR                                                                             {                            }
  333.   Temporary : BYTE;                                                             {                            }
  334. BEGIN                                                                           {                            }
  335.   IF ValidPort (Com, 1, 'RemoveInt')                                            {-Error checking important!  }
  336.     THEN                                                                        {                            }
  337.       BEGIN                                                                     {                            }
  338.         DisableInts;                                                            {-Accessing 8259 hardware    }
  339.         Set_DTR_RTS (Com, FALSE);                                               {-DTR/RTS off                }
  340.         IntInstalled [Com] := FALSE;                                            {-Uninstalling interrupt     }
  341.         PORT [COMPort [Com] + MCR] := PORT [COMPort [Com] + MCR] AND $F7;       {-Disable 8250 interrupt line}
  342.         PORT [COMPort [Com] + LCR] := PORT [COMPort [Com] + LCR] AND $7F;       {-Set THR, THE & IER active  }
  343.         PORT [COMPort [Com] + IER] := $00;                                      {-Disable 8250 interrupts    }
  344.         Temporary := 1 SHL IRQNmbr [Com];                                       {-Get bit for IRQ mask       }
  345.         IF (IRQMask AND Temporary) <> 0                                         {-If no other Comm_TP4       }
  346.           THEN BEGIN                                                            { interrupt is on this IRQ:  }
  347.                  PORT [$21] := PORT [$21] OR Temporary;                         { Disable 8259 IRQ handling, }
  348.                  SETINTVEC ($08 + IRQNmbr [Com], OldIntVector [Com]);           { Replace original vector,   }
  349.                  IRQMask := IRQMask AND NOT Temporary;                          { and update IRQ mask.       }
  350.                END;                                                             {                            }
  351.         EnableInts;                                                             {-Done with 8259 setup       }
  352.       END;                                                                      {                            }
  353. END;                                                                            {                            }
  354.                                                                                 {                            }
  355. { This procedure is provided for situations where you want to be sure that any of the serial port buffers    }
  356. { are empty.  This could be used for aborting an interrupt driven transmission or for clearing unwanted data }
  357. { from the input buffers.  The first parameter is the buffer number and the second parameter is TRUE for the }
  358. { input buffer or FALSE for the output buffer.  A buffer number of 0 causes all buffers to be emptied.       }
  359.                                                                                 {                            }
  360. PROCEDURE EmptyBuffer (Buffer : BYTE; TrueInFalseOut : BOOLEAN);                {                            }
  361. VAR                                                                             {                            }
  362.   LoopVar : BYTE;                                                               {                            }
  363. BEGIN                                                                           {                            }
  364.   FOR LoopVar := 1 to MaxPorts DO                                               {-A buffer number of 0 will  }
  365.     IF (Buffer = 0) OR (LoopVar = Buffer)                                       { empty all buffers. Invalid }
  366.       THEN BEGIN                                                                { buffer numbers do nothing. }
  367.              DisableInts;                                                       {-Disable buffer activity    }
  368.              IF TrueInFalseOut                                                  { before changing pointers.  }
  369.                THEN InHead [LoopVar] := InTail [LoopVar]                        {-Clear an input buffer      }
  370.                ELSE OutHead [LoopVar] := OutTail [LoopVar];                     {-Clear an output buffer     }
  371.              EnableInts;                                                        {-Reenable buffer activity   }
  372.            END;                                                                 {                            }
  373. END;                                                                            {                            }
  374.                                                                                 {                            }
  375. { This function/procedure writes character or string data to the serial port.  It does this by reading and   }
  376. { writing to the 8250 communications chip.  This is an example that may be modified to suit your purposes.   }
  377. { As is, it pauses the program while it sends the data.  If it cannot send a character after 65535 tries, it }
  378. { aborts the sending process.  Use the conditional compilation directive $DEFINE FWriteCOM in order to make  }
  379. { WriteCOM a function which returns a BOOLEAN TRUE if the transmission did not timeout. The statement: (PORT }
  380. { [COMPort [Com]+LSR] AND $20) <> $20 indicates when the THR is ready for a new character to send.  CTS and  }
  381. { DSR are not checked, but if you want to check for them (PORT [COMPort [Com]+MSR] AND $30) must equal $30.  }
  382.                                                                                 {                            }
  383. {$IFDEF FWriteCOM}                                                              {-A $DEFINE/$UNDEF FWriteCOM }
  384.   FUNCTION WriteCOM (Com : BYTE; Data : STRING) : BOOLEAN;                      { directive determines if    }
  385. {$ELSE}                                                                         { WriteCOM is a function or  }
  386.   PROCEDURE WriteCOM (Com : BYTE; Data : STRING);                               { a procedure.               }
  387. {$ENDIF}                                                                        {                            }
  388.                                                                                 {                            }
  389. VAR                                                                             {                            }
  390.  LoopVar,                                                                       {-Pointer to output char     }
  391.  TimeLoop : WORD;                                                               {-Timeout counter variable   }
  392.  TimeOut, Ready : BOOLEAN;                                                      {-TRUE if port timed out     }
  393. BEGIN                                                                           {                            }
  394.   {$IFDEF ErrorChecking}                                                        {                            }
  395.     IF NOT ValidPort (Com, 1, 'WriteCOM') THEN EXIT;                            {-Optional error trapping    }
  396.   {$ENDIF}                                                                      {                            }
  397.   LoopVar := 0;                                                                 {                            }
  398.   TimeOut := FALSE;                                                             {                            }
  399.   WHILE (LoopVar < LENGTH (Data)) AND NOT TimeOut DO                            {-Send the data one char at  }
  400.     BEGIN                                                                       { a time unless the port was }
  401.       INC (LoopVar);                                                            { timed out.                 }
  402.       TimeLoop := 0;                                                            {                            }
  403.       REPEAT                                                                    {                            }
  404.         Ready := (PORT [COMPort [Com]+LSR] AND $20) <> 0;                       {                            }
  405.         INC (TimeLoop);                                                         {                            }
  406.       UNTIL Ready OR (TimeLoop = 65535);                                        {                            }
  407.       IF Ready                                                                  {                            }
  408.         THEN BEGIN                                                              {                            }
  409.                PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] AND $7F;    {-Allow THR,RHR & IER access }
  410.                PORT [COMPort [Com]+THR] := ORD (Data [LoopVar]);                {-Put the data to send in    }
  411.              END                                                                { the THR.                   }
  412.         ELSE BEGIN                                                              {                            }
  413.                TimeOut := TRUE;                                                 {-WriteCOM aborts if the THR }
  414.                {$IFDEF ErrorChecking}                                           { takes too long to become   }
  415.                  MakeError (4, Com, 'WriteCOM');                                { empty & optionally creates }
  416.                {$ENDIF}                                                         { an error condition.        }
  417.              END;                                                               {                            }
  418.     END;                                                                        {                            }
  419.   {$IFDEF FWriteCOM}                                                            {-With a compiler directive  }
  420.     WriteCOM := NOT TimeOut;                                                    { of $DEFINE FWriteCOM, then }
  421.   {$ENDIF}                                                                      { WriteCOM is a function and }
  422. END;                                                                            { returns TRUE if no timeout }
  423.                                                                                 {                            }
  424. { This procedure is an example of how to write an interrupt driven send routine.  The main idea is that you  }
  425. { add data to the output buffer, then you get things going by manually placing one byte into the transmitter }
  426. { holding register.  After doing this, the rest of the buffer will be sent automatically.  Strenuous testing }
  427. { of this procedure under very high data rates has not been done, and it might be possible to rewrite it to  }
  428. { provide better throughput.  Data is the information to send to the port in either CHAR or STRING form.  It }
  429. { is impractical to use this procedure for sending single characters since it calls WriteCOM at least once.  }
  430. { It is best suited for high volume data transfers.  Interrupts should be off during buffer operations.      }
  431.                                                                                 {                            }
  432. PROCEDURE IWriteCOM (Com : BYTE; Data : STRING);                                {                            }
  433. VAR                                                                             {                            }
  434.   BuffFull : BOOLEAN;                                                           {-TRUE if output buffer full }
  435.   StartChr : CHAR;                                                              {-Temporary Buffer           }
  436.   Loop : WORD;                                                                  {-Points to current Data item}
  437. BEGIN                                                                           {                            }
  438.   {$IFDEF ErrorChecking}                                                        {                            }
  439.     IF NOT ValidPort (Com, 1, 'IWriteCOM') THEN EXIT;                           {-Optional error trapping    }
  440.   {$ENDIF}                                                                      {                            }
  441.   Loop := 1;                                                                    {                            }
  442.   PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] AND $7F;                 {-Enable access to 8250 IER  }
  443.   WHILE (Loop <= LENGTH (Data)) DO                                              {-Load the output buffer one }
  444.     BEGIN                                                                       { byte at a time.            }
  445.       DisableInts;                                                              {-During buffer operations   }
  446.       BuffFull := (OutTail [Com] + 1) MOD (MaxOutSize+1) = OutHead[Com];        {-Is the buffer full?        }
  447.       IF NOT BuffFull                                                           {-If not, add a character to }
  448.         THEN BEGIN                                                              { buffer and update pointers }
  449.                OutBuffer [Com, OutTail [Com]] := ORD (Data [Loop]);             {                            }
  450.                OutTail [Com] := (OutTail [Com] + 1) MOD (MaxOutSize + 1);       { NOTE: Interrupts should be }
  451.                INC (Loop);                                                      { enabled within the Loop so }
  452.              END;                                                               { the interrupt can empty    }
  453.       EnableInts;                                                               {-the buffer as we fill it.  }
  454.       IF BuffFull OR (Loop > LENGTH (Data))                                     {-Check the interrupt status }
  455.         THEN                                                                    { if the buffer gets full or }
  456.           BEGIN                                                                 { after data is all loaded.  }
  457.             IF (PORT [COMPort [Com] + IER] AND $02 <> 2)                        {-If the transmit interrupt  }
  458.               THEN                                                              { is not on, start it up.    }
  459.                 BEGIN                                                           {                            }
  460.                   DisableInts;                                                  {                            }
  461.                   StartChr := CHR (OutBuffer [Com, OutHead [Com]]);             {-Get the first character &  }
  462.                   OutHead [Com] := (OutHead [Com] + 1) MOD (MaxOutSize + 1);    { take it out of the buffer. }
  463.                   PORT [COMPort [Com]+IER] := PORT [COMPort [Com]+IER] OR $02;  {-Turn transmit interrupt on }
  464.                   EnableInts;                                                   {                            }
  465.                   {$IFDEF FWriteCOM}                                            {-Kickstart the transmitter  }
  466.                     IF WriteCOM (Com, StartChr) THEN {};                        { interrupt by sending one   }
  467.                   {$ELSE}                                                       { character.  WriteCOM is    }
  468.                     WriteCOM (Com, StartChr);                                   { used for simplicity.       }
  469.                   {$ENDIF}                                                      {                            }
  470.                 END;                                                            {                            }
  471.           END;                                                                  {                            }
  472.     END;                                                                        {                            }
  473. END;                                                                            {                            }
  474.                                                                                 {                            }
  475. { This function is an example of how to get a character from the serial port. As is, if the buffer is empty, }
  476. { it waits until a character arrives, so this will not work for the TTY emulation. The interrupts are always }
  477. { disabled when the buffer pointers are checked or modified.  Beware!  Do not completely disable interrupts  }
  478. { in the wait loop or else you never will get a character if there is not one there already.                 }
  479.                                                                                 {                            }
  480. FUNCTION ReadCOM (Com : BYTE) : CHAR;                                           {                            }
  481. VAR                                                                             {                            }
  482.   CharReady : BOOLEAN;                                                          {-TRUE if there is data in   }
  483. BEGIN                                                                           { the input buffer           }
  484.   {$IFDEF ErrorChecking}                                                        {                            }
  485.     IF NOT ValidPort (Com, 1, 'ReadCOM') THEN EXIT;                             {-Optional error trapping    }
  486.   {$ENDIF}                                                                      {                            }
  487.   CharReady := FALSE;                                                           {                            }
  488.   REPEAT                                                                        {-Wait for data to arrive    }
  489.     DisableInts;                                                                {                            }
  490.     CharReady := InTail [Com] <> InHead [Com];                                  {-Check to see if buffer is  }
  491.     EnableInts;                                                                 { empty                      }
  492.   UNTIL CharReady;                                                              {                            }
  493.   DisableInts;                                                                  {                            }
  494.   ReadCOM := CHR(InBuffer [Com, InHead [Com]]);                                 {-Read a character of data   }
  495.   InHead [Com] := (InHead [Com] + 1) MOD (MaxInSize + 1);                       {-Update the buffer pointer  }
  496.   EnableInts;                                                                   {                            }
  497. END;                                                                            {                            }
  498.                                                                                 {                            }
  499. { This function is an example of how to get a character from the serial port.  Unlike ReadCOM, this routine  }
  500. { returns if no data appears in the buffer for a short period of time. This makes it useful for applications }
  501. { which process data only if it is available.  The interrupts are always disabled when the buffer pointers   }
  502. { are checked or modified.  Beware!  Do not completely disable interrupts in the wait loop or else you never }
  503. { will get a character if there is not one there already.  Returns TRUE if valid data has been returned. You }
  504. { may optimize the time out period to a shorter one for your applications.  65535 is the maximum wait time.  }
  505.                                                                                 {                            }
  506. FUNCTION TimedReadCOM (Com : BYTE; VAR Data : CHAR) : BOOLEAN;                  {                            }
  507. VAR                                                                             {                            }
  508.   CharReady : BOOLEAN;                                                          {-TRUE if there is data in   }
  509.   TimeOut : WORD;                                                               { the input buffer           }
  510. BEGIN                                                                           {                            }
  511.   {$IFDEF ErrorChecking}                                                        {                            }
  512.     IF NOT ValidPort (Com, 1, 'TimedReadCOM') THEN EXIT;                        {-Optional error trapping    }
  513.   {$ENDIF}                                                                      {                            }
  514.   TimeOut := 0;                                                                 {                            }
  515.   REPEAT                                                                        {-Wait for data to arrive    }
  516.     DisableInts;                                                                {                            }
  517.     CharReady := InTail [Com] <> InHead [Com];                                  {-Is the buffer empty or not }
  518.     EnableInts;                                                                 {                            }
  519.     INC (TimeOut);                                                              {-Increment the timer        }
  520.   UNTIL CharReady OR (TimeOut = 65535);                                         {-Set the maximum time to    }
  521.   IF CharReady                                                                  { wait for data here.  Lower }
  522.     THEN BEGIN                                                                  { number for shorter timeout.}
  523.            DisableInts;                                                         {                            }
  524.            Data := CHR (InBuffer [Com, InHead [Com]]);                          {-If data became available,  }
  525.            InHead [Com] := (InHead [Com] + 1) MOD (MaxInSize + 1);              { read a character, and set  }
  526.            EnableInts;                                                          { the buffer pointers.       }
  527.          END                                                                    {                            }
  528.     ELSE MakeError (5, Com, 'TimedReadCOM');                                    {-Record the timeout error   }
  529.   TimedReadCOM := CharReady;                                                    {                            }
  530. END;                                                                            {                            }
  531.                                                                                 {                            }
  532. {$F+}                                                                           {-VERY IMPORTANT!  When the  }
  533. PROCEDURE RemoveIntOnExit;                                                      { program quits normally or  }
  534. BEGIN                                                                           { abnormally, the interrupt  }
  535.   FOR Loop := 1 TO MaxPorts DO                                                  { handler is automatically   }
  536.     IF IntInstalled [Loop]                                                      { uninstalled when Turbo     }
  537.       THEN RemoveInt (Loop);                                                    { invokes this procedure.    }
  538.   ExitProc := ExitSave;                                                         {-Return control to the      }
  539. END;                                                                            { original exit procedure    }
  540. {$F-}                                                                           {                            }
  541.                                                                                 {                            }
  542. { The following code is executed when any program which uses this unit first  } { Changeable defaults are    }
  543. { starts up.  It performs all necessary initializations.                      } { marked with a '*'          }
  544.                                                                                 {                            }
  545. BEGIN                                                                           {                            }
  546.   ExitSave := ExitProc;                                                         {-VERY IMPORTANT!  This lets }
  547.   ExitProc := @RemoveIntOnExit;                                                 { the program halt safely.   }
  548.   FOR Loop := 1 TO MaxPorts DO                                                  {                            }
  549.     BEGIN                                                                       {                            }
  550.       InHead [Loop] := 0;                                                       {-Default all buffers set to }
  551.       InTail [Loop] := 0;                                                       { empty on startup.          }
  552.       OutHead [Loop] := 0;                                                      {                            }
  553.       OutTail [Loop] := 0;                                                      {                            }
  554.       DTR_RTS [Loop] := FALSE;                                                  {*Default DTR/RTS setting on }
  555.       IntInstalled [Loop] := FALSE;                                             {-Default interrupts not on  }
  556.       CD  [Loop] := ($80 AND PORT [COMPort [Loop]+MSR] <> 0);                   {-Carrier Detect status set  }
  557.       CTS [Loop] := ($10 AND PORT [COMPort [Loop]+MSR] <> 0);                   {-Clear To Send status set   }
  558.       DSR [Loop] := ($20 AND PORT [COMPort [Loop]+MSR] <> 0);                   {-Data Set Ready status set  }
  559.       RI  [Loop] := ($40 AND PORT [COMPort [Loop]+MSR] <> 0);                   {-Ring Indicator status set  }
  560.       Framing [Loop] := 0;                                                      {-Reset framing error count  }
  561.       Overrun [Loop] := 0;                                                      {-Reset overrun error count  }
  562.       Parity [Loop] := 0;                                                       {-Reset parity error count   }
  563.       Break [Loop] := 0;                                                        {-Reset break interrupt count}
  564.     END;                                                                        {                            }
  565.   ErrorCode := 0;                                                               {-Default of no port errors  }
  566.   ErrorPort := 0;                                                               {                            }
  567.   IRQMask := 0;                                                                 {-No interrupts installed    }
  568.   {$IFNDEF NoMessageCode}                                                       {                            }
  569.     ShowMessages := TRUE;                                                       {*Default error messages on  }
  570.     ErrMsgX := 0;                                                               {*Default of error messages  }
  571.     ErrMsgY := 0;                                                               {*placed at cursor position. }
  572.   {$ENDIF}                                                                      {                            }
  573. END.                                                                            {                            }
  574.