Dec 092017
Small library of serial I/O code for Borland C.
File RS232-C.ZIP from The Programmer’s Corner in
Category C Source Code
Small library of serial I/O code for Borland C.
File Name File Size Zip Size Zip Type
RS232.C 44644 10076 deflated
RS232.DOC 47031 12689 deflated
RS_DEMO.C 13449 3842 deflated

Download File RS232-C.ZIP Here

Contents of the RS232.DOC file

RS232.C User Documentation
copyright C. Karcher 1992,93

> About RS232.C:

RS232.C was written to provide all of the basic functionality needed
to employ serial I/O in any application written with Borland 'C'
language compilers. Some features are:

1. Ease of use. No assembly language or library files are used and a
simple "#include" statement is all that is required to access all of
the functions provided.

2. Both input and output are buffered and interrupt driven for

3. Serial ports 1 - 4 are supported on PC, AT and PS/2 compatibles.
Chained interrupts used on port 3 and 4 are allowed for so as not to
interfere with devices such as a mouse or printer. Transmission
speeds of 110 to 115200 baud are available.

4. Detection and utilization of hardware buffered UARTs (NS16550AF
etc.) found in some machines is automatic.

5. Interrupt driven hardware and XON/XOFF flow control is provided for.

6. All source code is included. RS232.C can be used with all memory

This is user supported software and may be distributed freely in a
whole and unmodified state. It may be used in part or wholly, free of
royalty, to develop any commercial applications provided that the
developer has registered RS232.C. Registration entitles the developer
to any future enhancements or upgrades which may be released.
Registration also includes technical support as far as can be provided
via mail, telephone or electronic messaging. To register RS232.C,
send a check or money order for $20.00 to:

Chris A. Karcher
9537 Evanston Ave. N.
Seattle WA 98103-3131

The author may be contacted by mail at the above address, by telephone
at (206)789-7945 or via Compuserve EMAIL to user ID 76406,536.

The author of RS232.C makes no expressed or implied warranty of any
kind with regard to the software or accompanying documentation. In no
event shall the author be held liable for incidental or consequential
damages in connection or arising out of performance or use of any part
of this software.

The distribution should include the following files:

RS232.C - serial communication routines source code
RS232.DOC - documentation (this file)
RS_DEMO.C - sample program demonstrating RS232.C

> Why use RS232.C?

Borland's C compilers (and probably all DOS based C compilers) do
provide some basic support for serial communications but that support
makes use of calls to BIOS routines only. The BIOS serial routines
are adequate only for the slowest of transmission speeds and do not
exploit all of the capabilities of the hardware found in PC's. If
efficient serial communication is required, the programmer must
provide all of the low level software. RS232.C does just that by
taking care of all the low level details and providing the programmer
with all the high level functions needed for efficient serial I/O.

> How to use RS232.C:

All that is required to use RS232.C is to include it in your source
code. The simplest way to do that is with a compiler '#include'
directive, treating RS232.C just as you would a header file. Having
done that, three basic steps are all that are required to perform
serial I/0:

1. Allocate memory for input and output buffers. (It can be pre-
allocated at compile time or allocated via some memory
allocation function like malloc at runtime.)

2. "Open" the port with the function rs_initport, using the
desired communication parameters.

perform I/O with functions provided
. . . . .
. . . . .
. . . . .

3. "Close" the port when finished performing I/O with the
function rs_close before ending the program.

The file RS_DEMO.C, included with the distribution, is a sample
terminal program demonstrating most of the functions available with

The following code demonstrates an extremely simple but functional
program which turns a PC into a dumb terminal capable of communicating
with a modem or another computer:

TERM: a light weight dumb terminal program to demonstrate rs232.c
C. Karcher

#include"src\rs232.c" /* include rs232 variables and functions */


int key = 0;
char input_buffer[1024],output_buffer[1024]; /* allocate buffers */

/* open COM port 1 with 2400 baud, no parity, 8 data bits, 1 stop bit,
a 1024 byte input buffer and a 1024 byte output buffer */

1024U,input_buffer,1024U,output_buffer) > 0)
cprintf("Terminal mode active - press escape to exit\r\n");
cprintf("Unable to open COM port\r\n");
return 0;

