[comp.os.msdos.programmer] Serial Code

resnicks@netcom.COM (Steve Resnick) (06/28/91)

Due to the number of requests for serial code, I am posting this driver
I wrote. It's for Turbo/Borland C.

Happy Hacking ....

Steve

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 06/05/1991 15:24 UTC by resnicks@netcom
# Source directory /u9/resnicks/sources/pc/asynch
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#  11768 -rw-r--r-- asynch.c
#   3229 -rw-r--r-- asynch.h
#   2692 -rw-r--r-- circleq.c
#    336 -rw-r--r-- circleq.h
#    838 -rw-r--r-- main.c
#    270 -rw-r--r-- makefile
#
# ============= asynch.c ==============
if test -f 'asynch.c' -a X"$1" != X"-c"; then
	echo 'x - skipping asynch.c (File already exists)'
else
echo 'x - extracting asynch.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'asynch.c' &&
/*
X * ASYCNCH.C A simple, low-level, interrupt driven comm driver for
X * Turbo/Borland C. No assembly required!
X *
X * (C) Copyright 1991 Steve Resnick, Asylum Software. All Rights Reserved.
X *
X * Steve Resnick - 530 Lawrence Expressway, Box 374, Sunnyvale, Ca 94086
*/
#include <stdio.h>
#include <dos.h>
#include "asynch.h"
/*
X * This table is used to translate a BPS rate to a baud rate divisor for the
X * UART
*/
struct _baud
{
X	word	Rate, Divisor;
} BaudTable[] =
{
X	110,1040,150,768,300,384,600,192,1200,96,2400,48,4800,24,9600,12,
X	19200,6,		/* I have no idea if this works */
} ;
#if DEBUG
byte PICVals[2][2];		/* Mirror PIC values to these arrays */
byte DataTable[2][20];		/* Mirror UART values to these arrays */
byte portinb(word port);	/* Use our own port I/O functions */
void portoutb(word port, byte data);
#else
#define portinb(p) inportb(p)
#define portoutb(p,b) outportb(p,b)
#endif
uart cports[4] = {COM1_DEFAULTS,COM2_DEFAULTS};
X
/*
X * SetPortParms sets up the UART line parameters and baud rate.
X * If Base, IRQ, and BreakLength are specified as 0, defaults are used.
X * (See ASYNCH.H)
X * This may only be called for an open port.
*/
int SetPortParms(int PortNo, int Baud, int Parity, int Data, int Stop,
X		 int Base, int IRQ, int BreakLength)
{
X	int i, b;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X		
X	if (Base)
X		cports[PortNo].addr = Base;
X	if (IRQ)
X		cports[PortNo].irqline = IRQ;
X	if (BreakLength)
X		cports[PortNo].break_length = BreakLength;
X
X	cports[PortNo].parms = 0;
X	cports[PortNo].parms |= Data;
X	cports[PortNo].parms |= Stop;
X	cports[PortNo].parms |= Parity;
X	if (Baud != 0)
X	{
X		for (i = 0, b = -1; i < dim(BaudTable); i++)
X			if (BaudTable[i].Rate == Baud)
X				b = i;
X		if (b == -1)
X			return EINVBAUDRATE;
X
X		cports[PortNo].baud = BaudTable[b].Divisor;
X		InitBaud(PortNo);
X	}
X	else if (cports[PortNo].baud == 0)
X		return EINVBAUDRATE;
X	portoutb(cports[PortNo].addr+LCR_REG,cports[PortNo].parms);
X	return 0;
}
/*
X * copen opens a comm port spcified by PortNo. Input and Output buffers are
X * allocated according to the BufSiz parameter. 
X * If Base, IRQ, and BreakLength are specified as 0, defaults are used.
X * (See ASYNCH.H)
X *
X * When the port is opened, the following takes place:
X * Buffer memory is allocated
X * CommPort options are set
X * The interrupt vector is set based on IRQ+8
X * The 8259 is enabled to recieve interrupts.
X * The 8250 IE mask is set
X * The 8250 OUT2 is asserted, enabling the interrupt line from the UART to the
X * PIC.
*/
int copen(int PortNo, int BufSiz, int Baud, int Parity, int Data, int Stop,
X		  int Base, int IRQ, int BreakLength)
{
X	uart * Cp;
X	int Rc;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED)
X		return EPORTALREADYOPEN;
X	Cp = &cports[PortNo];
X	if ((Cp->InBuffer = Qalloc(BufSiz)) == NULL)
X		return EMEMALLOC;
X	if ((Cp->OutBuffer = Qalloc(BufSiz)) == NULL)
X	{
X		Qfree(Cp->InBuffer);
X		return EMEMALLOC;
X	}
X	Cp->status |= PS_ENABLED;
X	Rc = SetPortParms(PortNo,Baud,Parity,Data,Stop,Base,IRQ,BreakLength);
X	if (Rc)
X	{
X		Qfree(Cp->InBuffer);
X		Qfree(Cp->OutBuffer);
X		Cp->status = 0;
X		return Rc;
X	}
X	SetIntVector(PortNo);
X	disable();
X	SetPIC(PortNo,1);
X	SetUARTIe(PortNo);
X	enable();
X	ControlDTR(PortNo,ON);
X	return 0;
}
/*
X * cclose closes a serial port by disabling interrupts on the UART, then on
X * the PIC and finally, returns the original vector to the IVT.
*/
int cclose(int PortNo)
{
X	uart * Cp;
X	int Rc;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	Cp = &cports[PortNo];
X	disable();
X	ClrIntVector(PortNo);
X	SetPIC(PortNo,0);
X	enable();
X	Cp->status = 0;
X	Qfree(Cp->InBuffer);
X	Qfree(Cp->OutBuffer);
X	portoutb(Cp->addr + IE_REG,0);
X	portoutb(Cp->addr + MCR_REG,1);
X	return 0;
}
/*
X * InitBaud sets the baudrate on the UART by setting the divisor latch access
X * bit (DLAB) then writing the divisor's low byte,then the divisors high byte.
X * For baudrates >= 600 BPS, the high byte should be 0. (See ASYNCH.H)
*/
int InitBaud(int PortNo)
{
X	uart * Cp;
X	byte bdata;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	Cp = &cports[PortNo];
X	bdata = portinb(Cp->addr + LCR_REG);
X	portoutb(Cp->addr + LCR_REG,bdata | DLAB);
X	portoutb(Cp->addr + DIV_LOW,LOBYTE(Cp->baud));
X	portoutb(Cp->addr + DIV_HI, HIBYTE(Cp->baud));
X	portoutb(Cp->addr + LCR_REG,Cp->parms);
X	return 0;
}
/*
X * SetPIC sets or clears the interrupt enable mask on the PIC for a
X * particular interrupt. This function will work for both the master
X * and the slave 8259A's in an AT.
*/
SetPIC(int PortNo, int State)
{
static	int States[] = {-1,-1,-1,-1};
X	int PICAddr = PIC;
X	byte mask;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if(State == 0 && States[PortNo] == -1)
X		return -1;
X	if (cports[PortNo].irqline > 7)
X	{
X		mask = ( 1 << (cports[PortNo].irqline - 8));
X		PICAddr = 0xA0;
X	}
X	else
X		mask = (1 << cports[PortNo].irqline);
X 
X	if (State == 0)
X	{
X		States[PortNo] = portinb(PIC+1);
X		portoutb(PICAddr+1,(byte)States[PortNo]|mask);
X		return 0;
X	}
X	mask = ~mask;	
X	States[PortNo] = portinb(PICAddr+1);
X	portoutb(PICAddr+1,States[PortNo] & mask);
X	portinb(PICAddr+1);
X	return 0;
}
/*
X * EOI Sends a non-specific end of interrupt to the 8259A specific to the
X * IRQ line of a com port
*/
int EOI(int PortNo)
{
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	if (cports[PortNo].irqline > 7)
X		portoutb(0xA0,0x20);
X	else
X		portoutb(0x20,0x20);
X	return 0;
}
/*
X * This is the interrupt handler for ALL comm ports. Each ISR for a comm
X * port calls this routine passing the index into the UART definition array
X * so that values may be extracted for things like UART address, etc.
X *
X * This is a re-entrant function, although interrupts are disabled to ensure
X * that interrupts are handled one at a time for each UART. This may be
X * over-kill and may need to be changed in the future.
*/
void IsrHandler(int PortNo)
{
X	uart * Cp;
X	byte Idv, IIDvalue=0;
X	disable();
X	Cp = &cports[PortNo];
X	IIDvalue = portinb(Cp->addr+ IID_REG);
X	if ((IIDvalue & IID_PENDING) == 0)
X	{
X		IIDvalue &= IID_MASK ;
X		Idv = IIDvalue >> 1;
X		if (Idv & IID_DATA)
X			QinsertChar(Cp->InBuffer,portinb(Cp->addr));
X
X		if (Idv & IID_TEMPTY)
X			DrainOutQueue(PortNo);
X
X		if (Idv & IID_LSTAT)
X			Cp->lstat = portinb(Cp->addr+ LSR_REG);
X
X		if (Idv & IID_MSTAT)
X			Cp->mstat = portinb(Cp->addr+ MSR_REG);
X	
X		portinb(Cp->addr+5);
X		portinb(Cp->addr+6);
X	} 
X	enable();
X	SetUartOUT2(PortNo);
X	EOI(PortNo);
}
/*
X * Dummy ISR's
X * Each comm port needs to have it's own ISR, but we need only one real
X * handler. Since an ISR shared my multiple interrupts cannot determine
X * it's source, we set up these dummy ISR's to call IsrHandler specifying
X * the port which needs service.
*/
void interrupt com1isr()
{
X	IsrHandler(0);
}
void interrupt com2isr()
{
X	IsrHandler(1);
}
void interrupt com3isr()
{
X	IsrHandler(2);
}
void interrupt com4isr()
{
X	IsrHandler(3);
}
/*
X * SetIntVector sets the appropriate interrupt vector (correctly for both
X * (master and slaved IRQ's) based on the IRQ. The original vector is saved
X * in the UART structure so that it may be reset upon termination.
*/
SetIntVector(int PortNo)
{
X	int NewInt;
static	void interrupt (*isrs[])() = {com1isr,com2isr,com3isr,com4isr};
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	cports[PortNo].oldisr = getvect(IRQINT(cports[PortNo].irqline));
X	NewInt = IRQINT(cports[PortNo].irqline);
X	setvect(NewInt,isrs[PortNo]);
X	return 0;
}
/*
X * ClrIntVector revectors interrupts back to the original vector before we
X * loaded up.
*/
ClrIntVector(int PortNo)
{
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	setvect(IRQINT(cports[PortNo].irqline),cports[PortNo].oldisr);
X	return 0;
}
/*
X * SetUARTIe sets the interrupt enable mask on the UART.
X * SetUartOUT2 is then called to unmask interrupts from the UART to the PIC
*/
SetUARTIe(int PortNo)
{
X	int i;
X	word IntFlags = IIV_LSTAT | IIV_RDATA | IIV_MSTAT | IIV_XMITE;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	for (i = 5; i < 8; i++)
X		inportb(cports[PortNo].addr+i);
X
X	portoutb(cports[PortNo].addr + IE_REG, IntFlags);
X	SetUartOUT2(PortNo);
X	return 0;
}
/*
X * SetUARTOut2 set's the OUT2 line on the UART high, allowing interrupts
X * to pass from the UART to the PIC (thanks, IBM)
*/
SetUartOUT2(int PortNo)
{
X	byte IoData;	
X	IoData = portinb(cports[PortNo].addr + MCR_REG);
X	portoutb(cports[PortNo].addr + MCR_REG,IoData | UARTIEMASK);
X	return 0;
}
/*
X * DrainOutQueue reads characters from the output queue to the UART, as
X * long as the UART is ready for data. If it is not ready, we return
X * immediately.
*/
DrainOutQueue(int PortNo)
{
X	byte IoData;
X	char Ch;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X
X	while(1)
X	{
X		IoData = portinb(cports[PortNo].addr + LSR_REG);
X		if (IoData & 0x20 || IoData & 0x40)
X		{
X			Ch = QreadChar(cports[PortNo].OutBuffer);
X			if (Ch == -1)
X				break ;
X			portoutb(cports[PortNo].addr,Ch);
X			continue ;
X		}
X		break ;
X	}
X	return 0;
}
/*****************************************************************************
X ****************************** User Functions ******************************/
/*
X * cgetc reads a character from the input queue and returns it, or -1 if no
X * character is available.
*/ 
cgetc(int PortNo)
{
X	if (PortNo > 3 || PortNo < 0)
X		return -10 - EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return -10 - EPORTNOTOPEN;
X	return QreadChar(cports[PortNo].InBuffer);
}
/*
X * cputc writes a character to the output queue
*/
cputc(int PortNo, char Ch)
{
X	if (PortNo > 3 || PortNo < 0)
X		return -10 - EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return -10 - EPORTNOTOPEN;
X
X	QinsertChar(cports[PortNo].OutBuffer,Ch);
X	DrainOutQueue(PortNo);
X	return 0;
}
/*
X * SendBreak sets a break condition on the serial line and holds it
X * for break_lenght miliseconds
*/
int SendBreak(int PortNo)
{
X	byte IoData;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	IoData = portinb(cports[PortNo].addr + LCR_REG);
X	portoutb(cports[PortNo].addr + LCR_REG,IoData | 0x40);
X	delay(cports[PortNo].break_length);
X	portoutb(cports[PortNo].addr + LCR_REG,IoData);
X	return 0;	
}
/*
X * ControlDTR raises or lowers DTR on a specific port
*/
int ControlDTR(int PortNo, int State)
{
X	byte IoData;
X	if (PortNo > 3 || PortNo < 0)
X		return EINVPORT;
X	if (cports[PortNo].status & PS_ENABLED == 0)
X		return EPORTNOTOPEN;
X	IoData = portinb(cports[PortNo].addr + MCR_REG);
X	if (State == ON)
X		portoutb(cports[PortNo].addr + MCR_REG,(byte)(IoData | 1));
X	else 
X		portoutb(cports[PortNo].addr + MCR_REG,(byte)(IoData & ~1)&0xFF);
X	return 0;
}
/******************************************************************************
****************************** Debugging Functions ***************************/
#if DEBUG
void portoutb(word port, byte data)
{
X	int Idx = port - 0x3f8;
X	if (Idx > -1 && Idx < 20)
X		DataTable[0][Idx] = data;
X	if (port >0x1F && port < 0x22)
X	{
X		Idx = port - 0x20;
X		PICVals[0][Idx] = data;
X	}
X	outportb(port,data);
}
byte portinb(word port)
{
X	int Idx = port - 0x3F8;
X	byte Data;
X	Data = inportb(port);
X	if (port >0x1F && port < 0x22)
X	{
X		Idx = port - 0x20;
X		PICVals[1][Idx] = Data;
X	}
X	else if (Idx > -1 && Idx < 20)
X		DataTable[1][Idx] = Data;
X	return Data;
}
#endif
X
X	
SHAR_EOF
chmod 0644 asynch.c ||
echo 'restore of asynch.c failed'
Wc_c="`wc -c < 'asynch.c'`"
test 11768 -eq "$Wc_c" ||
	echo 'asynch.c: original size 11768, current size' "$Wc_c"
