[comp.sources.misc] v02i069: Timer and CTRL BRK functions in Turbo C

asjoshi@phoenix.princeton.edu (Amit S. Joshi) (03/06/88)

Submitted-By: "Amit S. Joshi" <asjoshi@phoenix.princeton.edu>

Archive-Name: turbo-C-timers


Comp.sources.misc: Volume 2, Issue 69
Submitted-By: "Amit S. Joshi" <asjoshi@phoenix.princeton.edu>
Archive-Name: turbo-C-timers

[*Another* ARCed source?!  Egads!  ++bsa]

Here are a set of four functions to handle timers and ^C breaks slightly
more easily from Turbo C. Includes a small 'manual' page to use the
functions. Could be ported to MSC (but I don't have that expensive
compiler  ;-). For some reason the functions cause a stack overflow in
the small, tiny and medium models. There is a small test program to
excercise the timer functions included. Also included is the makefile
and the default rules file. I used NDMAKE.

UUdecode and then unarchive. Any old arc program (even tthe UNIX ones)
should be able to do it.

[Not any more; it's now a shar.  ++bsa]

----- cut above this line -----
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r--r--   1 allbery  System      1622 Feb 27 00:55 MAKE.INI
# -rw-r--r--   1 allbery  System      1129 Mar  4 15:01 MAKEFILE
# -rw-r--r--   1 allbery  System      1574 Mar  4 15:37 TEST.C
# -rw-r--r--   1 allbery  System      5556 Feb 25 12:35 TICK.C
# -rw-r--r--   1 allbery  System      2511 Mar  4 14:56 TICK.DOC
# -rw-r--r--   1 allbery  System      1442 Feb 25 11:49 TICK.H
#
echo 'x - MAKE.INI'
if test -f MAKE.INI; then echo 'shar: not overwriting MAKE.INI'; else
sed 's/^X//' << '________This_Is_The_END________' > MAKE.INI
X#	rules especially for the Turbo C package.
X
XCC=tcc
XAS=masm
XLB=lib
XCPP=cpp
XLINK=tlink
XMODEL=s
X
XLIB=c:\turboc\lib
XINCLUDE=c:\turboc\include
X
XLIBS=
XSTDLIBFILES=$(LIB)\math$(MODEL) $(LIB)\c$(MODEL) $(LIB)\fp87 
XSTDOBJFILES=$(LIB)\c0$(MODEL) 
X
X
XTCFLAGS=-DTURBOC -m$(MODEL) 
XASFLAGS=/E
XLFLAGS=/d
XCFLAGS=
X
X.SUFFIXES: .i .com .exe .obj .asm .c .for .pas
X
X# create response files for tlink too.
X.RESPONSE_LINK:	tlink
X
X#	ASM -> EXE using masm and tlink
X.asm.exe:
X	$(AS) $<;
X	$(LINK) $(STDOBJFILES) $*,$*,,$(STDLIBFILES) $(LFLAGS) 
X	@rm -f $*.obj
X
X#	ASM -> OBJ using MASM
X.asm.obj:
X	$(AS) $<;
X
X#	C -> ASM using tcc -S option
X.c.asm:
X	$(CC) $(TCFLAGS) -S $(CFLAGS) -I$(INCLUDE) -L$(LIB) $(LIBS) $<
X	@tcod $*
X	@mv $*.cod $*.asm
X
X#	C -> COM using tcc and then exe2bin
X.c.com:
X	$(CC) $(TCFLAGS) $(CFLAGS) -I$(INCLUDE) -L$(LIB) $(LIBS) $<
X	-(@exe2bin $*.exe $*.com)
X	@rm -f $*.obj $*.exe
X
X#	C -> EXE using tcc ; use this since it is faster than C -> OBJ -> EXE
X.c.exe:
X	$(CC) $(TCFLAGS) $(CFLAGS) -I$(INCLUDE) -L$(LIB) $(LIBS) $<
X	@rm -fi- $*.obj
X
X#	C -> OBJ using tcc -c option
X.c.obj:
X	$(CC) $(TCFLAGS) -c $(CFLAGS) -I$(INCLUDE) -L$(LIB) $(LIBS) $<
X
X#	OBJ -> EXE using tcc 
X.obj.exe:
X	$(CC) $(TCFLAGS) $(CFLAGS) -I$(INCLUDE) -L$(LIB) $(LIBS) $<
X
X#	EXE -> COM using exe2bin
X.exe.com:
X	exe2bin $< $*.com
X	@rm -f $*.exe
X
X#	C -> I run preprocessor only
X.c.i:
X	$(CPP) $(TCFLAGS) -P $(CFLAGS) -I$(INCLUDE) -L$(LIB) $(LIBS) $<
X
X#	cleans the current directory - always needed
Xclean:;	@rm -f *.bak *.map *.lst
X
X#	make makefile using mkmf interactively
________This_Is_The_END________
if test `wc -l < MAKE.INI` -ne 75; then
	echo 'shar: MAKE.INI was damaged during transit (should have been 75 bytes)'
fi
fi		; : end of overwriting check
echo 'x - MAKEFILE'
if test -f MAKEFILE; then echo 'shar: not overwriting MAKEFILE'; else
sed 's/^X//' << '________This_Is_The_END________' > MAKEFILE
X
XMODEL	= ml
X#	turn optimization on by default
XCFLAGS        = -O
X
XDEST	      = .
X
XEXTHDRS	      = /turboc/include/dos.h \
X		/turboc/include/stdarg.h \
X		/turboc/include/stdio.h \
X		/turboc/include/stdlib.h
X
XHDRS	      = TICK.H
X
XLIBS	      = 
X
XMAKEFILE      = makefile
X
XOBJS	      = TEST.OBJ \
X		TICK.OBJ
X
X#	Print over the ethernet
XPRINT	      = eprint
X
XPROGRAM	      = test.exe
X
XSRCS	      = TEST.C \
X		TICK.C
X
Xall:		$(PROGRAM)
X
X$(PROGRAM):     $(OBJS) $(LIBS)
X		$(LINK) $(STDOBJFILES) $(OBJS),$@,,$(LIBS) $(STDLIBFILES)
X
Xclean:;	rm -f *.bak *.lst *.map $(OBJS) 
X
Xdepend:;	@mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)
X
Xinstall:	$(PROGRAM)
X		@mv $(PROGRAM) $(DEST)
X
Xprint:;		$(PRINT) $(HDRS) $(SRCS)
X
Xprogram:        $(PROGRAM)
X
Xupdate:		$(DEST)/$(PROGRAM)
X
X$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
X		@make -f $(MAKEFILE) DEST=$(DEST) install
X###
XTEST.OBJ: /turboc/include/stdio.h /turboc/include/stdarg.h tick.h \
X	/turboc/include/dos.h
XTICK.OBJ: /turboc/include/stdlib.h /turboc/include/stdio.h \
X	/turboc/include/stdarg.h tick.h /turboc/include/dos.h
________This_Is_The_END________
if test `wc -l < MAKEFILE` -ne 54; then
	echo 'shar: MAKEFILE was damaged during transit (should have been 54 bytes)'