/* turn on Data Terminal Ready */

/* display any characters received and send any keystrokes typed until
escape is pressed */
if(rs_inrcvd()) /* if a character has been received */
putch(rs_getbyt()); /* display it */
if(rs_keyhit()) /* if a key has been pressed */
rs_sndbyt((key = getch())); /* send it */
}while(key != 27); /* if the key pressed was ESC, end */

/* turn off Data Terminal Ready */

/* close the port and exit */
return 0;

} /* end TERM */

> RS232.C Functions:

The following is a list of the functions contained in RS232.C and a
brief explanation of the purpose of each one. A detailed description
of each function and it's use follows:

rs_initport - Prepares a COM port for use.
rs_sndbyt - Sends a single byte.
rs_sndstr - Sends a string of bytes.
rs_getbyt - Gets a single byte.
rs_getstr - Gets a string of bytes.
rs_inrcvd - Returns number of bytes which been received.
rs_outfre - Returns amount of free space remaining in output buffer.
rs_error - Returns code for last error detected.
rs_modctrl - Controls or returns status of modem control lines.
rs_break - Sends break to remote equipment.
rs_clrout - Clears output buffer.
rs_clrin - Clears input buffer.
rs_keyhit - Determines if a key has been pressed.
rs_timer - Times events or operations.
rs_setflow - Establishes or returns status of flow control.
rs_close - "Closes" port prepared by rs_initport.

> Function Reference: (Note: The pre defined constants shown in
parentheses with some of the function argument
descriptions or return value descriptions are
defined in RS232.C and may be used for

> rs_initport:

> Purpose:
Prepares a COM port for use.

> Prototype:
int rs_initport(char port, long baud, char parity, char data_bits,
char stop_bits, unsigned in_buf_size, char *in_buf,
unsigned out_buf_size, char *out_buf);

> Arguments:
port: Character indicating which port to use. May be one of
the following:
'1' (RS_PORT1) - COM port 1
'2' (RS_PORT2)
'3' (RS_PORT3)
'4' (RS_PORT4) - COM port 4
baud: The long value indicating the baud rate. Can be any of
the following:
110L (RS_B110) - 110 baud
300L (RS_B300)
600L (RS_B600)
1200L (RS_B1200)
2400L (RS_B2400)
4800L (RS_B4800)
9600L (RS_B9600)
19200L (RS_B19K)
38400L (RS_B38K)
57600L (RS_B57K)
115200L (RS_B115K) - 115200 baud
parity: A character indicating the type of parity checking to use
as follows:
'N' (RS_NOPAR) - no parity
'E' (RS_EVPAR) - even parity
'O' (RS_ODPAR) - odd parity
'S' (RS_SPPAR) - parity bit always space
'M' (RS_MKPAR) - parity bit always mark
data_bits: A character indicating how many data bits to use in each
'8' (RS_DBIT8) - 8 data bits
'7' (RS_DBIT7) - 7 data bits
stop_bits: A character indicating the number of stop bits to use:
'1' (RS_SBIT1) - 1 stop bit
'2' (RS_SBIT2) - 2 stop bits
in_buf_siz: An unsigned integer between 2 and 32768 indicating the
size in bytes of the input buffer. The value must be a
power of 2 (i.e. 2,4,8,16,256,512,1024 etc.).
in_buf: Character pointer to the input buffer allocated by the
out_buf_siz: The unsigned value between 2 and 32768 indicating the
size in bytes of the output buffer. The value must be a
power of 2 (i.e. 2,4,8,16,256,512,1024 etc.).
out_buf: Character pointer to the output buffer.

Note: If RS_POLLED_XMIT is defined, the output buffer is not used
for serial output. In this case the output buffer size may be 0
and the output buffer pointer may be NULL.

> Return value:
rs_initport can return the following integer values indicating
the success or failure of the operation:
4 (RS_UART4) Success, UART detected was a 16550 capable of
hardware buffering. This capability is automatically
3 (RS_UART3) Success, UART detected was a 16550 incapable of
hardware buffering.
2 (RS_UART2) Success, UART detected was an 8250A or 16450.
1 (RS_UART1) Success, UART detected was an 8250B
0 (RS_NOUART) Failure, no UART detected.
-1 (RS_BADIBUF) Failure, input buffer size invalid or input
buffer pointer is a NULL pointer.
-2 (RS_BADOBUF) Failure, output buffer size invalid or output
buffer pointer is a NULL pointer.
-3 (RS_BADPORT) Failure, invalid port argument.
-4 (RS_BADPAR) Failure, invalid parity argument.
-5 (RS_BADDBIT) Failure, invalid data_bits argument.
-6 (RS_BADSBIT) Failure, invalid stop_bits argument.
-7 (RS_BADBAUD) Failure, invalid baud argument.

> Examples:

1024U,input_buffer,1024U,output_buffer) > 0)
cprintf("Terminal mode active - press escape to exit\r\n");