fi
# ============= asynch.h ==============
if test -f 'asynch.h' -a X"$1" != X"-c"; then
	echo 'x - skipping asynch.h (File already exists)'
else
echo 'x - extracting asynch.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'asynch.h' &&
#if !defined(ASYNCH_INCLUDED)	/* Prevent multiple includes */
#define ASYNCH_INCLUDED
#include "circleq.h"
typedef	unsigned char	byte;
typedef unsigned short  word;
typedef unsigned long	dword;
typedef struct _lcr
{
X	word	databits	: 2;
X	word	stopbits	: 1;
X	word    parity		: 2;
X	word	stuckparity	: 1;
X	word	breakenable 	: 1;
X	word	dlab 		: 1;
} lcr;
typedef struct _uart
{
X	word 	addr;
X	byte 	irqline;
X	word 	baud;
X	byte 	lstat;
X
X	byte 	mstat;
X	byte 	status;
X	byte	break_length;
X	byte	parms;
X	CircleQ *InBuffer;
X	CircleQ *OutBuffer;
X	void interrupt (*oldisr)();
} uart;
#define LineStatus(n) ((n >= 0 && n <4)  ? cports[n].lstat : EINVPORT)
#define ModemStatus(n) ((n >= 0 && n <4)  ? cports[n].mstat : EINVPORT)
X 
extern uart cports[4];
#define COM1_DEFAULTS\
X	{0x3F8,4,0,0,0,0,250,\
X	DATA8|STOP1|NONE,\
X	NULL,NULL}
X
#define COM2_DEFAULTS \
X	{0x2F8,3,0,0,0,0,250,\
X	DATA8|STOP1|NONE,\
X	NULL,NULL}
X
#define PIC	0x20			/* 8259A PIC Address */
#define IRQINT(n) ((n < 9) ? n+8 : (n-9)+0x70)
X					/* Make int value from IRQ */
#define IID(n)	(n & 7) 		/* Get Interrupt ID from reg val */
/*
X * Define Interrupt ID values from the interrupt ID register
*/
#define IID_PENDING	0x01
#define IID_LSTAT	0x00			/* Line status change */
#define IID_DATA	0x02			/* Data is available from 8250 */
#define IID_TEMPTY	0x04			/* Transmitter buffer empty */
#define IID_MSTAT	0x06			/* New modem status */
#define IID_MASK	0x07
X
#define IIV_LSTAT	0x01
#define IIV_XMITE	0x02
#define IIV_RDATA	0x04
#define IIV_MSTAT	0x08
/*
X * Define some offsets from the UART base address for various registers
*/
#define XMT_REG		0x00		/* Transmitter holding buffer */
#define RCV_REG		0x00		/* Reciever holding buffer */
#define DIV_LOW		0x00		/* Divisor low byte when DLAB=1 */
#define DIV_HI		0x01		/* Devisor high byte when DLAB=1 */
X
#define IE_REG		0x01		/* Interrupt enable register */
#define IID_REG		0x02		/* Interrupt ID register */
#define LCR_REG		0x03		/* line control register */
#define MCR_REG		0x04		/* Modem control register */
#define LSR_REG		0x05		/* line status register */
#define MSR_REG		0x06		/* modem status register */
X
/*
X *	Stuff for the line control register
*/
#define NONE	0x00			/* No Parity */
#define ODD	0x08			/* Odd Parity */
#define EVEN	0x18			/* Even Parity */
#define STOP1	0x00			/* Need I explain these? */
#define STOP2	0x04
#define DATA5	0x00
#define DATA6	0x01
#define DATA7	0x02
#define DATA8	0x03
#define DLAB	0x80
X
#define UARTIEMASK	0x08		/* Mask used to enable interrupts on
X					   the 8250 OUT2 line */
#define PS_ENABLED	0x01
#define PS_INTERRUPT	0x02		/* Not used */
X
enum errors {EPORTNOTOPEN=1,EPORTALREADYOPEN,EINVBAUDRATE,EINVPORT,EMEMALLOC};
#define COM1	0
#define COM2	1
#define ON	1
#define OFF	0
#define HIBYTE(n) ((n >> 8) & 0xFF)
#define LOBYTE(n) (n & 0xFF)
X
#if !defined(dim)
#define dim(x) (sizeof(x)/sizeof(x[0]))
#endif
int SetPortParms(int PortNo, int Baud, int Parity, int Data, int Stop,
X		  int Base, int IRQ, int BreakLength);
int copen(int PortNo, int BufSiz, int Baud, int Parity, int Data, int Stop,
X		  int Base, int IRQ, int BreakLength);
int cclose(int PortNo);
int InitBaud(int PortNo);
SetPIC(int PortNo, int State);
cgetc(int PortNo);
cputc(int PortNo, char Ch);
X
#endif
X
SHAR_EOF
chmod 0644 asynch.h ||
echo 'restore of asynch.h failed'
Wc_c="`wc -c < 'asynch.h'`"
test 3229 -eq "$Wc_c" ||
	echo 'asynch.h: original size 3229, current size' "$Wc_c"
