[comp.sys.ibm.pc] serial com interrupts

miker@wjvax.UUCP (Michael Ryan) (02/24/88)

interrupt driven serial communications had me stumped 
but just a few weeks ago, before my vacation, I persevered. 
the following program reads a stupid com1 port and does no 
handshaking to control, in our case, a simple data line from a key pad.

the program has compiled, assembled, linked and run on a stock PC AT
with dos 3.3, Msoft C 5.0 , and Msoft Macro Assembler 5.0.

there are 3 files: serial.c , the main routine with the interrupt handler;
uart.asm, the uart initialization procedure; and file, the MAKE utility
data file.

you may look at the  assembly portion and say 'hey, I could do that in
C with inp/outp statements.' well, so did I, but it does not function.
oh well. 

WATCHOUT, this is a transcription from PC printout to 
our R+D Optimum V system. there may be typos.

this may not be the greatest utility in the world, but it works.
our 'real' implementation does not print out the input characters, but
uses a ring buffer to process them. 

NOTE: this code was PRODUCED on WJ time, and WJ and I do not guarantee
it or place copyright on it, or anything else really lame for such a small
piece of 'mostly borrowed' code. 
I transcribed the code on my time.(ahem)

please use it in good health and if you can get it to work all in C then
please mail me the code.

michael ryan
...!wjvax!miker


		O   /	cut here
-----------------o--------------------------------------------
		O   \
/* 
	file: serial.c
	microsoft C 5.0

	main routine and interrupt handler for serial com1 
	input. dumb routine with no handshaking and no ring
	buffering. mostly an exercise in com1 control
	and very dear to my heart as it saved the day.
	
	call:	serial [ loopcount ]

	loopcount: optional loop count for input of data.
	the larger the loop the more chars in, however , the
	limit on chars in is 258 ( just because ).
*/

#include <dos.h>
#include <stdio.h>
#include <stdlib.h>

/* serial communications definitions, com 1 */
#define	_COM1	0xc
#define	COMBASE	((unsigned int) 0x3f8) /* com 1 */
#define	RXREG	(COMBASE)
#define	INTEN	(COMBASE + 1)	/* int enable */
#define	INTID	(COMBASE + 2)	/* int identification */
#define	LSTAT	(COMBASE + 5)	/* line status */
#define MSTAT	(COMBASE + 6)	/* modem status */

#define	PIC_EOI	((unsigned int) 0x20)	/* PIC instruction address
					and instruction for end of interrupt */

/* local control definitions */
#define TIMEOUT	100000L
#define MAXKEY	258

extern void far uart();		/* the assembler uart routine */

void interrupt far handler1(void);  /* our interrupt handler */

void (interrupt far *oldcom1)();  /* var to hold old handler.
				    not real useful HERE, but good practice */
void (interrupt far *newhandler)(); /* var to hold address of new handler for
				    use in _dos_setvect. using the handler
				    identifier does not work, but this method
				    of indirection does */

static int pflag;		/* interrupt received flag. why the name? 
				  it had meaning, once */


main(argc,argv)
int argc;
char **argv;
{

	/* yeah, redundant declarations,but I like to play it safe */
	void interrupt far handler1;

	extern void ( interrupt far *oldcom1)();
	extern void ( interrupt far *newhandler)();
	extern int pflag;

	register long count, timeout;
	int in;			/* input variable */
	char keys[MAXKEY];	/* input buffer */
	int  keycount;

	timeout = TIMEOUT;	/* default */
	if ( argc > 1 ) {	/* if arg try to use it as count */
		timeout = atol(argv[1]);
		if ( timeout == 0 ) /* paranoid */
			timeout = TIMEOUT;
	}/*fi*/

	printf("starting .. %ld count.\n",timeout);

	oldcom1 = _dos_getvect(_COM1);	/* capture old handler */
	newhandler = handler1;		/* set variable indirection
					as described by Msoft support people */
	_dos_setvect(_COM1, newhandler );

	uart();		/* asm procedure to set uart:
			9600, odd, 8 data, 2 stop , interrupt on data ready */
	
	for ( pflag=in=keycount=0,count=0L; count < timeout ; count += 1 ) {
		if ( pflag ) {
			_disable();
			in = inp( RXREG ); /* read data from uart */
			keys[keycount++] = (char) in;

			if ( keycount >= MAXKEY )
				break;		/* get out to dos reset */

			pflag = 0;		/* reset flag */
			_enable();
		}/*fi*/
	}/*rof*/

	/* dos reset */
	_dos_setvect(_COM1,oldcom1);
	keys[keycount] = '\0' ; 	/* null terminate string */
	printf("chars in: %s.\n",keys); /* print all keys in */

	exit(0);
}/* eo main */