fi
fi		; : end of overwriting check
echo 'x - TEST.C'
if test -f TEST.C; then echo 'shar: not overwriting TEST.C'; else
sed 's/^X//' << '________This_Is_The_END________' > TEST.C
X/*	test.c - gently excersise the tick.c functions */
X
X#include <stdio.h>
X#include "tick.h"
X
X#define	NT 300
X#define ND 5
X
Xvoid test1(void) {
X	printf("1");
X}
X
Xvoid test2(void) {
X	putc('a',stderr);
X}
X
Xmain() {
X	int i;
X	void test1(), test2();
X	
X	for (i = NT; i > 0; i--) {
X		test1(); test2();
X		delay(ND);
X	}
X	
X	if ((i = install_timer(test1)) != 0) {
X		printf("Install_timer failed: %d\n",i);
X		exit(1);
X	}
X	
X	printf("\nInstalled timer 1\n");
X	
X	for (i = NT; i > 0; i--) delay(ND);
X	
X	if ((i = install_timer(test2)) != 0) {
X		printf("Install_timer failed: %d\n",i);
X		exit(1);
X	}
X	
X	printf("\nInstalled timer 2\n");
X	
X	for (i = NT; i > 0; i--) delay(ND);
X	
X	if ((i = remove_timer(test2)) != 0) {
X		printf("Remove timer failed: %d\n",i);
X		exit(1);
X	}
X	printf("\nRemoved timer 2\n");
X	for (i = NT; i > 0; i--) delay(ND);
X	if ((i = install_timer(test2)) != 0) {
X		printf("Install_timer failed: %d\n",i);
X		exit(1);
X	}
X	
X	printf("\nInstalled timer 2\n");
X	
X	for (i = NT; i > 0; i--) delay(ND);
X	if ((i = remove_timer(test1)) != 0) {
X		printf("Remove timer failed: %d\n",i);
X		exit(1);
X	}
X	printf("\nRemoved timer 1\n");
X	for (i = NT; i > 0; i--) delay(ND);
X
X	if ((i = install_timer(test1)) != 0) {
X		printf("Install_timer failed: %d\n",i);
X		exit(1);
X	}
X	
X	printf("\nInstalled timer 1\n");
X	for (i = NT; i > 0; i--) delay(ND);
X	
X	if ((i = remove_timer(NULLVFP)) != 0) {
X		printf("Remove timer failed: %d\n",i);
X		exit(1);
X	}
X	printf("\nRemoved all timers\n");
X	for (i = NT; i < 0; i--) delay(ND);
X}
X	
________This_Is_The_END________
if test `wc -l < TEST.C` -ne 80; then
	echo 'shar: TEST.C was damaged during transit (should have been 80 bytes)'