fi
# ============= circleq.c ==============
if test -f 'circleq.c' -a X"$1" != X"-c"; then
	echo 'x - skipping circleq.c (File already exists)'
else
echo 'x - extracting circleq.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'circleq.c' &&
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "circleq.h"
X
/*
X * Qalloc allocates space for a Queue Control Structure (CircleQ) and
X * a buffer to be contained within.
X * If space is not available for this structure, or it's buffer, NULL
X * is returned, otherwise a pointer to a CirclQ is returned.
*/
CircleQ * Qalloc(int Size)
{
X	CircleQ * Qptr;
X	if ((Qptr = calloc(sizeof(CircleQ),1)) == NULL)
X	{
X		errno = ENOMEM;
X		return NULL;
X	}
X	if ((Qptr->Buffer = calloc(Size,1)) == NULL)
X	{
X		free(Qptr);
X		errno = ENOMEM;
X		return NULL ;
X	}
X	Qptr->Head = Qptr->Tail = Qptr->Buffer;
X	Qptr->Size = Size;
X	return Qptr;
}
/*
X * QinsertChar inserts a character on a queue. The tail pointer is used
X * to indicate where the character shall be placed. Once a character is
X * written, the tail pointer is updated. If the tail pointer overruns the
X * head pointer, the overrun flag is set. Once the overrun flag is set, the
X * head pointer will be updated on all subsequent writes to ensure that
X * the buffer always contains Qptr->Size characters. If Qptr->Tail or
X * Qptr->Head reach the end of the buffer, they are wrapped around to
X * the beginning of the buffer.
*/
void QinsertChar(CircleQ * Qptr, char Ch)
{
X	*Qptr->Tail = Ch;
X	if ((Qptr->Tail + 1) >= (Qptr->Buffer+Qptr->Size))
X		Qptr->Tail = Qptr->Buffer;
X	else
X		Qptr->Tail ++;
X
X	if (Qptr->Head == Qptr->Tail)
X		Qptr->Flag = 1;		/* Queue buffer overflow */
X
X	if (Qptr->Flag)
X		Qptr->Head ++ ;
X	if (Qptr->Head >= Qptr->Buffer + Qptr->Size)
X		Qptr->Head = Qptr->Buffer;
}
/*
X * QpeekChar returns a character from the specified queue without removing
X * it. If the queue is empty -1 is returned.
*/
char QpeekChar(CircleQ * Qptr)
{
X	if (Qptr->Head == Qptr->Tail)
X	{
X		Qptr->Flag = 0;
X		return -1;
X	}
X	return *Qptr->Head;
}
/*
X * QreadChar returns a character from the specified queue. The queue is then
X * updated to reflect the last read. If the queue is empty, -1 is returned.
*/
char QreadChar(CircleQ * Qptr)
{
X	char Ch = -1;
X	if (Qptr->Head == Qptr->Tail)
X	{
X		Qptr->Flag = 0;
X		return -1;
X	}
X	Ch = *Qptr->Head;
X	Qptr->Head ++ ;
X	if (Qptr->Head >= Qptr->Buffer + Qptr->Size)
X		Qptr->Head = Qptr->Buffer;
X	return Ch;
}
/*
X * Qfree frees a queue control structure (CircleQ) and all buffers
X * associated with it (Qptr->Buffer). This Head and Tail pointers are
X * set to NULL, and Size and Flag (overrun flag) are set to 0.
X * Qptr is then freed, releasing all memory associated with the queue.
X * No subsequent operations may be made on this queue.
*/
void Qfree(CircleQ * Qptr)
{
X	if (Qptr != NULL)
X	{
X		free(Qptr->Buffer);
X		Qptr->Buffer = Qptr->Head = Qptr->Tail = NULL;
X		Qptr->Size = Qptr->Flag = 0;
X		free(Qptr);
X	}
}
X
SHAR_EOF
chmod 0644 circleq.c ||
echo 'restore of circleq.c failed'
Wc_c="`wc -c < 'circleq.c'`"
test 2692 -eq "$Wc_c" ||
	echo 'circleq.c: original size 2692, current size' "$Wc_c"
