[comp.sys.ibm.pc.programmer] SERIAL HANDLER IN C

mac@idacrd.UUCP (Robert McGwier) (06/11/90)

Microsoft C lets you define ISR's.  Here's code to do what you want if you
decide that Microsoft might do the job.  I understand that Turbo C had this
function type first.  asc_enab enables the ISR, asc_disab disables that
port.  I have included code that could be used to do both COM1 and COM2
simultaneously.  You should call signal and replace the control break signal
with a dummy routine.  If you don't, the first time a character comes
in the serial port and you have control C ended the routine, the
system will most likely hang.  Writing serial interrupt handlers
is one of the most trivial exercises a beginning programmer can do with
the PC.  It is disgusting beyond belief to me that IBM, etc. never did
this in the BIOS.  I have run this code at 19200 by programming the
hardware directly as mentioned in an earlier article on a 4.77 Mhz
PC and I am sure that it will run faster.  The following is Microsoft
5.1  I will modify it for 6.0 and repost in my next posting in three
months.  It seems this needs sending about once every three months, ;-).


/* Copyright Bob McGwier, 1990.  ALL RIGHTS RESERVED */
/* May be used freely for non-commercial purposes */


#include <stdio.h>
#include <dos.h>
#include <malloc.h>
#define PIC_MASK 0x21
#define	PIC_EOI  0x20
#define ERR_MSK	 0x9E

/* Definitions for interrupt handling */

/*  COM1 */

#define COM_DATA_1 0x3F8	/* Data received on this I/O address */
#define COM_IER_1  0x3F9	/* This register enables interrupts */
#define COM_MCR_1  0x3FC	/* Control Register (signals) */
#define COM_STAT_1  0x3FD	/* Status Register */
#define COM_INT_1   0xC		/* 0xC handles IRQ4 or COM1 by standard */
#define INT_MASK_1  0x10	/* Mask for PIC (programmable interrupt
				   controller) 8259A */

/* COM2 */

#define COM_DATA_2 0x2F8	/* Data received on this I/O address */
#define COM_IER_2  0x2F9	/* This register enables interrupts */
#define COM_MCR_2  0x2FC	/* Control Register (signals) */
#define COM_STAT_2  0x2FD	/* Status Register */
#define COM_INT_2   0xB		/* 0xB handles IRQ3 or COM2 by standard */
#define INT_MASK_2  0x8		/* Mask for PIC (programmable interrupt
				   controller) 8259A */
static unsigned char *c1_buf;
static unsigned char *c2_buf;
static unsigned asc_in_1,asc_in_2,asc_old_1,asc_old_2;
static void interrupt cdecl far int_com1();
static void interrupt cdecl far int_com2();
static void far *old_c1,far *old_c2;


/* Instat is trivial.*/

/* If the ring buffer write pointer is not the same as the ring buffer
   read pointer, then a character is in the buffer */

int instat(com_port)
int com_port;
{
	switch(com_port) {
		case 0:{
			if (asc_old_1 == asc_in_1) return 0;
			else return 1;
			break;
		}
		case 1:{
			if (asc_old_2 == asc_in_2) return 0;
			else return 1;
			break;
		}
	}
}

/* If the ring buffer write pointer is not the same as the ring buffer
   read pointer, then a character is in the buffer */

int rcvbyte(com_port)
int com_port;
{
	int ch;
	switch(com_port) {
		case 0: {
			if (asc_old_1 == asc_in_1) return -1;
			else {
				ch = c1_buf[asc_old_1];
				asc_old_1 = (asc_old_1+1)&4095;
				return ch;
			}
		}
		case 1: {
			if (asc_old_2 == asc_in_2) return -1;
 			else {
				ch = c2_buf[asc_old_2];
				asc_old_2 = (asc_old_2+1)&4095;
				return ch;
			}
		}
	}
}

/*  COM1 Interrupt handler.  HARDWARE DEPENDENT */

static void interrupt cdecl far int_com1(es,ds,di,si,bp,sp,bx,dx,cx,ax)
 unsigned es,ds,di, si, bp, sp, bx, dx, cx, ax;
{
	char ch;
	_disable();		/* Disable interrupts while we move data
				   and pointers */
	if((ch = (inp(COM_STAT_1)&ERR_MSK)) == 0) {  /*If no error message */
		ch = inp(COM_DATA_1);	/* Get the character */
		c1_buf[asc_in_1] = ch;	/* Store data in circular buffer */
		asc_in_1 = (asc_in_1+1)&4095; /*iterate and wrap */
	} else ch = inp(COM_DATA_1);	/* Get the character */
	_enable();		/* Enable interrupts */
	outp(PIC_EOI,0x20);	/* Tell 8259A we have handled the interrupt */
}

