home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
pascal
/
library
/
dos
/
communic
/
common
/
comm_tp4.doc
next >
Wrap
Text File
|
1989-11-15
|
43KB
|
804 lines
Comm_TP4.PAS Ver. 1.50 - RS-232 Support for IBM Compatibles - Documentation
(c) Copyright, 1989
Kevin R. Bulgrien
November, 1989
c/o LeTourneau University
Microcomputer Services
P.O. Box 7001
Longview, TX 75607
INTRODUCTION
This utility is an answer to the many public domain serial port routines which
I have acquired in the quest for good RS-232 support under Turbo Pascal. Unlike
the others, this one works. Not only does it work, but it also allows you to
use as many COM ports as you want at the same time. Designed to be relatively
simple and easy to use, I hope many will benefit from it.
This code only works under Turbo Pascal versions 4.0 and 5.x. For functionally
equivalent routines that work under Turbo Pascal 3.0, look for COMM_TP3. Also,
a Turbo C version called COMM_TC2 is available. While it was written for Turbo
C 2.0, it will probably also work for lower versions as well. Additionally, I
am in in the process of developing a Turbo Assembler version called Comm_TA1.
DISTRIBUTION POLICY
The following files must all be present whenever this package is distributed:
COMM_TP4.PAS - Turbo 4.0 & 5.x compatible TPU source code
COMM_TP4.DOC - Documentation for the TPU source code
COMM_TTY.PAS - Demo terminal emulator which uses COMM_TP4
This package may be used in any non-commercial application without restriction,
although I do require that the source code not be distributed in a modified
form unless you clearly identify the modifications and do not remove any of
my copyright information, distribution policy, or other relevant documentation.
If you wish to use this package for development of software which will be sold
or released with a shareware notice, I do require that a one-time registration
fee of $10.00 be paid to me. Because this source code is a platform from which
one can build other tools, you are not exempt from this requirement if you have
to modify or add to my code in order to make it fit your specific application.
A letter which acknowledges your payment will be sent to you by return mail.
I specifically prohibit any distribution of the source code where a disk fee is
charged, unless I have granted written permission to the specific vendor making
such a charge. A general access fee to an electronic bulletin board system is
not considered a disk fee.
I also specifically prohibit any person or entity from claiming copyright on
this material without first obtaining my personal, written consent. Monetary
compensation to me will be required for any such agreement to be valid. This
does not infer that you may not copyright material which uses this product.
DISCLAIMER
While I have extensively tested the routines in this package, I will not be
held responsible for any consequence arising from the use thereof. It is the
responsibility of the user to determine whether or not the enclosed routines
will satisfactorily perform the required functions.
This software directly accesses the Intel 8250 UART as well as the Intel 8259
interrupt controller hardware. Though they are IBM standards, it is possible
that some manufacturers could use different hardware to supply these functions,
in which case, program compatibility would be questionable.
CREDITS
Credit for this product must go to the various authors who showed me that if I
wanted something that worked, I would have to write it myself. I won't try to
identify the items, but I obtained at least twelve serial port handlers from
GEnie which could not satisfy my meager requirements. It amazing to see how
much code is out there which either: 1) does not perform satisfactorily, 2)
has inadequate documentation, 3) is so complex that only an expert could use
it, or, 4) does not contain modifiable source code.
All of the enclosed code is completely original to me, and has not been stolen,
borrowed, or purchased from any other source.
Credit for the information on the IBM compatible hardware must go to the
Prentice-Hall book "Systems Software Tools" by Ted J. Biggerstaff. I was
amazed to find a book that so clearly outlined the IBM PC interrupt and serial
port hardware. The world of IBM PC technical information is a desert, and
this book is a rare oasis in the midst of it.
Additionally, credit must also go to the "DOS Programmer's Reference" by Terry
R. Dettmann which is published by the QUE corporation. I would recommend it
to any programmer who needs a good DOS or BIOS reference.
FEATURES
- Easily expandable interrupt routine
- Interrupt driven send and receive routines
- Carrier Detect monitoring for modem support
- Turbo Pascal 4.0 & 5.x compatible TPU source code
- Convenient TPU format keeps your code uncluttered
- Easy to customize for your hardware, and software requirements
- Uninstalls the interrupt handlers on abnormal program termination
- Number of COM ports limited only by buffer sizes & Turbo memory limits
- A TPU which handles 4 ports can be less than 8K in size
- Easy to use even if you don't understand how it works
- All COM ports handled concurrently in the background
- Example TTY and port setup routines included
- Optional error checking and error messages
- Extensive documentation for software use
- Heavily documented source code
DESIGN PHILOSOPHY
This is a rather grand heading, but I think now is a good time to share the
philosophy used while writing this software. This, along with a look at the
code and the rest of the documentation, will help you to decide if Comm_TP4
is for you.
Most serial applications which I have been involved with were very simple in
nature. I did not require a super-high-performance-do-everything-imaginable
piece of software, and as a result, I wrote a port handler which reflected my
particular needs - but also a little bit more.
For example, the simple monitoring of Carrier Detect makes this an ideal
package to write online BBS support programs with. One of the university
students here has used my package to write a complete online game.
Also, early versions of Comm_TP4 only supported standard DOS ports, but since
I saw that some people would need non-standard setups, I rewrote the code so
that it would allow most any setup. I have yet to need such a feature, but it
is there and you can be sure that I will keep on adding more support items
like this as time goes on.
Perhaps one of the most important guiding principles I have tried to abide by
can be summed up with a KISS (Keep It Simple Stupid). I have seen one or two
communications packages that provided a plethora of low level routines which
could be used to manipulate every aspect of the hardware. I have deliberately
kept this type of code out of Comm_TP4 in the interest of providing a simple-
to-use package rather than an it-does-everything one. I figure that if someone
knows how to use the low-level operations, they probably know enough to be able
to add them to my code. Because most people do not need complicated routines,
I felt I would gain more friends by making Comm_TP4 easy-to-use than enemies by
making Comm_TP4 "incomplete". Make no mistake, though, "incomplete" is a very
relative term, and I really don't believe it applies here.
In summary, I am confident that the routines are high-performance enough to
satisfy the bulk of serial port applications. They do not, however, provide
a do-everything-imaginable user interface, and yet, there is little that one
cannot do. Easy-to-use, flexible, and growing are the key words.
GETTING STARTED - DOES IT REALLY WORK?
If you want to prove that these routines really work, first compile the source
file COMM_TP4.PAS into a TPU file with your Turbo compiler.
IMPORTANT NOTE: Never compile interrupt driven routines with the stack
checking code enabled. Doing so will cause the program
to crash whenever the interrupt is invoked.
Next, compile and run the COMM_TTY program as it is distributed. What you will
see is a crude terminal emulation program which uses the COMM_TP4 routines. It
is capable of receiving data from several COM ports at the same time. The
screen echoes data from the "logged" COM port and buffers the data from other
ports. When a different COM port is logged, the buffered data is sent to the
screen. Also provided is a setup routine that lets you select a baud rate from
110 to 38400. The other protocol attributes are also selectable.
AN EXAMPLE OF HOW EASY IT IS TO USE COMM_TP4
All it takes is a few simple commands. For example, let us set up the serial
port to 2400 baud, 8 bits, no parity, and 1 stop bit. Then, we'll install an
interrupt handler and send a string out the port, and wait for a short
response. Lastly, we will shut off the interrupt handler.
The program that follows will work with COM1 or COM2 without any modification
to the TPU source code that has been included with this documentation.
PROGRAM TestPort;
USES Comm_TP4;
CONST
Com = 1; { Use COM1, use 2 for COM2 }
VAR
InputData : CHAR;
BEGIN
SetupCOMPort (Com, ORD(B2400), 8, ORD(None), 1);
InstallInt (Com);
WriteCOM (Com, 'This really is easy. Isn't it?');
InputData := TimedReadCOM (Com);
RemoveInt (Com);
END.
That is all there is to it. The Comm_TTY example may be intimidating because
of its size, but that's just because it is a whole terminal emulator.
To be sure, most applications will need a bit more than what is shown here, but
that's what the rest of the documentation addresses. I just thought it would
be good to emphasize the ease of using Comm_TP4 before delving into the deep,
dark world of technical information.
HOW TO USE THE INTERRUPT HANDLERS IN YOUR PROGRAM
You may invoke SetupCOMPort to change the settings of the COM ports at any
time. Unless you are sure the port is already set up correctly, you should
use it before you attempt to communicate with the port.
You may turn DTR and RTS on and off with Set_DTR_RTS in order to provide
simple hardware handshake capability.
You may install COM port handlers with InstallInt at any time and any order.
You may uninstall COM port handlers with RemoveInt at any time and any order.
You may force any of the input or output buffers empty at any time by using
Procedure EmptyBuffer. This allows you to abort any interrupt driven transmit
routine. It also allows you to flush unwanted input from the receive buffers.
To read data that has come in from a port, one of the following is required:
1) FUNCTION ReadCOM (Com : BYTE) : CHAR;
FUNCTION TimedReadCOM (Com : BYTE; VAR Data : CHAR) : BOOLEAN;
where Com <= MaxPorts. Usually Com will refer to the DOS COM port
number, but it does not have to. In any case, the global value
COMNmbr [Com] indicates the DOS COM port number.
ReadCOM will wait until data becomes available if there is no data in
the receive buffer - forever if necessary.
TimedReadCOM will abort and return FALSE if data does not become
available in a relatively short period of time. The data is placed in
the VARiable parameter 'Data' when TimedReadCOM returns TRUE.
2) DisableInts;
CharReady := InTail [Com] <> InHead [Com];
EnableInts;
where CharReady is a user defined BOOLEAN that will be TRUE if data is
waiting in the buffer or FALSE if not data is waiting.
DisableInts;
CharData := CHR(InBuffer [Com, InHead [Com]]);
InHead [Com] := (InHead [Com] + 1) MOD (MaxInSize + 1);
EnableInts;
where CharData is a user defined CHAR variable that will contain the
next item in the buffer IF AND ONLY IF CharReady is TRUE. The item is
removed from the buffer with the statement following the CharData
assignment.
To write data to a port, use one of the following methods:
1) FUNCTION WriteCOM (Com : BYTE; Data : STRING) : BOOLEAN;
PROCEDURE WriteCOM (Com : BYTE; Data : STRING);
PROCEDURE IWriteCOM (Com : BYTE; Data : STRING);
where Com <= MaxPorts. Usually Com will refer to the DOS COM port
number, but it does not have to. In any case, the global value
COMNmbr [Com] indicates the DOS port number. Data is either STRING
or CHAR information to be sent to the port.
Both the function and procedure declarations of WriteCOM will wait for
all of the data to be sent. If it has trouble sending the information,
both will time out and return. Use the function declaration if you
want to be notified when this happens. A returned FALSE indicates that
the transmission failed.
IWriteCOM returns immediately after placing all data in the transmit
buffer. The data is sent in the background while your program does
other things. It is not practical to use IWriteCOM for sending single
characters since it always calls WriteCOM once. No provision is made
to handle transmission failures.
2) PortReady := ((PORT [COMPort [Com]+LSR] AND $20) = $20);
where PortReady is a user defined BOOLEAN variable which will be TRUE
if the port Transmitter Holding Register is ready for a character to
send to the port.
PortReady := PortReady AND CTS [Com] AND DSR [Com];
This statement allows you to see if the CTS and DTR lines indicate that
the other serial device is ready to receive data. It may be omitted if
you do not want to check these lines.
PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] AND $7F;
PORT [COMPort [Com]+THR] := ORD (CharData);
This statement places the data in the transmit buffer. CharData is a
user-defined CHAR variable that contains a character to send to the
port. PortReady, as defined above, MUST be TRUE before placing this
data in the Transmitter Holding Register otherwise the previous data
item will be lost.
NOTE: These low-level code fragments are provided so that you can write
receiver and transmitter routines which support your particular needs.
See the following technical information for more details.
SETTING COMM_TP4 UP TO USE IN YOUR OWN PROGRAMS - TECHNICAL INFORMATION
In order to use the serial routines, simply include Comm_TP4 in your USES
statement at the top of the program. Of course, you can add or delete routines
from the TPU source code to make it fit your applications. Because of Turbo's
smart linking, however, it is not necessary to remove code that you do not need
in a particular program since unused code is not added to the final .EXE file.
All of the variables necessary for developing your own customized input/output
routines are in the INTERFACE section so that you do not have to recompile the
unit if you include new serial I/O source code in your program.
Several conditional compilation directives at the top of the source file allow
you to compile TPUs customized for special applications:
{$DEFINE ErrorChecking} - Inclusion of this directive will enable various
error checking code that could help you debug a
program under development. It can also be used
as a basis for your own error checking routines.
To safeguard against fatal run-time errors, the
procedures InstallInt & RemoveInt always contain
some error checking whether this directive is
present or not.
{$DEFINE NoMessageCode} - This directive is used to eliminate all code
which would send error messages to the screen.
It does not disable error handling.
The global variables ErrMsgX, ErrMsgY, and
ShowMessages are also disabled with this
directive.
Error messages consist of a beep, the procedure
or function name, the handler number, and the
error description.
{$DEFINE FWriteCOM} - WriteCOM is the main procedure which is used to
send data to a port. It times out if the data
cannot be transmitted for some reason. Placing
this directive at the top of the source file
causes WriteCOM to be made a function that
returns a boolean value of TRUE if all of the
data was transmitted successfully.
I would suggest that you use the {$UNDEF} directive in place of {$DEFINE} when
you do not want to use one of these compilation directives. By doing so, you
will always realize that the directive exists when you casually look over the
source code.
Several CONSTants exist which can be used to customize Comm_TP4 for a variety
of purposes:
MaxPorts - The maximum number of ports which can be supported at the
same time. The maximum possible setting of MaxPorts is
restricted only by Turbo Pascal's space limitations for
TPU files.
MaxInSize - The maximum size of the input and output buffers. Both are
MaxOutSize provided so that memory can be conserved if the sizes needed
for input and output are different, and both affect the
maximum possible value of MaxPorts.
COMNmbr - A predefined array which can be used to identify the actual
COM port serviced in case the order does not agree with the
internal port/buffer numbers.
Actual COM number = COMNmbr [Software Port Number]
COMPort - A predefined array of base addresses for the INTEL 8250
serial ports available. Comm_TP4 comes preconfigured with
the standard IBM/DOS addresses used for COM1 through COM4 but
can be set up for any valid port address.
To access the registers of a given port, use the following
constants:
THR - Transmit Holding Register
RHR - Receive Holding Register
DLL - Divisor Latch Register Least Significant Byte
IER - Interrupt Enable Register
DLM - Divisor Latch Register Most Significant Byte
IIR - Interrupt ID Register
LCR - Line Control Register
MCR - Modem Control Register
LSR - Line Status Register
MSR - Modem Status Register
For example, to access the Modem Control Register, use the
following expression where Com is the port handler number:
PORT [COMPort [Com] + MSR]
One exception exists however. The THR, RHR, and DLL occupy
the same address, as do the DLM and IER. To distinguish
which registers you are accessing, use the LCR register.
Do not be confused by THR and RHR, as they are really the
same register, but functions differently depending on whether
you write to or read from it. In any case, to select the
THR, RHR, and the IER, use the following commands:
PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] AND $7F;
To select the DLL and DLM registers, use:
PORT [COMPort [Com]+LCR] := PORT [COMPort [Com]+LCR] OR $80;
IRQNmbr - A predefined array which identifies the interrupt priority to
be used for each port. Lower IRQ numbers cause the port to
receive attention first if a conflict arises. This number
depends on a hardware setting, and can have fatal effects
if numbers other than 3 or 4 are used. You must know exactly
what you are doing if you change these numbers. Comm_TP4
comes preconfigured with the standard IBM/DOS IRQ numbers for
COM1 through COM4.
ChainInt - A predefined array which is not used at this time. It is
here to remind me to make an enhancement that will allow a
Comm_TP4 interrupt handler to be placed in series with a
pre-existing interrupt handler. For example, many memory
resident programs place their own keyboard interrupt handler
in series with the original DOS interrupt handler so that
they can invoke themselves with unique key combinations.
The possibilities are intriguing...
NOTE: All of these CONSTants could be changed to VARiables so that your
program could prompt the user for the correct information about his
machine. Dynamically allocated heap variables would come in handy if
you wanted to change the MaxPorts, MaxInSize, and MaxOutSize constants
at run-time. Perhaps Comm_TP4 will incorporate these ideas later on.
Several VARiables exist which can be used to poll Comm_TP4 for the current port
status. They can also be used to build your own low-level Comm_TP4 functions
for a variety of purposes:
OutBuffer - These two dimensional character arrays are the output and
InBuffer input buffers for all of the ports which can be serviced
by the Comm_TP4 routines. CONSTants MaxPorts, MaxInSize,
and MaxOutSize directly affect the size of these arrays,
so you should optimize their settings for your particular
application. All buffer operations are outlined below.
OutHead - These one dimensional word arrays are the pointers into the
OutTail buffer arrays. They are used so that the buffers become
InHead circular queues. The head points to the next item in the
InTail buffer, unless (Head = Tail) in which case the buffer is
empty. The tail points points to the place where new data
will be placed when it arrives, unless (Tail + 1) MOD
MaxSize = Head in which case, the buffer is full.
When you want to take something out of a buffer:
1) You must first be sure the buffer is not empty.
2) Get the character with Buffer [Com] [Head]
3) Adjust the head pointer to (Head + 1) MOD MaxSize
When you put something into the buffer:
1) You must first be sure the buffer is not full
2) Put the character into Buffer [Com] [Tail]
3) Adjust the tail pointer to (Tail + 1) MOD MaxSize
In the above examples, the "In" and "Out" qualifiers have
been left out. Also, "Com" refers to the handler number.
NOTE: ALWAYS disable the hardware interrupts before you
do anything with the input or output buffers and pointers.
Not doing so will cause hard to find run-time errors, but
NEVER forget to re-enable the interrupts after you disable
them. Not doing so will disable other crucial hardware
operations along with your serial port I/O functions.
IntInstalled - This one dimensional boolean array indicates when a port
handler has been installed. You should never change its
value as doing so will eventually cause the computer to
crash since it controls the modification of the system
interrupt vectors. To read the install status, check the
following expression, where Com refers to the appropriate
handler number:
IntInstalled [Com]
ErrorCode - ErrorCode is set to 0 after a serial port operation has
ErrorPort occurred without any errors. If it is not zero, ErrorPort
indicates which port the error occurred on. ErrorCode will
be one of the following:
1 - Invalid port number
2 - Port already installed
3 - Port not installed yet
4 - Timeout writing to port
5 - Timeout reading a port
ErrorCode and ErrorPort remain valid until the next serial
port operation is requested.
ShowMessages - This global boolean variable is used to tell Comm_TP4's
error handler whether or not it should display messages
on the screen. TRUE allows messages to be displayed, and
the default value may be set in the unit initialization
code. If the {$DEFINE NoMessageCode} directive is used,
this variable will not exist.
ErrMsgX - Like ShowMessages, these global variables are used by the
ErrMsgY Comm_TP4 error handler. The {$DEFINE NoMessageCode}
directive also removes them from the code.
ErrMsgX and ErrMsgY determine where the messages will be
printed. A coordinate of 0 will cause the message to print
at the current cursor coordinate given by WHEREX or WHEREY.
Their default values are 0. If you want the messages to be
written at a specific screen position, you must set them to
the desired coordinates before you request a serial port
operation which uses error checking. The default settings
may be specified in the unit initialization code.
DTR_RTS - This array controls whether or not the Comm_TP4 routines
are allowed to change the status of DTR and RTS. Programs
which use modems may not want the software to drop DTR and
RTS since this can cause the modem to hang up. A TRUE
enables DTR and RTS setting, and the default value may be
specified in the unit initialization code.
Be wary of hardware handshaking... I have found that my
modem refuses to work when I set DTR_RTS TRUE even though
it theoretically should work. Actually, I prefer to write
my applications so they turn DTR and RTS on, them leave
them on. Hardware handshakes end up being headaches more
often than not.
CD - These one dimensional boolean arrays indicate the current
CTS status of each port's input lines. They are correctly set
DSR when the program starts up, but only track changes in
RI status while an interrupt handler is installed AND while
the Modem Status Change interrupt type is enabled. When
you install an interrupt handler, each array is set again.
The current status of an individual line can be read while
an interrupt handler is not installed as well. To do so,
use one of the following statements, where Com refers to
the port handler number:
CD [Com] := ($80 AND PORT [COMPort [Com]+MSR] <> 0);
CTS [Com] := ($10 AND PORT [COMPort [Com]+MSR] <> 0);
DSR [Com] := ($20 AND PORT [COMPort [Com]+MSR] <> 0);
RI [Com] := ($40 AND PORT [COMPort [Com]+MSR] <> 0);
Framing - These arrays are used to keep track of the number of
Break communications errors which occur on each port. This is
Overrun perhaps a trivial operation, however, it does serve to
Parity document how to detect the error conditions.
Each array element is initialized to zero on startup, then
they are incremented every time a corresponding line status
error interrupt occurs. You may change their values at any
time without affecting the operation of the software.
Several VARiables exist which are deliberately not included in the INTERFACE
section of the TPU. They are critical to the internal operation of Comm_TP4,
and should NEVER be altered by external routines. They are documented here
for completeness.
OldIntVector - This array keeps track of the interrupt vector which should
be restored when Comm_TP4 uses RemoveInt to uninstall an
interrupt handler. InstallInt initializes OldIntVector
as necessary.
ExitSave - A pointer which is necessary for linking into Turbo's exit
procedure structure. This is what allows the automatic
uninstallation of Comm_TP4 interrupts whenever the program
terminates - whether normally or abnormally. ExitSave is
initialized on program startup.
IRQMask - A bit map of all IRQ levels which currently have Comm_TP4
interrupts installed. This makes it fairly easy for the
InstallInt and RemoveInt procedures to know whether or not
they need to alter the interrupt vectors and the 8259 IMR.
The primary reason for using IRQMask, however, is to allow
the interrupt handler to enable higher priority interrupts
while preventing Comm_TP4 from interrupting itself. Only
InstallInt and RemoveInt may modify the value of IRQMask
after it is initialized to zero on program startup.
KNOWN PROBLEMS WITH COMM_TP4
At this time, the interrupt handler is not efficient enough to operate well
at high baud rates on slow PC's if you have too many of the interrupt types
enabled. It will fail, for example, if you try to communicate at 9600 baud
on a 4.77 MHz machine while the Line Status, Modem Status, and Receive
interrupt are all activated. I plan to explore the nature of the problem
further in the near future.
CONSIDERATIONS FOR PROGRAMS WHICH USE INTERRUPT HANDLERS
Interrupt handlers operate in ways that can confuse those who are not used to
working with them, so, the following discussion may be helpful if you are
planning to modify Comm_TP4.
Keep in mind that an interrupt can occur at ANY time. This means that a Pascal
statement could be interrupted during its evaluation. Usually this is not a
problem, but sometimes it is very much a problem. This is the case when both
the interrupted code and the interrupt handler need to access and/or modify the
same data. Consider the following trivial, unrealistic, but enlightening code
fragment:
X := 1;
{ Interrupt1 }
Y := 2;
Z := X * Y;
IF (Y = { Interrupt2 } Z)
THEN WRITE ('EQUAL')
ELSE WRITE ('NOT EQUAL');
It seems obvious that the else clause will never be executed, but this can be
very untrue in the case of a program which uses interrupts! If, for example,
an interrupt handler interrupted this program at the comment { Interrupt1 },
we can easily see that the program would not do what we expect it to do if the
interrupt handler changed the value of X. A less obvious flaw shows up though,
if you consider an interrupt occurring at { Interrupt2 }. Here, both Y and Z
could actually equal 2, but since the interrupt changes Z before it finishes
comparing the values, the comparison fails. Once again, this is a non-sensible
example, but try to imagine the same thing happening with our serial port
handler buffers and buffer pointers...
The whole point is that certain operations should not be interrupted, and this
is where the DisableInts and EnableInts inline procedures come into play. They
are used to temporarily suspend interrupt processing and to re-enable interrupt
processing when the program has passed the danger zones. This is the reason
you will see them sprinkled throughout the Comm_TP4 routines, and if you plan
to write you own serial I/O routines, you will need to know where to put them.
1) Always disable interrupts while setting the 8259 interrupt hardware.
2) Consider disabling interrupts when you are accessing data which could
be altered by the interrupt handler. The key is to be able to discern
whether or not the effects of an interrupt would cause the program to
operate abnormally -or- if an interrupt could even occur in crucial
areas. Obviously, if an interrupt has not been installed, it is
pointless to disable it during that section of code.
3) Remember that you must not leave interrupts off for significant periods
of time. The clock, keyboard, disk drives, etc. all work off of the
hardware interrupt system. Things could really get flaky if you forgot
to reenable interrupts after turning them off - besides, you would
defeat the whole purpose behind using an interrupt driven routine.
4) Be careful that you don't paint yourself into a corner by disabling
interrupts during an operation that requires the interrupts to be on.
For example, if we disabled interrupts while checking to see if data
was in the input buffer, we had better turn them back on sometime before
the loop returns to check the buffer again, or else you've just created
an endless loop.
Interrupt handlers can be tricky to use, but with the right mix of caution,
bravery, and forethought the results can be impressive. Don't let the words
of warning scare you off!
SUPPORT
User support may be obtained by contacting me via the LeTourneau University BBS
at (214) 237-2742. The BBS runs 24 hours a day and accepts calls at 300, 1200,
and 2400 baud. Since I am the SysOp of this board, this is the fastest way to
reach me. My GEnie mail address is K.BULGRIEN, but money being at a premium,
I do not always check in on a regular basis. I may also be reached during
business hours at (214) 753-0231 ext. 352.
Support will be limited to clarification of the routines I wrote, but don't
holler for help if you haven't bothered to read the documentation first. I
will be glad to fix any bugs that I may have missed, but I cannot be expected
to debug your routines or enhance my routines for your specific application.
Money does have a way of changing people's minds though...
UPDATES
I will place updates of the enclosed routines on LeTourneau University BBS at
(214) 237-2742. The BBS runs 24 hours a day and accepts calls at 300, 1200,
and 2400 baud.
I will also place updates in the BORLAND RT on the GEnie information service.
Possible updates may include:
1) Run-time allocation/configuration of port handler variables and setup
2) Addition of file transfer protocols
3) Miscellaneous enhancements
4) Other language versions
5) Your ideas...
6) Bug fixes
VERSION HISTORY
Version numbers will always be in the form [a.bc] where [c] indicates a bug fix
or a minor file change, [b] indicates a minor enhancement or rewrite, and [a]
indicates a major addition or rewrite. I will always increment the version
number when I release a set of files with code changes as I want to maintain
strict control of the status of files which are being distributed.
Beta ??/87 It was a long time in the making. The early implementations were
buggy and undocumented since I was just learning the technology.
1.00 11/88 The first officially released version which was placed in the
Borland Roundtable on GEnie. The code was written under Turbo
Pascal Version 3.01A and I ported the routines to Version 4.0 &
5.0, and then to Turbo C 2.0. Subsequent versions have been
developed under Turbo Pascal 4.0/5.x and ported to the other
language implementations.
1.01 02/89 Bug fix in Procedure RemoveInt. It correctly uninstalled the
interrupt code except that it would disable all IRQ interrupts
except the one used by the specified COM port. (An AND instead
of an OR in the statement writing to PORT [$21]. Uploaded the
new version to GEnie to replace the old one.
1.10, 02/89 Interrupt transmitter added. (Procedure IWriteCOM)
1.20, 02/89 Major improvements corrected limitations and potential flaws
in the code. Unlimited port support was made possible by
allowing the user to specify IRQ numbers with port numbers and
addresses instead of using the assumed DOS standards. To use
the additional features, the interrupt handler now processes
all ports with the same IRQ when it is invoked. Planning for
chained interrupts was started with this revision.
1.30, 03/89 Converted code to a unit file with a separate TTY test program.
Repositioned DataBits parameter in SetupCOMPort so the order
is more intuitive.
1.31, 10/89 Added the conditional compilation directive Online to optionally
disable RTS & DTR from being shut off if the program expects a
modem online at startup. Added the conditional compilation
directive Debug to optionally disable debugging code.
1.40, 10/89 Added the conditional compilation directive FWriteCOM that lets
you easily convert procedure WriteCOM to a function that returns
a boolean TRUE if the transmission was successful. Also added
CONST COMNmbr that shows which COM port each handler refers to
in case non-sequential port numbers are used. Renamed IRQNum
to IRQNmbr to match COMNmbr. I moved more declarations to the
INTERFACE section so they are visible to the calling program.
Added procedure EmptyBuffer. Expanded the initialization code
so that it sets up all variables which might be needed by the
calling program. Renamed the conditional compilation directive
Debug to ErrorChecking. Rewrote the debugging code and error
message handling by adding in procedure MakeError and function
ValidPort. Added variables ErrMsgX, ErrMsgY, ErrorCode, and
ErrorPort to make the error handling code more versatile. Added
the conditional compilation directive ShowMessages to enable or
disable the error messages. Removed the buffer initialization
commands from InstallInt. Fixed IWriteCOM bugs: 1) Eliminated
the possibility of it locking up on a full output buffer, and 2)
added a check to see if the interrupt was already on before
attempting to turn it on. This also improved the through-put
efficiency for the buffers during heavy data transfer.
1.50, 11/89 Removed the conditional compile directives Online & ShowMessages.
ShowMessages is now a global boolean variable which may be set by
the calling code. Added the compiler directive NoMessageCode
which is used to optionally eliminate the error message support
and the control variables ShowMessages, ErrMsgX, and ErrMsgY.
The global boolean DTR_RTS was added to allow the calling code to
decide if DTR and RTS should be modified. This replaces the less
flexible conditional compilation directive Online. Added the
procedure Set_DTR_RTS which is called when DTR and RTS are to be
set. Fixed a bug in InstallInt and RemoveInt so that they now
work correctly when more than one port handler is installed on a
single IRQ. Created the header file Comm_TC2.H for the Turbo C
version. Renamed Carrier to CD and added CTS, DSR, and RI modem
status arrays with the code to track their current states. Added
the Break, Framing, Overrun, and Parity arrays along with code to
track the number of communications errors. Streamlined various
parts of the code by removing some unnecessary DisableInts and
EnableInts statements. Reworked some of the I/O routines. Fixed
IntHandler so that it safely enables higher priority interrupts
while making sure Comm_TP4 will never interrupt itself. Added
IRQMask and modified InstallInt and RemoveInt for this feature.
THE END