fi
# ============= circleq.h ==============
if test -f 'circleq.h' -a X"$1" != X"-c"; then
	echo 'x - skipping circleq.h (File already exists)'
else
echo 'x - extracting circleq.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'circleq.h' &&
#if !defined(CIRCLEQ_INCLUDE)
#define CIRCLEQ_INCLUDE
typedef struct _circq
{
X	char * 	Head;
X	char * 	Tail;
X	char * 	Buffer;
X	int	Size;
X	int	Flag;
} CircleQ;
X
CircleQ * 	Qalloc(int size);
void		Qfree(CircleQ * qptr);
void		QinsertChar(CircleQ * qptr, char ch);
char		QpeekChar(CircleQ * qptr);
char 		QreadChar(CircleQ * qptr);
#endif
X
SHAR_EOF
chmod 0644 circleq.h ||
echo 'restore of circleq.h failed'
Wc_c="`wc -c < 'circleq.h'`"
test 336 -eq "$Wc_c" ||
	echo 'circleq.h: original size 336, current size' "$Wc_c"
fi
# ============= main.c ==============
if test -f 'main.c' -a X"$1" != X"-c"; then
	echo 'x - skipping main.c (File already exists)'
else
echo 'x - extracting main.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
#include <stdio.h>
#include <conio.h>
#include "asynch.h"
void Beep(void);
int ctrlc(void)
{
X	cputc(COM2,3);
X	return 1;
}
main()
{
X	int RetCode, c;
X	if((RetCode = copen(COM2,2048,2400,NONE,DATA8,STOP1,0,0,0)) != 0)
X	{
X		printf("copen failed: Code: %d\n",RetCode);
X		return -1;
X	}
X	clrscr();
X	ctrlbrk(ctrlc);
X	fclose(stdout);
X	while(1)
X	{
X		if (kbhit())
X		{
X			c = getch();
X			if (c == 0)
X			{
X				switch(getch()&0xFF)
X				{
X					case 68 :
X						cclose(COM2);
X						return 0;
X					case 35 :
X						ControlDTR(COM2,OFF);
X						delay(500);
X						ControlDTR(COM2,ON);
X						break ;
X					case 48 :
X						SendBreak(COM2);
X						break ;
X					default :
X						Beep();
X						break;
X				}
X				
X			}
X			else
X				cputc(COM2,c);
X		}
X		if ((c = cgetc(COM2)) >= 0)
X			fprintf(stderr,"%c",c);
X	}
}
void Beep()
{
X	sound(1000);
X	delay(250);
X	nosound();
}
X
X	
SHAR_EOF
chmod 0644 main.c ||
echo 'restore of main.c failed'
Wc_c="`wc -c < 'main.c'`"
test 838 -eq "$Wc_c" ||
	echo 'main.c: original size 838, current size' "$Wc_c"