/*	
	handler1:
	handle com1 interrupts by setting the pflag and resetting
	the PIC
*/
void cdecl interrupt far handler1()
{
	extern int pflag;

	pflag =1;
	_enable();	/* not done automatically by Msoft 'interrupt'
			function construct */
	(void) outp(PIC_EOI,PIC_EOI);  /* reset pic */
}/*eo handler1 */
			

		O   /	cut here
-----------------o--------------------------------------------
		O   \

; 	file: uart.asm
;	microsoft macro assembler 5.0
;
;	uart initialization file
;	a lot lifted from mr. ray duncan.

		DOSSEG
		PAGE 58, 132
		TITLE	'uart -- assembler module of serial.exe '

		PUBLIC	_uart

		.MODEL	large

; definitions for com 1
; not all used, but handy to know
comm_data	equ	03f8h	; uart data
comm_ier	equ	03f9h	; uart interrupt enable reg
comm_mcr	equ	03fch	; uart modem control reg
comm_stat	equ	03fdh	; line status	

com_int		equ	0ch	; com 1 interrupt

int_mask	equ	010h	; mask for 8259

pic_mask	equ	021h	; port address 8259 mask reg
pic_eoi		equ	020h	; port address 8259 EOI instruction

.CODE

_uart	proc	far
		
		xor	dx, dx		; zero dx: com1 value
		mov	ah, 0		; bios finit value
		mov	al, 0efh	; 9600 baud,odd parity,8 data,2 stop
		int	14h		; bios service

		mov	dx, comm_mcr	; modem control, because it
					; was in the ex and I have not  
					; tested without it
		mov	al, 0bh		; modem control DTR,RTS,OUT2
		out	dx, al

		mov	dx, comm_ier	; interrupt enable register
		mov	al, 1		; on data ready
		out	dx, al		

		in	al, pic_mask	; read current 8259 mask
		and 	al, 0efh	; == not int_mask 
		nop			; insert clock ticks (3)
					; may not be necessary
		out	pic_mask, al	; write the new one back

		ret
_uart	endp
		end
		O   /	cut here
-----------------o--------------------------------------------
		O   \
#	file : file
#	MAKE utility
#	use: make file 
#	creates serial.exe

# creates asm output for inspection, links for debugging
CFLAGS=/AL /Fa /Zi /c
LFLAGS=/CO
MFLAGS=/Zi
# assumes Msoft cl, link, and masm in your path
CC=cl $(CFLAGS)
LN=link $(LFLAGS)
AS=masm $(MFLAGS)

.C.OBJ :
	$(CC) $*.c
.ASM.OBJ :
	$(AS) $*,,,;

serial.obj : serial.c

uart.obj : uart.asm

serial.exe : serial.obj uart.obj
	$(LN) $**, $@;
-- 
====	*michael j ryan
	*{..!pyramid,..!decwrl!qubix}!wjvax!miker
	*Watkins-Johnson Co., San Jose CA : (408) 435 1400 x3079
	* above views are not necessarily those of Watkins-Johnson Co.