fi
fi		; : end of overwriting check
echo 'x - TICK.C'
if test -f TICK.C; then echo 'shar: not overwriting TICK.C'; else
sed 's/^X//' << '________This_Is_The_END________' > TICK.C
X/*	tick.c - installs a function which is called every clock tick */
X/*	(c) - Amit Joshi, Princeton University 
X
X	This code may be used freely for any noncommercial use. It may NOT
X	be used in any commercial package without written permission from
X	the author. This clause is to protect me from legal hassles with
X	the university about code developed here. This code is supplied
X	"AS IS" i.e. with no warranty. Do not remove this notice. Any 
X	modifications should be clearly noted before redistribution.
X**/
X
X/**	Amit Joshi
X	MAE Dept., Engg. Quad.
X	Princeton University
X	December 1987
X**/
X
X/**			 
X	The __tick__() and dosbusy() functions have been stolen from the
X	"rdir.c" code by Dean D. McCrory. The __tick__() has been 
X	rewritten (and renamed from timer_handler()) to be more general.
X	Amit Joshi
X	January 1988
X**/
X
X#include <stdlib.h>
X#include <stdio.h>
X#include "tick.h"
X
X/* Stuff for to handle the ctrl break functions */
Xstatic int __nc_brks = 0;
Xstatic char __abort = 1;
Xstatic void (* __c_brks[NCBRKS])();
X
X/* Stuff to run timers */
Xstatic void interrupt (* __otimer)() = NULLIVFP;
Xstatic int __ntimers = 0;
Xstatic void (* __timers[NTIMERS])();
Xstatic char far * dosbusy_fl;    /* dos maintains this */
X
X/* The functions used in this file */
Xstatic int __cbrk(void);
Xstatic void __clean_timer(void);
Xstatic void interrupt __tick__(void);
Xstatic char far * getdosbusy(void);
X
Xstatic int
X__cbrk(void) {
X	int nf;
X	
X	if (!abort) {
X		for (nf = 0; nf < __nc_brks; ++nf)
X			(* __c_brks[nf])();
X		exit(1);
X	} else return 1;
X}
X
Xstatic void
X__clean_timer(void) {
X	if (__otimer == NULLIVFP) return;	 /* nothing set yet */
X	setvect(TIMER_INT,__otimer);
X}
X
X/* __tick__ ()
X *
X * This function intercepts the hardware timer interrupt.  It checks the
X * dosbusy flag and runs through a list of timer driven functions if safe 
X * to do so.
X */	
X
Xstatic void interrupt
X__tick__(void)
X{
X   static int in_fl = 0;
X   int timer;
X
X   /* if the following statement is NOT coded, the 8259 blocks all hardware
X      interrupts including the keyboard interrupt.  Since we wait for a key
X      in list_directory (), this causes the PC to lock up.  This one took
X      a while to figure out */
X   outportb (0x20, 0x20);        /* send eoi to 8259 */
X   (*__otimer) ();           /* chain to previous timer handler */
X   
X   if (! in_fl)
X      {
X      in_fl = 1;                 /* we are in our ISR */
X         if (! *dosbusy_fl ) 
X		 /* run through the list of timers */
X		 for (timer = 0; timer < __ntimers; ++timer)
X			 if (__timers[timer] != NULLVFP)  
X				 (* __timers[timer])();
X      in_fl = 0;
X      }
X   return;                    /* return from ISR */
X}
X
X/* getdosbusy ()
X *
X * Gets the Dos busy flag through interrupt 34h.  This Dos function returnes
X * the busy flag address in es:bx.  This is an UNDOCUMENTED feature of Dos,
X * however it has worked in Dos versions 2.11 - 3.30 for me - Dean McCrory.
X */
Xstatic char far * 
Xgetdosbusy (void)
X{
X   struct SREGS sregs;        /* segment registers */
X   union REGS regs;           /* normal registers */
X
X   regs.h.ah = 0x34;          /* get dos busy flag address (UNDOCUMENTED) */
X   intdosx (&regs, &regs, &sregs);
X   return (MK_FP (sregs.es, regs.x.bx));
X}
X
Xint
Xinstall_timer(void (*func)(void))
X{	
X	int i = 0;
X	void __clean_timer();
X	void interrupt __tick__();
X	
X	/* check if the function is already installed */
X
X	if (!__ntimers) {
X		__otimer = getvect(TIMER_INT);
X		/* Get address of DOS busy flag. */
X		dosbusy_fl = getdosbusy();
X		if (atexit(__clean_timer)) return 2;
X		install_cbrk(NULLVFP);
X		setvect(TIMER_INT,__tick__);
X	}
X	/* are we already installed ? */
X	for (i=0; i < __ntimers; i++) 
X		if (__timers[i] == func) return 0;
X	/* enough space for another function ? */
X	if (__ntimers >= NTIMERS) return 1;
X	__timers[__ntimers++] = func;
X	return 0;
X}
X
Xint
Xremove_timer(void (*func)(void))
X{
X	int i = 0;
X	
X	if (func == NULLVFP) {
X		__clean_timer();
X		__ntimers = 0;
X		return 0;
X	}
X	
X	if (!__ntimers) return 1;	/* No timers return func not there */
X	
X	do {
X		/* have we found the function ? */
X		if (__timers[i] == func) { 
X			/* is it the last one in the chain ? */
X			if (i++ == __ntimers) {
X				__timers[i-1] = NULLVFP;
X			} else {
X				/* move the chain backwards */
X				do { 
X					__timers[i-1] = __timers[i]; i++; 
X				} while(i <= __ntimers);
X			}			
X			__ntimers--;
X			return 0;
X		} else i++;
X	} while (i < __ntimers);
X	return 1;
X}
X
Xint
Xinstall_cbrk (void (* func)(void))
X{
X	int i = 0;
X
X
X	
X	if (!__nc_brks) {
X		setcbrk(1);	/* ensure that ctrl break is enabled */
X		ctrlbrk(__cbrk);
X		__abort = 1;
X	}
X	
X	if (func == NULLVFP) __abort = 1;
X	
X	for (i = 0; i < __nc_brks; i++)
X		if (__c_brks[i] == func) return 0;
X	/* enough space for another function ? */
X	if (__nc_brks >= NCBRKS) return 1;
X	__c_brks[__nc_brks++] = func;
X	return 0;
X}
X
Xint
Xremove_cbrk (void (* func)(void))
X{
X	int i = 0;
X	
X	if (func == NULLVFP) __abort = 0;
X	
X	if (!__nc_brks) return 1;	/* No timers return func not there */
X	
X	do {
X		/* have we found the function ? */
X		if (__c_brks[i] == func) { 
X			/* is it the last one in the chain ? */
X			if (i++ == __nc_brks) {
X				__c_brks[i-1] = NULLVFP;
X			} else {
X				/* move the chain backwards */
X				do { 
X					__c_brks[i-1] = __c_brks[i]; i++; 
X				} while(i <= __nc_brks);
X			}			
X			__nc_brks--;
X			return 0;
X		} else i++;
X	} while (i < __nc_brks);
X	return 1;
________This_Is_The_END________
if test `wc -l < TICK.C` -ne 220; then
	echo 'shar: TICK.C was damaged during transit (should have been 220 bytes)'
fi
fi		; : end of overwriting check
echo 'x - TICK.DOC'
if test -f TICK.DOC; then echo 'shar: not overwriting TICK.DOC'; else
sed 's/^X//' << '________This_Is_The_END________' > TICK.DOC
XAll the functions in this file make heavy use of TurboC and PCDOS
Xfacilities and are not portable. 
X
XWARNING:
X	DO NOT compile with tiny, small or medium models. Stack overflow
X	occurs and if the stack checking option of the compiler is used
X	then the timers are not unloaded and the system crashes - in fact
X	you have to setup the entire system from scratch.
X		
XUSAGE:
X	#include "tick.h"
X
X	int install_timer(vod (*func());
X		Installs the function "func()" to be called EVERY tick. 
X		Removes the function on exit from the program - both if
X		^C or normal. You MUST NOT use the Turbo C supplied
X		ctrlbrk() function if you use this one. Use
X		"install_cbrk()" instead.
X
X	int install_cbrk(void (*func)());
X		Installs the function "func()" to be called when ^C is hit.
X		You can chain a series of functions. If you use this DO NOT
X		use the Turbo C supplied ctrlbrk(). If argument is NULLVFP
X		^C exits from the program.
X
X	int remove_timer(void (*func)());
X		Removes "func()" from timer list. If NULLVFP is given as an
X		argument all timers are cleaned.
X
X	int remove_cbrk(void (*func)());
X		Removes "func()" from ctrlbrk list. If argument	is NULLVFP
X		^C has no action.
XRETURN VALUES:
X	0 => function was successfully installed or removed.
X	1 => Install failed because of lack of space. Compile again with a
X	     larger NTIMER or NCBRK in "tick.h"
X	     Remove  failed because function not found.
X	2 => Install timer failed because we could not hook timer cleaner 
X	     onto atexit(). Try again with fewer atexit() functions.
X
XNOTES:
X	* DO NOT use the Turbo C ctrlbrk() function if using these functions
X	  - it can have quite disasterous effects. Use install_cbrk() - it 
X	  is more general in any case !.
X	* Both the install functions try to check if the function is already
X	  present and do not duplicate installations.
X	* Define HARDTIMER in "tick.h" if you want to use the hardware 
X	  interrupt rather than the DOS 'soft' interrupt. This is at your own
X	  peril. I have not used this.
X
XBUGS:
X	Notify all bugs to :
X		Q3696@PUCC.BITNET or
X		{seismo, rutgers}\!princeton\!phoenix\!asjoshi
X
XACKNOWLEDGEMENTS:
X	Dean D. McCrory for two functions which really form the heart of the 
X	timer portion : timer_handler, and dosbusy. The first has been 
X	modified and made more general. It is also renamed to __tick__().
X	The rest of the code was written using Turbo C v1.5 entirely by me
X	Thanks to Borland for the wonderful (and inexpensive) C compiler.
X
________This_Is_The_END________
if test `wc -l < TICK.DOC` -ne 62; then
	echo 'shar: TICK.DOC was damaged during transit (should have been 62 bytes)'
fi
fi		; : end of overwriting check
echo 'x - TICK.H'
if test -f TICK.H; then echo 'shar: not overwriting TICK.H'; else
sed 's/^X//' << '________This_Is_The_END________' > TICK.H
X/*	tick.h - the include file for timer and c_brk function installation */
X/*	(c) - Amit Joshi, Princeton University 
X
X	This code may be used freely for any noncommercial use. It may NOT
X	be used in any commercial package without written permission from
X	the author. This clause is to protect me from legal hassles with
X	the university about code developed here. This code is supplied
X	"AS IS" i.e. with no warranty. Do not remove this notice. Any 
X	modifications should be clearly noted before redistribution.
X**/
X
X/**	Amit Joshi
X	MAE Dept., Engg. Quad.
X	Princeton University
X	December 1987
X**/
X
X/**
X	Change the values defined for NTIMERS and NCBRKS to increase 
X	number of timer and cbrk functions installed.
X
X	Amit Joshi
X	January 1988
X**/
X
X#ifndef	__TICK_H__
X#define	__TICK_H__
X
X#include <dos.h>
X
X/* Change the following definitions to increase number of timers and cbrks */
X#define	NTIMERS	2	/* number of timers installable */
X#define	NCBRKS	2	/* number of cbrks installable */
X
X#define NULLIVFP	(void interrupt (*)())NULL
X#define	NULLVFP		(void (*)())NULL
X#define NULLFP		(int (*)())NULL 
X
X#ifdef	HARDTIMER
X#define	TIMER_INT	0x08
X#else
X#define TIMER_INT	0x1C 	
X#endif
X
X/* user callable functions */
Xint	_Cdecl		install_timer(void (*func)());	
Xint	_Cdecl		install_cbrk(void (*func)());
Xint	_Cdecl		remove_timer(void (*func)());
Xint	_Cdecl		remove_cbrk(void (*func)());
X
X#endif	__TICK_H__
________This_Is_The_END________
if test `wc -l < TICK.H` -ne 51; then
	echo 'shar: TICK.H was damaged during transit (should have been 51 bytes)'
fi
fi		; : end of overwriting check
exit 0
---- cut below this line -----

Amit Joshi	BITNET	|	Q3696@PUCC.BITNET
		USENET	| {seismo, rutgers}\!princeton\!phoenix\!asjoshi
"There's a pleasure in being mad... which none but madmen know!" - St.Dryden
-- 
Amit Joshi	BITNET	|	Q3696@PUCC.BITNET
		USENET	| {seismo, rutgers}\!princeton\!phoenix\!asjoshi
"There's a pleasure in being mad... which none but madmen know!" - St.Dryden