fi
# ============= makefile ==============
if test -f 'makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping makefile (File already exists)'
else
echo 'x - extracting makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'makefile' &&
X
!if $d(DEBUG)
objs=asynch.obj circleq.obj test.obj
cargs=-ms -v -DDEBUG=1
X
test.exe: $(objs)
X	cc $(cargs) -etest.exe $(objs)
!else
objs=asynch.obj circleq.obj main.obj
cargs=-ms -v
X
term.exe: $(objs)
X	cc $(cargs) -eterm.exe $(objs)
!endif
X
.c.obj:
X	cc -c $(cargs) $<
X
X
SHAR_EOF
chmod 0644 makefile ||
echo 'restore of makefile failed'
Wc_c="`wc -c < 'makefile'`"
test 270 -eq "$Wc_c" ||
	echo 'makefile: original size 270, current size' "$Wc_c"
fi
exit 0

-- 
-------------------------------------------------------------------------------
        resnicks@netcom.com, steve@camphq, IFNA:        1:143/105.0, 
                 co moderator for comp.binaries.os2
 Real life: Steve Resnick. Chief Software Architect, Process Scientific, Inc
 Flames, grammar and spelling errors >/dev/null
 The Asylum OS/2 BBS - (408)263-8017 12/2400,8,1 - Running Maximus CBCS 1.2
-------------------------------------------------------------------------------