home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / PASCAL / COMM_TP5.ZIP / COMM_TP4.DOC next >
Text File  |  1989-11-15  |  43KB  |  804 lines

  1.   Comm_TP4.PAS Ver. 1.50 - RS-232 Support for IBM Compatibles - Documentation
  2.  
  3.                               (c) Copyright, 1989
  4.                                Kevin R. Bulgrien
  5.                                 November,  1989
  6.  
  7.                            c/o LeTourneau University
  8.                                Microcomputer Services
  9.                                P.O. Box 7001
  10.                                Longview, TX 75607
  11.  
  12.  
  13.  
  14. INTRODUCTION
  15.  
  16. This utility is an answer to the many public domain serial port routines which
  17. I have acquired in the quest for good RS-232 support under Turbo Pascal. Unlike
  18. the others, this one works.  Not only does it work, but it also allows you to
  19. use as many COM ports as you want at the same time.  Designed to be relatively
  20. simple and easy to use, I hope many will benefit from it.
  21.  
  22. This code only works under Turbo Pascal versions 4.0 and 5.x.  For functionally
  23. equivalent routines that work under Turbo Pascal 3.0, look for COMM_TP3.  Also,
  24. a Turbo C version called COMM_TC2 is available.  While it was written for Turbo
  25. C 2.0, it will probably also work for lower versions as well.  Additionally, I
  26. am in in the process of developing a Turbo Assembler version called Comm_TA1.
  27.  
  28.  
  29.  
  30. DISTRIBUTION POLICY
  31.  
  32. The following files must all be present whenever this package is distributed:
  33.  
  34.          COMM_TP4.PAS  -  Turbo 4.0 & 5.x compatible TPU source code
  35.          COMM_TP4.DOC  -  Documentation for the TPU source code
  36.          COMM_TTY.PAS  -  Demo terminal emulator which uses COMM_TP4
  37.  
  38. This package may be used in any non-commercial application without restriction,
  39. although I do require that the source code not be distributed in a modified
  40. form unless you clearly identify the modifications and do not remove any of
  41. my copyright information, distribution policy, or other relevant documentation.
  42.  
  43. If you wish to use this package for development of software which will be sold
  44. or released with a shareware notice, I do require that a one-time registration
  45. fee of $10.00 be paid to me.  Because this source code is a platform from which
  46. one can build other tools, you are not exempt from this requirement if you have
  47. to modify or add to my code in order to make it fit your specific application.
  48. A letter which acknowledges your payment will be sent to you by return mail.
  49.  
  50. I specifically prohibit any distribution of the source code where a disk fee is
  51. charged, unless I have granted written permission to the specific vendor making
  52. such a charge.  A general access fee to an electronic bulletin board system is
  53. not considered a disk fee.
  54.  
  55. I also specifically prohibit any person or entity from claiming copyright on
  56. this material without first obtaining my personal, written consent.  Monetary
  57. compensation to me will be required for any such agreement to be valid.  This
  58. does not infer that you may not copyright material which uses this product.
  59.  
  60.  
  61.  
  62. DISCLAIMER
  63.  
  64. While I have extensively tested the routines in this package, I will not be
  65. held responsible for any consequence arising from the use thereof.  It is the
  66. responsibility of the user to determine whether or not the enclosed routines
  67. will satisfactorily perform the required functions.
  68.  
  69. This software directly accesses the Intel 8250 UART as well as the Intel 8259
  70. interrupt controller hardware.  Though they are IBM standards, it is possible
  71. that some manufacturers could use different hardware to supply these functions,
  72. in which case, program compatibility would be questionable.
  73.  
  74.  
  75.  
  76. CREDITS
  77.  
  78. Credit for this product must go to the various authors who showed me that if I
  79. wanted something that worked, I would have to write it myself.  I won't try to
  80. identify the items, but I obtained at least twelve serial port handlers from
  81. GEnie which could not satisfy my meager requirements.  It amazing to see how
  82. much code is out there which either:  1) does not perform satisfactorily, 2)
  83. has inadequate documentation, 3) is so complex that only an expert could use
  84. it, or, 4) does not contain modifiable source code.
  85.  
  86. All of the enclosed code is completely original to me, and has not been stolen,
  87. borrowed, or purchased from any other source.
  88.  
  89. Credit for the information on the IBM compatible hardware must go to the
  90. Prentice-Hall book "Systems Software Tools" by Ted J. Biggerstaff.  I was
  91. amazed to find a book that so clearly outlined the IBM PC interrupt and serial
  92. port hardware.  The world of IBM PC technical information is a desert, and
  93. this book is a rare oasis in the midst of it.
  94.  
  95. Additionally, credit must also go to the "DOS Programmer's Reference" by Terry
  96. R. Dettmann which is published by the QUE corporation.  I would recommend it
  97. to any programmer who needs a good DOS or BIOS reference.
  98.  
  99.  
  100.  
  101. FEATURES
  102.  
  103.  - Easily expandable interrupt routine
  104.  - Interrupt driven send and receive routines
  105.  - Carrier Detect monitoring for modem support
  106.  - Turbo Pascal 4.0 & 5.x compatible TPU source code
  107.  - Convenient TPU format keeps your code uncluttered
  108.  - Easy to customize for your hardware, and software requirements
  109.  - Uninstalls the interrupt handlers on abnormal program termination
  110.  - Number of COM ports limited only by buffer sizes & Turbo memory limits
  111.  - A TPU which handles 4 ports can be less than 8K in size
  112.  - Easy to use even if you don't understand how it works
  113.  - All COM ports handled concurrently in the background
  114.  - Example TTY and port setup routines included
  115.  - Optional error checking and error messages
  116.  - Extensive documentation for software use
  117.  - Heavily documented source code
  118.  
  119.  
  120.  
  121. DESIGN PHILOSOPHY
  122.  
  123. This is a rather grand heading, but I think now is a good time to share the
  124. philosophy used while writing this software.  This, along with a look at the
  125. code and the rest of the documentation, will help you to decide if Comm_TP4
  126. is for you.
  127.  
  128. Most serial applications which I have been involved with were very simple in
  129. nature.  I did not require a super-high-performance-do-everything-imaginable
  130. piece of software, and as a result, I wrote a port handler which reflected my
  131. particular needs - but also a little bit more.
  132.  
  133. For example, the simple monitoring of Carrier Detect makes this an ideal
  134. package to write online BBS support programs with.  One of the university
  135. students here has used my package to write a complete online game.
  136.  
  137. Also, early versions of Comm_TP4 only supported standard DOS ports, but since
  138. I saw that some people would need non-standard setups, I rewrote the code so
  139. that it would allow most any setup.  I have yet to need such a feature, but it
  140. is there and you can be sure that I will keep on adding more support items
  141. like this as time goes on.
  142.  
  143. Perhaps one of the most important guiding principles I have tried to abide by
  144. can be summed up with a KISS (Keep It Simple Stupid).  I have seen one or two
  145. communications packages that provided a plethora of low level routines which
  146. could be used to manipulate every aspect of the hardware.  I have deliberately
  147. kept this type of code out of Comm_TP4 in the interest of providing a simple-
  148. to-use package rather than an it-does-everything one.  I figure that if someone
  149. knows how to use the low-level operations, they probably know enough to be able
  150. to add them to my code.  Because most people do not need complicated routines,
  151. I felt I would gain more friends by making Comm_TP4 easy-to-use than enemies by
  152. making Comm_TP4 "incomplete".  Make no mistake, though, "incomplete" is a very
  153. relative term, and I really don't believe it applies here.
  154.  
  155. In summary, I am confident that the routines are high-performance enough to
  156. satisfy the bulk of serial port applications.  They do not, however, provide
  157. a do-everything-imaginable user interface, and yet, there is little that one
  158. cannot do.  Easy-to-use, flexible, and growing are the key words.
  159.  
  160.  
  161.  
  162. GETTING STARTED - DOES IT REALLY WORK?
  163.  
  164. If you want to prove that these routines really work, first compile the source
  165. file COMM_TP4.PAS into a TPU file with your Turbo compiler.
  166.  
  167.    IMPORTANT NOTE:  Never compile interrupt driven routines with the stack
  168.                     checking code enabled.  Doing so will cause the program
  169.                     to crash whenever the interrupt is invoked.
  170.  
  171. Next, compile and run the COMM_TTY program as it is distributed.  What you will
  172. see is a crude terminal emulation program which uses the COMM_TP4 routines.  It
  173. is capable of receiving data from several COM ports at the same time.  The
  174. screen echoes data from the "logged" COM port and buffers the data from other
  175. ports.  When a different COM port is logged, the buffered data is sent to the
  176. screen.  Also provided is a setup routine that lets you select a baud rate from
  177. 110 to 38400.  The other protocol attributes are also selectable.
  178.  
  179.  
  180.  
  181. AN EXAMPLE OF HOW EASY IT IS TO USE COMM_TP4
  182.  
  183. All it takes is a few simple commands.  For example, let us set up the serial
  184. port to 2400 baud, 8 bits, no parity, and 1 stop bit.  Then, we'll install an
  185. interrupt handler and send a string out the port, and wait for a short
  186. response.  Lastly, we will shut off the interrupt handler.
  187.  
  188. The program that follows will work with COM1 or COM2 without any modification
  189. to the TPU source code that has been included with this documentation.
  190.  
  191.      PROGRAM TestPort;
  192.  
  193.      USES Comm_TP4;
  194.  
  195.      CONST
  196.        Com = 1;  { Use COM1, use 2 for COM2 }
  197.  
  198.      VAR
  199.        InputData : CHAR;
  200.  
  201.      BEGIN
  202.        SetupCOMPort (Com, ORD(B2400), 8, ORD(None), 1);
  203.        InstallInt (Com);
  204.        WriteCOM (Com, 'This really is easy.  Isn't it?');
  205.        InputData := TimedReadCOM (Com);
  206.        RemoveInt (Com);
  207.      END.
  208.  
  209. That is all there is to it.  The Comm_TTY example may be intimidating because
  210. of its size, but that's just because it is a whole terminal emulator.
  211.  
  212. To be sure, most applications will need a bit more than what is shown here, but
  213. that's what the rest of the documentation addresses.  I just thought it would
  214. be good to emphasize the ease of using Comm_TP4 before delving into the deep,
  215. dark world of technical information.
  216.  
  217.  
  218.  
  219. HOW TO USE THE INTERRUPT HANDLERS IN YOUR PROGRAM
  220.  
  221. You may invoke SetupCOMPort to change the settings of the COM ports at any
  222. time.  Unless you are sure the port is already set up correctly, you should
  223. use it before you attempt to communicate with the port.
  224.  
  225. You may turn DTR and RTS on and off with Set_DTR_RTS in order to provide
  226. simple hardware handshake capability.
  227.  
  228. You may install COM port handlers with InstallInt at any time and any order.
  229.  
  230. You may uninstall COM port handlers with RemoveInt at any time and any order.
  231.  
  232. You may force any of the input or output buffers empty at any time by using
  233. Procedure EmptyBuffer.  This allows you to abort any interrupt driven transmit
  234. routine.  It also allows you to flush unwanted input from the receive buffers.
  235.  
  236. To read data that has come in from a port, one of the following is required:
  237.  
  238.      1) FUNCTION ReadCOM (Com : BYTE) : CHAR;
  239.         FUNCTION TimedReadCOM (Com : BYTE; VAR Data : CHAR) : BOOLEAN;
  240.  
  241.         where Com <= MaxPorts.  Usually Com will refer to the DOS COM port
  242.         number, but it does not have to.  In any case, the global value
  243.         COMNmbr [Com] indicates the DOS COM port number.
  244.  
  245.         ReadCOM will wait until data becomes available if there is no data in
  246.         the receive buffer - forever if necessary.
  247.  
  248.         TimedReadCOM will abort and return FALSE if data does not become
  249.         available in a relatively short period of time.  The data is placed in
  250.         the VARiable parameter 'Data' when TimedReadCOM returns TRUE.
  251.  
  252.      2) DisableInts;
  253.         CharReady := InTail [Com] <> InHead [Com];
  254.         EnableInts;
  255.  
  256.         where CharReady is a user defined BOOLEAN that will be TRUE if data is
  257.         waiting in the buffer or FALSE if not data is waiting.
  258.  
  259.         DisableInts;
  260.         CharData := CHR(InBuffer [Com, InHead [Com]]);
  261.         InHead [Com] := (InHead [Com] + 1) MOD (MaxInSize + 1);
  262.         EnableInts;
  263.  
  264.         where CharData is a user defined CHAR variable that will contain the
  265.         next item in the buffer IF AND ONLY IF CharReady is TRUE.  The item is
  266.         removed from the buffer with the statement following the CharData
  267.         assignment.
  268.  
  269. To write data to a port, use one of the following methods:
  270.  
  271.      1) FUNCTION WriteCOM (Com : BYTE; Data : STRING) : BOOLEAN;
  272.         PROCEDURE WriteCOM (Com : BYTE; Data : STRING);
  273.         PROCEDURE IWriteCOM (Com : BYTE; Data : STRING);
  274.  
  275.         where Com <= MaxPorts.  Usually Com will refer to the DOS COM port
  276.         number, but it does not have to.  In any case, the global value
  277.         COMNmbr [Com] indicates the DOS port number.  Data is either STRING
  278.         or CHAR information to be sent to the port.
  279.  
  280.         Both the function and procedure declarations of WriteCOM will wait for
  281.         all of the data to be sent.  If it has trouble sending the information,
  282.         both will time out and return.  Use the function declaration if you
  283.         want to be notified when this happens.  A returned FALSE indicates that
  284.         the transmission failed.
  285.  
  286.         IWriteCOM returns immediately after placing all data in the transmit
  287.         buffer.  The data is sent in the background while your program does
  288.         other things.  It is not practical to use IWriteCOM for sending single
  289.         characters since it always calls WriteCOM once.  No provision is made
  290.         to handle transmission failures.
  291.  
  292.      2) PortReady := ((PORT [COMPort [Com]+LSR] AND $20) = $20);
  293.  
  294.         where PortReady is a user defined BOOLEAN variable which will be TRUE
  295.         if the port Transmitter Holding Register is ready for a character to
  296.         send to the port.
  297.  
  298.         PortReady := PortReady AND CTS [Com] AND DSR [Com];
  299.  
  300.         This statement allows you to see if the CTS and DTR lines indicate that
  301.         the other serial device is ready to receive data.  It may be omitted if
  302.         you do not want to check these lines.
  303.  
  304.         PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] AND $7F;
  305.         PORT [COMPort [Com]+THR] := ORD (CharData);
  306.  
  307.         This statement places the data in the transmit buffer.  CharData is a
  308.         user-defined CHAR variable that contains a character to send to the
  309.         port.  PortReady, as defined above, MUST be TRUE before placing this
  310.         data in the Transmitter Holding Register otherwise the previous data
  311.         item will be lost.
  312.  
  313. NOTE:   These low-level code fragments are provided so that you can write
  314.         receiver and transmitter routines which support your particular needs.
  315.         See the following technical information for more details.
  316.  
  317.  
  318.  
  319. SETTING COMM_TP4 UP TO USE IN YOUR OWN PROGRAMS - TECHNICAL INFORMATION
  320.  
  321. In order to use the serial routines, simply include Comm_TP4 in your USES
  322. statement at the top of the program.  Of course, you can add or delete routines
  323. from the TPU source code to make it fit your applications.  Because of Turbo's
  324. smart linking, however, it is not necessary to remove code that you do not need
  325. in a particular program since unused code is not added to the final .EXE file.
  326.  
  327. All of the variables necessary for developing your own customized input/output
  328. routines are in the INTERFACE section so that you do not have to recompile the
  329. unit if you include new serial I/O source code in your program.
  330.  
  331. Several conditional compilation directives at the top of the source file allow
  332. you to compile TPUs customized for special applications:
  333.  
  334.      {$DEFINE ErrorChecking} - Inclusion of this directive will enable various
  335.                                error checking code that could help you debug a
  336.                                program under development.  It can also be used
  337.                                as a basis for your own error checking routines.
  338.                                To safeguard against fatal run-time errors, the
  339.                                procedures InstallInt & RemoveInt always contain
  340.                                some error checking whether this directive is
  341.                                present or not.
  342.  
  343.      {$DEFINE NoMessageCode} - This directive is used to eliminate all code
  344.                                which would send error messages to the screen.
  345.                                It does not disable error handling.
  346.  
  347.                                The global variables ErrMsgX, ErrMsgY, and
  348.                                ShowMessages are also disabled with this
  349.                                directive.
  350.  
  351.                                Error messages consist of a beep, the procedure
  352.                                or function name, the handler number, and the
  353.                                error description.
  354.  
  355.      {$DEFINE FWriteCOM}     - WriteCOM is the main procedure which is used to
  356.                                send data to a port.  It times out if the data
  357.                                cannot be transmitted for some reason.  Placing
  358.                                this directive at the top of the source file
  359.                                causes WriteCOM to be made a function that
  360.                                returns a boolean value of TRUE if all of the
  361.                                data was transmitted successfully.
  362.  
  363. I would suggest that you use the {$UNDEF} directive in place of {$DEFINE} when
  364. you do not want to use one of these compilation directives.  By doing so, you
  365. will always realize that the directive exists when you casually look over the
  366. source code.
  367.  
  368. Several CONSTants exist which can be used to customize Comm_TP4 for a variety
  369. of purposes:
  370.  
  371.      MaxPorts   - The maximum number of ports which can be supported at the
  372.                   same time.  The maximum possible setting of MaxPorts is
  373.                   restricted only by Turbo Pascal's space limitations for
  374.                   TPU files.
  375.  
  376.      MaxInSize  - The maximum size of the input and output buffers.  Both are
  377.      MaxOutSize   provided so that memory can be conserved if the sizes needed
  378.                   for input and output are different, and both affect the
  379.                   maximum possible value of MaxPorts.
  380.  
  381.      COMNmbr    - A predefined array which can be used to identify the actual
  382.                   COM port serviced in case the order does not agree with the
  383.                   internal port/buffer numbers.
  384.  
  385.                   Actual COM number = COMNmbr [Software Port Number]
  386.  
  387.      COMPort    - A predefined array of base addresses for the INTEL 8250
  388.                   serial ports available.  Comm_TP4 comes preconfigured with
  389.                   the standard IBM/DOS addresses used for COM1 through COM4 but
  390.                   can be set up for any valid port address.
  391.  
  392.                   To access the registers of a given port, use the following
  393.                   constants:
  394.  
  395.                   THR - Transmit Holding Register
  396.                   RHR - Receive Holding Register
  397.                   DLL - Divisor Latch Register Least Significant Byte
  398.                   IER - Interrupt Enable Register
  399.                   DLM - Divisor Latch Register Most Significant Byte
  400.                   IIR - Interrupt ID Register
  401.                   LCR - Line Control Register
  402.                   MCR - Modem Control Register
  403.                   LSR - Line Status Register
  404.                   MSR - Modem Status Register
  405.  
  406.                   For example, to access the Modem Control Register, use the
  407.                   following expression where Com is the port handler number:
  408.  
  409.                   PORT [COMPort [Com] + MSR]
  410.  
  411.                   One exception exists however.  The THR, RHR, and DLL occupy
  412.                   the same address, as do the DLM and IER.  To distinguish
  413.                   which registers you are accessing, use the LCR register.
  414.  
  415.                   Do not be confused by THR and RHR, as they are really the
  416.                   same register, but functions differently depending on whether
  417.                   you write to or read from it.  In any case, to select the
  418.                   THR, RHR, and the IER, use the following commands:
  419.  
  420.                   PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] AND $7F;
  421.  
  422.                   To select the DLL and DLM registers, use:
  423.  
  424.                   PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] OR $80;
  425.  
  426.      IRQNmbr    - A predefined array which identifies the interrupt priority to
  427.                   be used for each port.  Lower IRQ numbers cause the port to
  428.                   receive attention first if a conflict arises.  This number
  429.                   depends on a hardware setting, and can have fatal effects
  430.                   if numbers other than 3 or 4 are used.  You must know exactly
  431.                   what you are doing if you change these numbers.  Comm_TP4
  432.                   comes preconfigured with the standard IBM/DOS IRQ numbers for
  433.                   COM1 through COM4.
  434.  
  435.      ChainInt   - A predefined array which is not used at this time.  It is
  436.                   here to remind me to make an enhancement that will allow a
  437.                   Comm_TP4 interrupt handler to be placed in series with a
  438.                   pre-existing interrupt handler.  For example, many memory
  439.                   resident programs place their own keyboard interrupt handler
  440.                   in series with the original DOS interrupt handler so that
  441.                   they can invoke themselves with unique key combinations.
  442.                   The possibilities are intriguing...
  443.  
  444. NOTE:  All of these CONSTants could be changed to VARiables so that your
  445.        program could prompt the user for the correct information about his
  446.        machine.  Dynamically allocated heap variables would come in handy if
  447.        you wanted to change the MaxPorts, MaxInSize, and MaxOutSize constants
  448.        at run-time.  Perhaps Comm_TP4 will incorporate these ideas later on.
  449.  
  450. Several VARiables exist which can be used to poll Comm_TP4 for the current port
  451. status.  They can also be used to build your own low-level Comm_TP4 functions
  452. for a variety of purposes:
  453.  
  454.      OutBuffer    - These two dimensional character arrays are the output and
  455.      InBuffer       input buffers for all of the ports which can be serviced
  456.                     by the Comm_TP4 routines.  CONSTants MaxPorts, MaxInSize,
  457.                     and MaxOutSize directly affect the size of these arrays,
  458.                     so you should optimize their settings for your particular
  459.                     application.  All buffer operations are outlined below.
  460.  
  461.      OutHead      - These one dimensional word arrays are the pointers into the
  462.      OutTail        buffer arrays.  They are used so that the buffers become
  463.      InHead         circular queues.  The head points to the next item in the
  464.      InTail         buffer, unless (Head = Tail) in which case the buffer is
  465.                     empty.  The tail points points to the place where new data
  466.                     will be placed when it arrives, unless (Tail + 1) MOD
  467.                     MaxSize = Head in which case, the buffer is full.
  468.  
  469.                     When you want to take something out of a buffer:
  470.  
  471.                     1) You must first be sure the buffer is not empty.
  472.                     2) Get the character with Buffer [Com] [Head]
  473.                     3) Adjust the head pointer to (Head + 1) MOD MaxSize
  474.  
  475.                     When you put something into the buffer:
  476.  
  477.                     1) You must first be sure the buffer is not full
  478.                     2) Put the character into Buffer [Com] [Tail]
  479.                     3) Adjust the tail pointer to (Tail + 1) MOD MaxSize
  480.  
  481.                     In the above examples, the "In" and "Out" qualifiers have
  482.                     been left out.  Also, "Com" refers to the handler number.
  483.  
  484.                     NOTE:  ALWAYS disable the hardware interrupts before you
  485.                     do anything with the input or output buffers and pointers.
  486.                     Not doing so will cause hard to find run-time errors, but
  487.                     NEVER forget to re-enable the interrupts after you disable
  488.                     them.  Not doing so will disable other crucial hardware
  489.                     operations along with your serial port I/O functions.
  490.  
  491.      IntInstalled - This one dimensional boolean array indicates when a port
  492.                     handler has been installed.  You should never change its
  493.                     value as doing so will eventually cause the computer to
  494.                     crash since it controls the modification of the system
  495.                     interrupt vectors.  To read the install status, check the
  496.                     following expression, where Com refers to the appropriate
  497.                     handler number:
  498.                                        IntInstalled [Com]
  499.  
  500.      ErrorCode    - ErrorCode is set to 0 after a serial port operation has
  501.      ErrorPort      occurred without any errors.  If it is not zero, ErrorPort
  502.                     indicates which port the error occurred on.  ErrorCode will
  503.                     be one of the following:
  504.  
  505.                                   1 - Invalid port number
  506.                                   2 - Port already installed
  507.                                   3 - Port not installed yet
  508.                                   4 - Timeout writing to port
  509.                                   5 - Timeout reading a port
  510.  
  511.                     ErrorCode and ErrorPort remain valid until the next serial
  512.                     port operation is requested.
  513.  
  514.      ShowMessages - This global boolean variable is used to tell Comm_TP4's
  515.                     error handler whether or not it should display messages
  516.                     on the screen.  TRUE allows messages to be displayed, and
  517.                     the default value may be set in the unit initialization
  518.                     code.  If the {$DEFINE NoMessageCode} directive is used,
  519.                     this variable will not exist.
  520.  
  521.      ErrMsgX      - Like ShowMessages, these global variables are used by the
  522.      ErrMsgY        Comm_TP4 error handler.  The {$DEFINE NoMessageCode}
  523.                     directive also removes them from the code.
  524.  
  525.                     ErrMsgX and ErrMsgY determine where the messages will be
  526.                     printed.  A coordinate of 0 will cause the message to print
  527.                     at the current cursor coordinate given by WHEREX or WHEREY.
  528.                     Their default values are 0.  If you want the messages to be
  529.                     written at a specific screen position, you must set them to
  530.                     the desired coordinates before you request a serial port
  531.                     operation which uses error checking.  The default settings
  532.                     may be specified in the unit initialization code.
  533.  
  534.      DTR_RTS      - This array controls whether or not the Comm_TP4 routines
  535.                     are allowed to change the status of DTR and RTS.  Programs
  536.                     which use modems may not want the software to drop DTR and
  537.                     RTS since this can cause the modem to hang up.  A TRUE
  538.                     enables DTR and RTS setting, and the default value may be
  539.                     specified in the unit initialization code.
  540.  
  541.                     Be wary of hardware handshaking...  I have found that my
  542.                     modem refuses to work when I set DTR_RTS TRUE even though
  543.                     it theoretically should work.  Actually, I prefer to write
  544.                     my applications so they turn DTR and RTS on, them leave
  545.                     them on.  Hardware handshakes end up being headaches more
  546.                     often than not.
  547.  
  548.      CD           - These one dimensional boolean arrays indicate the current
  549.      CTS            status of each port's input lines.  They are correctly set
  550.      DSR            when the program starts up, but only track changes in
  551.      RI             status while an interrupt handler is installed AND while
  552.                     the Modem Status Change interrupt type is enabled.  When
  553.                     you install an interrupt handler, each array is set again.
  554.                     The current status of an individual line can be read while
  555.                     an interrupt handler is not installed as well.  To do so,
  556.                     use one of the following statements, where Com refers to
  557.                     the port handler number:
  558.  
  559.                       CD  [Com] := ($80 AND PORT [COMPort [Com]+MSR] <> 0);
  560.                       CTS [Com] := ($10 AND PORT [COMPort [Com]+MSR] <> 0);
  561.                       DSR [Com] := ($20 AND PORT [COMPort [Com]+MSR] <> 0);
  562.                       RI  [Com] := ($40 AND PORT [COMPort [Com]+MSR] <> 0);
  563.  
  564.      Framing      - These arrays are used to keep track of the number of
  565.      Break          communications errors which occur on each port.  This is
  566.      Overrun        perhaps a trivial operation, however, it does serve to
  567.      Parity         document how to detect the error conditions.
  568.  
  569.                     Each array element is initialized to zero on startup, then
  570.                     they are incremented every time a corresponding line status
  571.                     error interrupt occurs.  You may change their values at any
  572.                     time without affecting the operation of the software.
  573.  
  574. Several VARiables exist which are deliberately not included in the INTERFACE
  575. section of the TPU.  They are critical to the internal operation of Comm_TP4,
  576. and should NEVER be altered by external routines.  They are documented here
  577. for completeness.
  578.  
  579.      OldIntVector - This array keeps track of the interrupt vector which should
  580.                     be restored when Comm_TP4 uses RemoveInt to uninstall an
  581.                     interrupt handler.  InstallInt initializes OldIntVector
  582.                     as necessary.
  583.  
  584.      ExitSave     - A pointer which is necessary for linking into Turbo's exit
  585.                     procedure structure.  This is what allows the automatic
  586.                     uninstallation of Comm_TP4 interrupts whenever the program
  587.                     terminates - whether normally or abnormally.  ExitSave is
  588.                     initialized on program startup.
  589.  
  590.      IRQMask      - A bit map of all IRQ levels which currently have Comm_TP4
  591.                     interrupts installed.   This makes it fairly easy for the
  592.                     InstallInt and RemoveInt procedures to know whether or not
  593.                     they need to alter the interrupt vectors and the 8259 IMR.
  594.                     The primary reason for using IRQMask, however, is to allow
  595.                     the interrupt handler to enable higher priority interrupts
  596.                     while preventing Comm_TP4 from interrupting itself.  Only
  597.                     InstallInt and RemoveInt may modify the value of IRQMask
  598.                     after it is initialized to zero on program startup.
  599.  
  600.  
  601.  
  602. KNOWN PROBLEMS WITH COMM_TP4
  603.  
  604. At this time, the interrupt handler is not efficient enough to operate well
  605. at high baud rates on slow PC's if you have too many of the interrupt types
  606. enabled.  It will fail, for example, if you try to communicate at 9600 baud
  607. on a 4.77 MHz machine while the Line Status, Modem Status, and Receive
  608. interrupt are all activated.  I plan to explore the nature of the problem
  609. further in the near future.
  610.  
  611.  
  612.  
  613. CONSIDERATIONS FOR PROGRAMS WHICH USE INTERRUPT HANDLERS
  614.  
  615. Interrupt handlers operate in ways that can confuse those who are not used to
  616. working with them, so, the following discussion may be helpful if you are
  617. planning to modify Comm_TP4.
  618.  
  619. Keep in mind that an interrupt can occur at ANY time.  This means that a Pascal
  620. statement could be interrupted during its evaluation.  Usually this is not a
  621. problem, but sometimes it is very much a problem.  This is the case when both
  622. the interrupted code and the interrupt handler need to access and/or modify the
  623. same data.  Consider the following trivial, unrealistic, but enlightening code
  624. fragment:
  625.                         X := 1;
  626.                         { Interrupt1 }
  627.                         Y := 2;
  628.                         Z := X * Y;
  629.                         IF (Y = { Interrupt2 } Z)
  630.                           THEN WRITE ('EQUAL')
  631.                           ELSE WRITE ('NOT EQUAL');
  632.  
  633. It seems obvious that the else clause will never be executed, but this can be
  634. very untrue in the case of a program which uses interrupts!  If, for example,
  635. an interrupt handler interrupted this program at the comment { Interrupt1 },
  636. we can easily see that the program would not do what we expect it to do if the
  637. interrupt handler changed the value of X.  A less obvious flaw shows up though,
  638. if you consider an interrupt occurring at { Interrupt2 }.  Here, both Y and Z
  639. could actually equal 2, but since the interrupt changes Z before it finishes
  640. comparing the values, the comparison fails.  Once again, this is a non-sensible
  641. example, but try to imagine the same thing happening with our serial port
  642. handler buffers and buffer pointers...
  643.  
  644. The whole point is that certain operations should not be interrupted, and this
  645. is where the DisableInts and EnableInts inline procedures come into play.  They
  646. are used to temporarily suspend interrupt processing and to re-enable interrupt
  647. processing when the program has passed the danger zones.  This is the reason
  648. you will see them sprinkled throughout the Comm_TP4 routines, and if you plan
  649. to write you own serial I/O routines, you will need to know where to put them.
  650.  
  651.    1) Always disable interrupts while setting the 8259 interrupt hardware.
  652.  
  653.    2) Consider disabling interrupts when you are accessing data which could
  654.       be altered by the interrupt handler.  The key is to be able to discern
  655.       whether or not the effects of an interrupt would cause the program to
  656.       operate abnormally -or- if an interrupt could even occur in crucial
  657.       areas.  Obviously, if an interrupt has not been installed, it is
  658.       pointless to disable it during that section of code.
  659.  
  660.    3) Remember that you must not leave interrupts off for significant periods
  661.       of time.  The clock, keyboard, disk drives, etc. all work off of the
  662.       hardware interrupt system.  Things could really get flaky if you forgot
  663.       to reenable interrupts after turning them off - besides, you would
  664.       defeat the whole purpose behind using an interrupt driven routine.
  665.  
  666.    4) Be careful that you don't paint yourself into a corner by disabling
  667.       interrupts during an operation that requires the interrupts to be on.
  668.       For example, if we disabled interrupts while checking to see if data
  669.       was in the input buffer, we had better turn them back on sometime before
  670.       the loop returns to check the buffer again, or else you've just created
  671.       an endless loop.
  672.  
  673. Interrupt handlers can be tricky to use, but with the right mix of caution,
  674. bravery, and forethought the results can be impressive.  Don't let the words
  675. of warning scare you off!
  676.  
  677.  
  678.  
  679. SUPPORT
  680.  
  681. User support may be obtained by contacting me via the LeTourneau University BBS
  682. at (214) 237-2742.  The BBS runs 24 hours a day and accepts calls at 300, 1200,
  683. and 2400 baud.  Since I am the SysOp of this board, this is the fastest way to
  684. reach me.  My GEnie mail address is K.BULGRIEN, but money being at a premium,
  685. I do not always check in on a regular basis.  I may also be reached during
  686. business hours at (214) 753-0231 ext. 352.
  687.  
  688. Support will be limited to clarification of the routines I wrote, but don't
  689. holler for help if you haven't bothered to read the documentation first.  I
  690. will be glad to fix any bugs that I may have missed, but I cannot be expected
  691. to debug your routines or enhance my routines for your specific application.
  692. Money does have a way of changing people's minds though...
  693.  
  694.  
  695. UPDATES
  696.  
  697. I will place updates of the enclosed routines on LeTourneau University BBS at
  698. (214) 237-2742.  The BBS runs 24 hours a day and accepts calls at 300, 1200,
  699. and 2400 baud.
  700.  
  701. I will also place updates in the BORLAND RT on the GEnie information service.
  702.  
  703. Possible updates may include:
  704.  
  705.      1) Run-time allocation/configuration of port handler variables and setup
  706.      2) Addition of file transfer protocols
  707.      3) Miscellaneous enhancements
  708.      4) Other language versions
  709.      5) Your ideas...
  710.      6) Bug fixes
  711.  
  712.  
  713.  
  714. VERSION HISTORY
  715.  
  716. Version numbers will always be in the form [a.bc] where [c] indicates a bug fix
  717. or a minor file change, [b] indicates a minor enhancement or rewrite, and [a]
  718. indicates a major addition or rewrite.  I will always increment the version
  719. number when I release a set of files with code changes as I want to maintain
  720. strict control of the status of files which are being distributed.
  721.  
  722.  Beta  ??/87  It was a long time in the making.  The early implementations were
  723.               buggy and undocumented since I was just learning the technology.
  724.  
  725.  1.00  11/88  The first officially released version which was placed in the
  726.               Borland Roundtable on GEnie.  The code was written under Turbo
  727.               Pascal Version 3.01A and I ported the routines to Version 4.0 &
  728.               5.0, and then to Turbo C 2.0.  Subsequent versions have been
  729.               developed under Turbo Pascal 4.0/5.x and ported to the other
  730.               language implementations.
  731.  
  732.  1.01  02/89  Bug fix in Procedure RemoveInt.  It correctly uninstalled the
  733.               interrupt code except that it would disable all IRQ interrupts
  734.               except the one used by the specified COM port.  (An AND instead
  735.               of an OR in the statement writing to PORT [$21].  Uploaded the
  736.               new version to GEnie to replace the old one.
  737.  
  738.  1.10, 02/89  Interrupt transmitter added. (Procedure IWriteCOM)
  739.  
  740.  1.20, 02/89  Major improvements corrected limitations and potential flaws
  741.               in the code.  Unlimited port support was made possible by
  742.               allowing the user to specify IRQ numbers with port numbers and
  743.               addresses instead of using the assumed DOS standards.  To use
  744.               the additional features, the interrupt handler now processes
  745.               all ports with the same IRQ when it is invoked.  Planning for
  746.               chained interrupts was started with this revision.
  747.  
  748.  1.30, 03/89  Converted code to a unit file with a separate TTY test program.
  749.               Repositioned DataBits parameter in SetupCOMPort so the order
  750.               is more intuitive.
  751.  
  752.  1.31, 10/89  Added the conditional compilation directive Online to optionally
  753.               disable RTS & DTR from being shut off if the program expects a
  754.               modem online at startup.  Added the conditional compilation
  755.               directive Debug to optionally disable debugging code.
  756.  
  757.  1.40, 10/89  Added the conditional compilation directive FWriteCOM that lets
  758.               you easily convert procedure WriteCOM to a function that returns
  759.               a boolean TRUE if the transmission was successful.  Also added
  760.               CONST COMNmbr that shows which COM port each handler refers to
  761.               in case non-sequential port numbers are used.  Renamed IRQNum
  762.               to IRQNmbr to match COMNmbr.  I moved more declarations to the
  763.               INTERFACE section so they are visible to the calling program.
  764.               Added procedure EmptyBuffer.  Expanded the initialization code
  765.               so that it sets up all variables which might be needed by the
  766.               calling program.  Renamed the conditional compilation directive
  767.               Debug to ErrorChecking.  Rewrote the debugging code and error
  768.               message handling by adding in procedure MakeError and function
  769.               ValidPort.  Added variables ErrMsgX, ErrMsgY, ErrorCode, and
  770.               ErrorPort to make the error handling code more versatile.  Added
  771.               the conditional compilation directive ShowMessages to enable or
  772.               disable the error messages.  Removed the buffer initialization
  773.               commands from InstallInt.  Fixed IWriteCOM bugs:  1) Eliminated
  774.               the possibility of it locking up on a full output buffer, and 2)
  775.               added a check to see if the interrupt was already on before
  776.               attempting to turn it on.  This also improved the through-put
  777.               efficiency for the buffers during heavy data transfer.
  778.  
  779.  1.50, 11/89  Removed the conditional compile directives Online & ShowMessages.
  780.               ShowMessages is now a global boolean variable which may be set by
  781.               the calling code.  Added the compiler directive NoMessageCode
  782.               which is used to optionally eliminate the error message support
  783.               and the control variables ShowMessages, ErrMsgX, and ErrMsgY.
  784.               The global boolean DTR_RTS was added to allow the calling code to
  785.               decide if DTR and RTS should be modified.  This replaces the less
  786.               flexible conditional compilation directive Online.  Added the
  787.               procedure Set_DTR_RTS which is called when DTR and RTS are to be
  788.               set.  Fixed a bug in InstallInt and RemoveInt so that they now
  789.               work correctly when more than one port handler is installed on a
  790.               single IRQ.  Created the header file Comm_TC2.H for the Turbo C
  791.               version.  Renamed Carrier to CD and added CTS, DSR, and RI modem
  792.               status arrays with the code to track their current states.  Added
  793.               the Break, Framing, Overrun, and Parity arrays along with code to
  794.               track the number of communications errors.  Streamlined various
  795.               parts of the code by removing some unnecessary DisableInts and
  796.               EnableInts statements.  Reworked some of the I/O routines.  Fixed
  797.               IntHandler so that it safely enables higher priority interrupts
  798.               while making sure Comm_TP4 will never interrupt itself.  Added
  799.               IRQMask and modified InstallInt and RemoveInt for this feature.
  800.  
  801.  
  802.  
  803. THE END
  804.