port_ok = rs_initport(port,RS_B2400,RS_NOPAR,RS_DBIT8,RS_SBIT1,

> Notes:

If a port has already been "opened" with rs_initport, it will
automatically be closed before another port (or the same port) is
opened. Only one port may be opened at any one time although the
interrupt service routine installed by rs_initport can coexist
with others sharing the same IRQ (interrupt request). An example
of this would be serial mouse installed on COM port 1 and an
application using rs_initport to prepare access to COM port 3.
In this case, both the mouse driver and the application would be
using IRQ4. Some serial applications "take over" the interrupt
completely which would disable use of the mouse. The interrupt
service routine installed by rs_initport "chains" the interrupt
so that if it is invoked by other than the targeted device,
control is passed to the appropriate routine.

To determine the appropriate I/O address for a given COM port's
UART, rs_initport first uses the BIOS data area and, failing
this, uses the "standard" I/O addresses. Standard IRQ's are also
assumed. If a serial adapter has been installed with other than
the standard I/O addresses and IRQ's, rs_initport will not find
the UART and will fail. The standard addresses and IRQ's assumed
port I/O address IRQ
COM1 3F8 4
COM2 2F8 3
COM3 3E8 (PS/2 = 3220) 4 (PS/2 = 3)
COM4 2E8 (PS/2 = 3228) 3 (PS/2 = 4)

If a UART capable of FIFO buffering is detected, this feature is
automatically enabled. This can greatly reduce the amount of
overhead due to interrupts and can prevent "lost characters" due
to receive overruns at higher transmission speeds.

rs_initport does not alter the state of any of the modem control
output lines (DTR or RTS). They must be explicitly controlled
using rs_modctrl.

> rs_sndbyt
> Purpose:
Sends a single byte.

> Prototype:
int rs_sndbyt(int rs_snd);

> Arguments:
The byte to send (int converted to char).

> Return Value:
Returns 0 if the byte was successfully written to the output
buffer. Returns -1 no port open or byte could not be written.

> Examples:

printf("Unable to send message\n");


> Notes:
rs_sndbyt writes the character into the output buffer and
returns immediately without waiting for the character to
actually be transmitted. If there is no room in the output
buffer, rs_sndbyt will wait until there is room unless output is
currently disabled via flow control. In this case, rs_sndbyt
returns -1 to indicate failure.
If transmit interrupts are disabled by defining RS_POLLED_XMIT,
the buffer is not used and the character is sent when the UART
is free to send it. If output is disabled via flow control,
the character is not sent and rs_sndbyt returns -1.

> rs_sndstr

> Purpose:
Sends a string of bytes.

> Prototype:
int rs_sndstr(int rs_sndcnt, char *rs_sndstr);

> Arguments:
rs_sndcnt indicates the number of bytes to be sent - if a number
other than 0 is specified, the string need not be nul
terminated. If rs_sndcnt is 0, rs_sndstr is treated as a nul
terminated string and characters will be sent up to but not
including the terminating nul. rs_sndstr is a character pointer
to the string to be sent. rs_sndstr can be larger than the size
of the output buffer and up to 32767 bytes.

> Return Value:
Function returns 0 on success. If no port is open, it returns
-1. If the entire string could not be written to the output
buffer, the number of characters written is returned.

> Examples:

rs_sndstr(0, "Hello, world.");

printf("Message could not be sent\n");

> Notes:
Like rs_sndbyt, rs_sndstr writes the characters to the output
buffer and returns immediately - it does not wait for the
characters to actually be transmitted. If there is insufficient
room in the output buffer, it waits until there is unless output
is currently disabled via flow control. In this case, rs_sndbyt
returns the number of characters that were written to the
If transmit interrupts are disabled by defining RS_POLLED_XMIT,
the output buffer is not used and the function does not return
until the entire string has been sent or output becomes disabled
via flow control.

> rs_getbyt

> Purpose:
Gets a single byte.

> Prototype:
int rs_getbyt(void);

> Arguments:

> Return Value:
Returns the next received byte (converted to int) available. If
no characters are currently waiting to be read from the input
buffer, returns -1.

> Examples:


if((ch = rs_getbyt()) < 0)
printf("No received character\n");

> Notes:
rs_getbyt does not wait for incoming characters - it returns -1
immediately if no received characters are available.

> rs_getstr

> Purpose:
Gets a string of bytes.

> Prototype:
int rs_getstr(int rs_getcnt, char *rs_getbuf);

> Arguments:
rs_getcnt is the number of characters to be read from the input
buffer. If rs_getcnt is 0, characters are read from the input
buffer until a nul character is encountered, which is included
in rs_getbuf. rs_getbuf is a character pointer to the string
which the received characters will be written in to. rs_getbuf
must be large enough to hold the entire string plus a
terminating nul character. rs_getstr can be used to get a
string up to the input buffer size.

> Return Value:
Returns -1 if no port is open or the number of characters read
from the input buffer and written to the target string.

> Examples:

if(rs_getstr(8,msg) > 8)
printf("No message available\n");

> Notes:
If the requested number of characters are not available,
rs_getstr does not wait for them to become available but instead
copies as many as are available and returns the number copied.

> rs_inrcvd

> Purpose:
Returns number of bytes which been received.

> Prototype:
unsigned rs_inrcvd(void);

> Arguments:

> Return Value:
Returns the number of characters which have been received and
are ready to be read from the input buffer by rs_getbyt or

> Examples:

in_cnt = rs_inrcvd();


> Notes:
rs_inrcvd will never return a value greater than the input
buffer size specified when rs_initport was called, even if an
input buffer overflow has occurred. Use rs_error to determine
if there has been an overflow.

> rs_outfre

> Purpose:
Returns amount of free space remaining in output buffer. If
RS_POLLED_XMIT is defined, this function will not return a
meaningful value.

> Prototype:
unsigned rs_outfre(void);

> Arguments:

> Return Value:
Unsigned integer indicating current amount of free space in
bytes that are available in the output buffer.

> Examples:

if(rs_outfre() == 0)
printf("Serial port busy - try again later\n");

printf("%u bytes are currently buffered for output\n",
output_buffer_size - rs_outfre());

> rs_error

> Purpose:
Returns code for last error detected.

> Prototype:
int rs_error(void);

> Arguments:

> Return Value:
Returns integer whose bit pattern indicates the last error which
occurred as follows:
bit 0 (RS_RBER): 1 = receive buffer overflow
bit 1 (RS_ROER): 1 = receive data overrun (UART received data
faster than it could be read by the interrupt
service routine)
bit 2 (RS_PERR): 1 = parity error
bit 3 (RS_FERR): 1 = framing error
bit 4 (RS_BKDT): 1 = break detected
Returns 0 if no error has been detected since last call to
rs_error or since port was opened.

> Examples:

printf("Serial port error\n");

if(rs_error() & RS_PERR)
printf("Parity error!\n");

> Notes:
A call to rs_error returns a code for any errors which have
occurred and clears the error flags. Subsequent calls will not
indicate the same error unless it has reoccurred.

Most UARTs will indicate a framing error when a break is

> rs_modctrl

> Purpose:
Controls or returns status of modem control lines.

> Prototype:
int rs_modctrl(int rs_cmd,...);

> Arguments:
rs_cmd = 2 (RS_GETMCR): Return status of modem control output
rs_cmd = 1 (RS_WRTMCR): Change status of modem control output
lines. 2 additional arguments are required with this
command. The first is an integer with a bit pattern
indicating which line(s) to control:
1 (RS_MCRDTR): Data Terminal Ready line.
2 (RS_MCRRTS): Ready To Send line.
The second is an integer determining whether the
line(s) should be turned on or off:
0 (RS_LINOFF): Turn line off.
1 (RS_LINON): Turn line on.
rs_cmd = 0 (RS_GETMSR): Return status of modem control input

> Return Value:
If rs_cmd = 0 (RS_GETMSR), an integer with a bit pattern
indicating the requested status is returned:
0x01 (RS_CTSCHG) CTS line changed states
0x02 (RS_DSRCHG) DSR line changed states
0x04 (RS_RICHG) RI line changed states
0x08 (RS_DCDCHG) DCD line changed states
0x10 (RS_CTSSTE) state of CTS line
0x20 (RS_DSRSTE) state of DSR line
0x40 (RS_RICHG) state of RI line
0x80 (RS_DCDCHG) state of DCD line
If rs_cmd = 1 (RS_WRTMCR), zero is returned.
If rs_cmd = 2 (RS_GETMCR), an integer with a bit pattern
indicating the current state of the modem control output lines
is returned:
0x01 (RS_MCRDTR): 1 = DTR on
0x02 (RS_MCRRTS): 1 = RTS on
If an operation fails (no port open), -1 is returned.

> Examples:

if(! (rs_modctrl(RS_GETMSR) & RS_MSRDSR))
printf("Remote equipment is not ready\n");

/* set Data Terminal Ready output line on */

if(rs_modctrl(RS_GETMCR) & RS_MCRRTS)
printf("Ready To Send output line is currently off\n");

> Notes:
When using rs_modctrl to return the status of the modem status
input lines (rs_cmd = RS_GETMSR), the lower 4 bits of the value
returned indicate that the corresponding modem status lines have
changed states since the last time the status was read.
The next time the status is read, these bits will be cleared
unless a line has again changed states.

When using rs_modctrl to set modem control output lines, more
than 1 line can be turned on or off with the same call by or'ing
the bit pattern for the desired lines (i.e.

> rs_break

> Purpose:
Sends break to remote equipment.

> Prototype:
int rs_break(void);

> Return Value:
Returns 0 on success, -1 if no port open.

> Examples:


> Notes:
An RS232 line is normally in the "marking" state when idle.
rs_break places the line in a spacing state for approx. 1
character period. It is used by some communications equipment
to signal some event.

If a buffer transmit is currently in progress, rs_break will
wait until the buffer is empty before sending the break unless
output is currently disabled via flow control. In this case,
rs_break will send the break immediately.

> rs_clrout

> Purpose:
Clears output buffer (and the UART output FIFO if used). If
RS_POLLED_XMIT is defined, clears the output FIFO only.

> Prototype:
void rs_clrout(void);

> Return Value:

> Examples:

if(rs_error() & RS_BKDT) /* break detected */
rs_clrout(); /* stop transmission, clear output */

> Notes:
rs_clrout will immediately stop transmission and clear the
output buffer. It has no effect if a port is not open.

> rs_clrin

> Purpose:
Clears input buffer.

> Prototype:
void rs_clrin(void);

> Return Value:

> Examples:

if(rs_error()){ /* if an error occurred */
rs_clrin(); /* clear input buffer */
rs_sndstr(0,"resend last message");

> Notes:
rs_clrin will immediately clear the input buffer of any
characters not yet read. If a UART FIFO is in use, it will also
be cleared.

> rs_keyhit

> Purpose:
Determines if a key has been pressed.

> Prototype:
int rs_keyhit(void);

> Return Value:
Returns 0 if no keys have been pressed, 1 if one or more keys
have been pressed but not yet read.

> Examples:


> Notes:
When constant keyboard monitoring is required, rs_keyhit should
be used in place of library functions like kbhit(). rs_keyhit
reads the BIOS keyboard data area directly to determine if any
keys have been pressed. Library functions like kbhit() call a
BIOS interrupt routine to accomplish the same thing. This can
cause loss of incoming serial data when using high transmission
speeds. A port need not be open to use this function.

> rs_timer

> Purpose:
Times events or operations.

> Prototype:
unsigned rs_timer(int rs_cmd);

> Arguments:
One integer argument determines the action which will be taken:
0 (RS_CLRTIM): The current timer value is returned and the
timer is set to 0.
1 (RS_GETTIM): The current timer value is returned and timing

> Return Value:
rs_timer returns an unsigned integer indicating the number of
"ticks" (18.2 per second) which have elapsed.

> Examples:

while(! rs_inrcvd()){
if(rs_timer(RS_GETTIM) > 18){ /* wait approx. 1 second */
printf("Message response timed out\n");

> Notes:
rs_timer reads the BIOS time data area directly and avoids use
of BIOS interrupt calls to do this. It should be used instead
of library routines such as biostime() to avoid loss of incoming
serial data when performing high speed communication. The value
returned may be in error by as much as 1 "tick" so should be
considered a fairly low resolution timer. A port need not be
open to use this function.

> rs_setflow

> Purpose:
Establishes or returns status of flow control.

> Prototype:
int rs_setflow(int rs_cmd,...);

> Arguments:
rs_cmd can be one of the following:
0 (RS_FLWOFF) - Disables flow control.
1 (RS_FLWHDW) - Enables hardware flow control. Requires an
additional integer argument to define hardware
line(s) to use for flow control:
1 (RS_FLWCTS) - Use Clear To Send
2 (RS_FLWDSR) - Use Data Set Ready
4 (RS_FLWRI) - Use Ring Indicator
8 (RS_FLWDCD) - Use Data Carrier Detect
2 (RS_FLWXON) - Enables XON/XOFF flow control. Requires two
additional parameters, the first of which
defines the character to use for XON, the
second defining the character to use as XOFF.
The following two pre-defined constants are
the standard XON and XOFF characters:
0x11 (RS_XON)
0x13 (RS_XOFF)
3 (RS_FLWSTAT) - Returns the status of flow control. 1
indicates that output is currently disabled
by flow control. 0 indicates output is
currently enabled.
4 (RS_FLWINS) - Inserts a control character in the output
stream. The control character to be sent is
defined by one additional argument.

> Return Value:
Returns -1 if no port open. Except when rs_cmd is 3
(RS_FLWSTAT), returns 0 on success. When rs_cmd is RS_FLWSTAT,
value returned is status of flow control.

> Examples:

/* set flow control to use Data Set Ready hardware line */

/* set flow control to XON/XOFF using standard XON and XOFF
characters */

printf("Output currently disabled via flow control\n");

/* send XOFF character to remote */

/* disable flow control */

> Notes:
Enabling flow control, whether with hardware line monitoring or
with XON/XOFF characters, will cause output to stop immediately
when the defined condition is met (the selected hardware line
goes to logic 0 or the character used as XOFF is received). Any
characters buffered for output will remain buffered and
transmission will resume when the hardware line being used for
flow control goes to logic 1 or the character used for XON is
received. Flow control effects output only. Characters can
continue to be received or written to the output buffer up to
it's capacity when output is disabled via flow control. Only
one type of flow control may be used at once.

If the remote equipment is using XON/XOFF type flow control,
rs_setflow can be used to control remote output with the
RS_FLWINS command. The control character will be sent
immediately, even if characters are already being transmitted
via the output buffer. The buffer will continue to be sent
after the control character has been transmitted.

> rs_close

> Purpose:
Closes" port prepared by rs_initport.

> Prototype:
void rs_close(void);

> Return Value:

> Notes:
This function should always be used when an application has
"opened" a port with rs_initport. rs_close disables interrupts
from the port and restores the previous interrupt vector.
Failure to call rs_close before exiting from the application can
very likely cause undefined behavior. The function can be
registered with the library function atexit() to insure that it
gets called. It is probably also a good idea to install a
ctrl-break handler which calls rs_close to prevent exiting the
application without closing the port:

int c_break(void)

/* somewhere in application */
ctrlbrk(c_break); /* library function ctrlbrk registers
cleanup function */

If a buffer transmit is currently in progress, rs_close waits
for it to finish before closing the port unless output is
currently disabled via flow control.

> General Notes:

> Baud Rates:
Although RS232.C supports speeds up to 115.2K baud, the higher
speeds may not work on some machines due to a variety of
factors. Foremost is the speed of the CPU itself - slower
machines may not be capable of sustained input at high speeds.
Other factors may effect the maximum usable baud rate also. For
instance, even though a machine's CPU is fast enough to support
high speeds, other software running on the machine may prevent
it. (One example is a 386 CPU running an extended memory
manager which places the CPU in protected mode. This can cause
interrupt service routines to be slower.) The BIOS in some
machines may have routines which are too slow, such as the timer
interrupt service routine - this can effect time critical
routines like the serial interrupt service routine in RS232.C.
Only experimentation can determine what the highest usable speed
is on any given machine and configuration.

> Interrupts:
Because machines equipped with COM ports 3 and 4 use hardware
interrupt lines shared with ports 1 and 2, a problem can arise
with some hardware. For example, an internal modem is installed
as COM1 and you are attempting to use COM3 for some other
purpose. Because both ports use interrupt request line IRQ4, if
the modem board is "holding" IRQ4 inactive, COM3 will never be
able to signal an interrupt and thus will appear dead. Most
serial adapter boards do not behave this way and will "float"
the interrupt request line when not signaling an interrupt.
Also, the problem can only occur if software accessing the
problem board leaves interrupts enabled at the board.
(Specifically, the signal OUT2 is left active with modem control
register bit 3.)

If your application installs any of it's own interrupt routines,
they must re-enable interrupts as soon as possible after entry
to prevent loss of incoming serial characters.

RS232.C re-prioritizes hardware interrupts to give serial
interrupts the highest priority. While this will have no
noticeable effect with most applications, it should be taken
into account in those applications which make use of hardware
interrupts from other sources.

> Polled vs. interrupt driven output:
The default method of character transmission by RS232.C uses
interrupts. Each time a character is written a character is
written to the UART's transmit holding register, an interrupt is
generated by the UART when the transmit holding register becomes
available for the next character to be sent. It may be
desirable under certain circumstances to disable this feature
and use "polled transmission" instead. This requires that
software constantly poll the UART and write the next character
when the transmit holding register becomes free. The method
used is determined at compile time. If the macro RS_POLLED_XMIT
is defined, the polling method will be used. The following line
placed at the beginning of the application's source code will
accomplish this:


The following describes some advantages and disadvantages to
both methods:

Interrupt driven output:
Advantages: This is the more efficient method of output. No
CPU time is wasted polling the UART. Characters may be queued
for output and the application may go on processing without
waiting for the characters to actually be sent.

Disadvantages: Some additional overhead is incurred in the
interrupt service routine. At high transmission speeds this
may cause problems on slower machines or under certain
circumstances. Interrupt driven output may not work properly
with some non-standard hardware.

Polled output:
Advantages: This method makes the interrupt service routine
slightly "leaner" and simplifies some of the code elsewhere
making the overall code size slightly smaller. This method
may prove to be more reliable on some hardware.

Disadvantages: While the application is sending characters,
it can do nothing else and no output buffer is used. This
method is less efficient for throughput.

> Flow control and handshaking:
Flow control is provided for only in respect to stopping and
starting output as requested by remote equipment. To utilize
flow control on remote equipment, the application must monitor
the conditions requiring flow control (i.e. input buffer free
space) and take the appropriate action when needed (like sending
XOFF or dropping a hardware line).

Handshaking is something that is completely arbitrary and the
only requirement is that the local and remote equipment must
agree on the protocol. RS232.C provides a function for
determining the status of and controlling hardware handshake
lines but it is up to the application to determine how it should
be used.

> Compiling:
If RS232.C can be simply included (whether by "pasting" it
directly into an applications source code or with a preprocessor
"#include" directive) no special compiler considerations are
needed. If desired, RS232.C can be split into separate header
and code files to follow more conventional 'C' conventions.
This is accomplished by splitting the file where indicated.
Programs using RS232.C can be compiled and linked from the IDE
if desired.

Borland's newer compilers have many optimization options and, so
far, none have caused problems with RS232.C. One necessity is a
must when using Turbo C 2.0 or earlier compilers: Stack checking
must be disabled. With stack checking enabled in the older
compilers, stack checking code is generated even in interrupt

Any of the memory models can be used with RS232.C - the only
drawback is the extra function call overhead and/or memory
access overhead incurred with the large models. This may
prevent an application from being able to use the higher baud
rates on some machines.

> Other Compilers:

RS232.C will compile and run with other DOS based compilers with
minor changes. The following lists types/functions/macros which
may need attention under compilers other than those produced by

function type interrupt - must take care of saving and
restoring registers and provide IRET instruction

unsigned char inportb(int portid) /* reads a byte from
a hardware port */

void outportb(int portid,unsigned char value) /* writes a
/* byte to a port */

void far *MK_FP(unsigned seg, unsigned ofs) /* takes integer
segment and offset
arguments and returns
far pointer */

> 16550 FIFO mode:
The 8250A/B and 16450 UARTs found in most machines contain only
a one byte buffer for input and output. This means that for
every byte which is received, an interrupt is generated which
invokes a routine to retrieve the incoming byte and store it.
This must take place before the next incoming byte has been
fully received to avoid loss of data. The same is true of
outgoing data - after each byte being transmitted has been
sent, an interrupt is generated to invoke a routine to write the
next byte to the transmit register. In some situations, this is
simply too much overhead or the interrupt service routines can
not be allowed to react quickly enough to keep up with the data.
The more sophisticated 16550 series of UARTs overcome this with
the inclusion of 16 byte FIFOs (first-in-first-out-registers)
for both input and output. Software must be able to recognize
and enable this feature when it's available, and software
determines how it is used. Code in RS232.C checks for the
presence of FIFO capable UARTs and automatically employs them.
Outgoing data is written to the UART at 16 bytes per interrupt
and incoming data signals generates a receive interrupt after 4
bytes have accumulated in the FIFO - though up to 16 bytes can
be received before the interrupt service routine is able to
respond, without losing any data. These defaults can be changed
by altering the corresponding constants in the function
rs_initport (see comments in RS232.C).

> Using More Than 1 COM Port Simultaneously:
While RS232.C provides no direct support for simultaneous serial
port access, it is easy enough to accomplish. The solution is
to include 2 copies of RS232.C in your source code. Because all
variable and function names in RS232.C begin with 'rs_', this
makes it easy. To allow an application to access 2 ports at
once you can do the following:

Create 2 copies of RS232.C renaming them to RS232A.C and

Using Borland's built in editor (or any editor capable of
search and replace), edit RS232A.C performing a global
search and replace of the string "rs_" with "rs1_". Do
the same for "RS_" replacing it with "RS1_".

Do the same thing with RS232B.C, replacing "rs_" with
"rs2_" and "RS_" with "RS2_".

Include both modified copies in the source code for your
application. This provides 2 complete sets of functions
(the function names now start with "rs1_" or "rs2_",
depending on which port is being accessed) allowing access
to 2 different ports and simultaneous serial port I/O.
While not the most elegant of solutions, it is workable
and RS232.C is small enough so as not to make inclusion of
2 copies a problem. (RS232.C adds about 3400 bytes to an
executable file.)

Only certain ports can be used simultaneously. Because COM1
uses the same hardware interrupt as COM3 and COM2 uses the same
interrupt as COM4, you will most likely not be able to use COM1
with COM3 or COM2 with COM4. The problem arises due to the
design of the hardware - if a UART has its OUT2 hardware line at
logic 1 (necessary to enable interrupts), another UART sharing
the same interrupt will not be able to signal an interrupt.
This is true of any serial communications software which uses
interrupts. A serial mouse driver using COM1 preventing serial
I/O on COM3 is one example of how this problem may arise even
when using only one port in an application. One way to get
around this problem (other than opening and closing the ports as
needed using rs_close) is to turn off OUT2 on the UART which is
NOT currently being utilized. OUT2 is controlled by bit 3 of
the modem control register at offset 4 from the UART's base I/O
address. The following is an example which would allow
interrupts to occur on COM3 and reenable interrupts on COM1 when

outportb(0x3FC,(inportb(0x3FC) & '\xF7'); /* COM1 OUT2 off */
outportb(0x3EC,(inportb(0x3EC) | '\x08'); /* COM3 OUT2 on */
perform I/O with COM3
. . . .
. . . .
. . . .
outportb(0x3EC,(inportb(0x3EC) & '\xF7'); /* COM3 OUT2 off */
outportb(0x3FC,(inportb(0x3FC) | '\x08'); /* COM1 OUT2 on */
resume I/O with COM1

 December 9, 2017  Add comments

Leave a Reply