/*  COM2 Interrupt handler.  HARDWARE DEPENDENT */

static void interrupt cdecl far int_com2(es,ds,di,si,bp,sp,bx,dx,cx,ax)
unsigned es,ds,di, si, bp, sp, bx, dx, cx, ax;
{
	char ch;
	_disable();		/* Disable interrupts while we move data
				   and pointers */
	if((ch = (inp(COM_STAT_2)&ERR_MSK)) == 0) {/* If no error message*/
		ch = inp(COM_DATA_2);	/* Get the character */
		c2_buf[asc_in_2] = ch;	/* Store data in circular buffer */
		asc_in_2 = (asc_in_2+1)&4095; /*iterate and wrap */
	} else ch=inp(COM_DATA_2);
	_enable();		/* Enable interrupts */
	outp(PIC_EOI,0x20);	/* Tell 8259A we have handled the interrupt */
}

/* 4096 character buffers are utilized, that is usually more than a screenful
	of data ;-) */
void asc_enab(com_port)
int com_port;
{
	char ch;
	switch(com_port) {
		case 0:{
			asc_in_1 = asc_old_1 = 0;
			if((c1_buf = (unsigned char *)malloc(4096)) == NULL) {
				perror("Out of memory");
				exit(0);
			}
			old_c1 = _dos_getvect(COM_INT_1);  /* Be a friendly
							      ISR and save the
							      old vector for
							      restoration */
			_dos_setvect(COM_INT_1,int_com1); /* Tell DOS whose got
							     the vector now */
			outp(COM_MCR_1,0xB);	/* Raise DTR and OUT2 */
			outp(COM_IER_1,1);	/* Interrupt enable register*/
			ch = inp(PIC_MASK);	/* Read the current 8259A
						   interrupt mask */
			ch &=  (0xFF^INT_MASK_1);/* Reset mask for COM1 */
			outp(PIC_MASK,ch);	/* Send it to the 8259A */
			break;
		}
		case 1:{
			asc_in_2 = asc_old_2 = 0;
			if((c2_buf = (unsigned char *)malloc(4096)) == NULL) {
				perror("Out of memory");
				exit(0);
			}
			old_c2 = _dos_getvect(COM_INT_2);  /* Be a friendly
							      ISR and save the
							      old vector for
							      restoration */
			_dos_setvect(COM_INT_2,int_com2);  /* Tell DOS whose
							      got the vector
							      now */
			outp(COM_MCR_2,0xB);	/* Raise DTR and OUT2 */
			outp(COM_IER_2,1);	/* Interrupt enable register*/
			ch = inp(PIC_MASK);	/* Read the current 8259A
						   interrupt mask */
			ch &=  (0xFF^INT_MASK_2);/* Reset mask for COM2 */
			outp(PIC_MASK,ch);	/* Send it to the 8259A */
			break;
		}
	}
}
void asc_disab(com_port)
int com_port;
{
	char ch;
	switch(com_port){
		case 0: {
			free(c1_buf);		/* Free the ring buffer */
			ch = inp(PIC_MASK);	/* Get 8259A (PIC) Mask */
			ch |= INT_MASK_1;	/* Set Interrupt Mask COM1 */
			outp(PIC_MASK,ch);	/* Write int. mask to 8259A*/
			outp(COM_MCR_1,inp(COM_MCR_1)^0xB);/* Lower DTR and OUT2 */
			_dos_setvect(COM_INT_1,old_c1);/* Return the vector
							  to its old position*/
			break;
		}
		case 1: {
			free(c2_buf);		/*Free the ring buffer */
			ch = inp(PIC_MASK);	/* Get 8259A (PIC) Mask */
			ch |= INT_MASK_2;	/* Set Interrupt Mask COM1 */
			outp(PIC_MASK,ch);	/* Write int. mask to 8259A*/
			outp(COM_MCR_2,inp(COM_MCR_2)^0xB);/* Lower DTR and OUT2 */
			_dos_setvect(COM_INT_2,old_c2);/* Restore the old
							  vector */
			break;
		}
	}
}

putcom(p,ch)
int p;
unsigned char ch;
{
	unsigned char status;
	switch(p) {
		case 0:{
			do {
				status = inp(COM_STAT_1);
			} while (!(status&0x20));
			outp(COM_DATA_1,ch);
			break;
		}
		case 1:{
			do {
				status = inp(COM_STAT_2);
			} while (!(status&0x20));
			outp(COM_DATA_2,ch);
			break;
		}
	}
}



-- 
____________________________________________________________________________
    My opinions are my own no matter	|	Robert W. McGwier, N4HY
    who I work for! ;-)			|	CCR, AMSAT, etc.
----------------------------------------------------------------------------