DVX_GUI/rs232/README.md

8.4 KiB

RS232 -- Serial Port Library for DJGPP

ISR-driven UART communication library supporting up to 4 simultaneous COM ports with ring buffers and hardware/software flow control.

Ported from the DOS Serial Library 1.4 by Karl Stenerud (MIT License), stripped to DJGPP-only codepaths and restyled.

Features

  • ISR-driven receive and transmit with 2048-byte ring buffers
  • Auto-detected IRQ from BIOS data area
  • UART type detection (8250, 16450, 16550, 16550A)
  • 16550 FIFO detection and configurable trigger threshold
  • XON/XOFF, RTS/CTS, and DTR/DSR flow control
  • DPMI memory locking for ISR safety
  • Speeds from 50 to 115200 bps
  • 5-8 data bits, N/O/E/M/S parity, 1-2 stop bits

API Reference

Types

All functions take a COM port index (int com) as their first argument:

Constant Value Description
RS232_COM1 0 COM1
RS232_COM2 1 COM2
RS232_COM3 2 COM3
RS232_COM4 3 COM4

UART Types

Constant Value Description
RS232_UART_UNKNOWN 0 Unknown or undetected
RS232_UART_8250 1 8250 -- no FIFO, no scratch register
RS232_UART_16450 2 16450 -- scratch register, no FIFO
RS232_UART_16550 3 16550 -- broken FIFO (unusable)
RS232_UART_16550A 4 16550A -- working 16-byte FIFO

Handshaking Modes

Constant Value Description
RS232_HANDSHAKE_NONE 0 No flow control
RS232_HANDSHAKE_XONXOFF 1 Software (XON/XOFF)
RS232_HANDSHAKE_RTSCTS 2 Hardware (RTS/CTS)
RS232_HANDSHAKE_DTRDSR 3 Hardware (DTR/DSR)

Error Codes

Constant Value Description
RS232_SUCCESS 0 Success
RS232_ERR_UNKNOWN -1 Unknown error
RS232_ERR_NOT_OPEN -2 Port not open
RS232_ERR_ALREADY_OPEN -3 Port already open
RS232_ERR_NO_UART -4 No UART detected
RS232_ERR_INVALID_PORT -5 Bad port index
RS232_ERR_INVALID_BASE -6 Bad I/O base address
RS232_ERR_INVALID_IRQ -7 Bad IRQ number
RS232_ERR_INVALID_BPS -8 Unsupported baud rate
RS232_ERR_INVALID_DATA -9 Bad data bits (not 5-8)
RS232_ERR_INVALID_PARITY -10 Bad parity character
RS232_ERR_INVALID_STOP -11 Bad stop bits (not 1-2)
RS232_ERR_INVALID_HANDSHAKE -12 Bad handshaking mode
RS232_ERR_INVALID_FIFO -13 Bad FIFO threshold
RS232_ERR_NULL_PTR -14 NULL pointer argument
RS232_ERR_IRQ_NOT_FOUND -15 Could not detect IRQ
RS232_ERR_LOCK_MEM -16 DPMI memory lock failed

Functions

Open / Close

int rs232Open(int com, int32_t bps, int dataBits, char parity,
              int stopBits, int handshake);

Opens a COM port. Detects the UART base address from the BIOS data area, auto-detects the IRQ, installs the ISR, and configures the port.

  • bps -- baud rate (50, 75, 110, 150, 300, 600, 1200, 1800, 2400, 3800, 4800, 7200, 9600, 19200, 38400, 57600, 115200)
  • dataBits -- 5, 6, 7, or 8
  • parity -- 'N' (none), 'O' (odd), 'E' (even), 'M' (mark), 'S' (space)
  • stopBits -- 1 or 2
  • handshake -- RS232_HANDSHAKE_* constant
int rs232Close(int com);

Closes the port, removes the ISR, and restores the original interrupt vector.

Read / Write

int rs232Read(int com, char *data, int len);

Reads up to len bytes from the receive buffer. Returns the number of bytes actually read (0 if the buffer is empty).

int rs232Write(int com, const char *data, int len);

Blocking write. Sends len bytes, waiting for transmit buffer space as needed. Returns RS232_SUCCESS or an error code.

int rs232WriteBuf(int com, const char *data, int len);

Non-blocking write. Copies as many bytes as will fit into the transmit buffer. Returns the number of bytes actually queued.

Buffer Management

int rs232ClearRxBuffer(int com);
int rs232ClearTxBuffer(int com);

Discard all data in the receive or transmit ring buffer.

Getters

int     rs232GetBase(int com);       // UART I/O base address
int32_t rs232GetBps(int com);        // Current baud rate
int     rs232GetCts(int com);        // CTS line state (0 or 1)
int     rs232GetData(int com);       // Data bits setting
int     rs232GetDsr(int com);        // DSR line state (0 or 1)
int     rs232GetDtr(int com);        // DTR line state (0 or 1)
int     rs232GetHandshake(int com);  // Handshaking mode
int     rs232GetIrq(int com);        // IRQ number
int     rs232GetLsr(int com);        // Line status register
int     rs232GetMcr(int com);        // Modem control register
int     rs232GetMsr(int com);        // Modem status register
char    rs232GetParity(int com);     // Parity setting ('N','O','E','M','S')
int     rs232GetRts(int com);        // RTS line state (0 or 1)
int     rs232GetRxBuffered(int com); // Bytes in receive buffer
int     rs232GetStop(int com);       // Stop bits setting
int     rs232GetTxBuffered(int com); // Bytes in transmit buffer
int     rs232GetUartType(int com);  // UART type (RS232_UART_* constant)

rs232GetUartType probes the UART hardware to identify the chip:

  1. Scratch register test -- writes two values to register 7 and reads them back. The 8250 lacks this register, so readback fails.
  2. FIFO test -- enables the FIFO via the FCR, then reads IIR bits 7:6. 0b11 = 16550A (working FIFO), 0b10 = 16550 (broken FIFO), 0b00 = 16450 (no FIFO). The original FCR value is restored after probing.

Setters

int rs232Set(int com, int32_t bps, int dataBits, char parity,
             int stopBits, int handshake);

Reconfigure all port parameters at once (port must be open).

int rs232SetBase(int com, int base);           // Override I/O base address
int rs232SetBps(int com, int32_t bps);         // Change baud rate
int rs232SetData(int com, int dataBits);       // Change data bits
int rs232SetDtr(int com, bool dtr);            // Assert/deassert DTR
int rs232SetFifoThreshold(int com, int thr);   // FIFO trigger level (1,4,8,14)
int rs232SetHandshake(int com, int handshake); // Change flow control mode
int rs232SetIrq(int com, int irq);             // Override IRQ (before Open)
int rs232SetMcr(int com, int mcr);             // Write modem control register
int rs232SetParity(int com, char parity);      // Change parity
int rs232SetRts(int com, bool rts);            // Assert/deassert RTS
int rs232SetStop(int com, int stopBits);       // Change stop bits

Example

#include "rs232.h"

int main(void) {
    // Open COM1 at 115200 8N1, no flow control
    int rc = rs232Open(RS232_COM1, 115200, 8, 'N', 1, RS232_HANDSHAKE_NONE);
    if (rc != RS232_SUCCESS) {
        return 1;
    }

    // Identify UART chip
    int uartType = rs232GetUartType(RS232_COM1);
    // uartType == RS232_UART_16550A on most systems

    // Blocking send
    rs232Write(RS232_COM1, "Hello\r\n", 7);

    // Non-blocking receive
    char buf[128];
    int  n;
    while ((n = rs232Read(RS232_COM1, buf, sizeof(buf))) > 0) {
        // process buf[0..n-1]
    }

    rs232Close(RS232_COM1);
    return 0;
}

Implementation Notes

  • The ISR handles all four COM ports from a single shared handler. On entry it disables UART interrupts for all open ports, then re-enables CPU interrupts so higher-priority devices are serviced.
  • Ring buffers use power-of-2 sizes (2048 bytes) with bitmask indexing for zero-branch wraparound.
  • Flow control watermarks are at 80% (assert) and 20% (deassert) of buffer capacity.
  • DPMI __dpmi_lock_linear_region is used to pin the ISR, ring buffers, and port state in physical memory.

Building

make        # builds ../lib/librs232.a
make clean  # removes objects and library

Target: DJGPP cross-compiler, 486+ CPU.