
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	readme
#	testtime.c
#	timer.c
#	timer.h
#	makefile
#	testtime.prj
# This archive created: Fri Feb  9 02:07:03 1990
# By:	Dean Pentcheff (Department of Integrative Biology  Univ. of Calif., Berkeley)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'readme'
then
	echo shar: "will not over-write existing file 'readme'"
else
cat << \SHAR_EOF > 'readme'
Dean Pentcheff
Department of Integrative Biology
University of California at Berkeley
dean@violet.berkeley.edu

The code here provides PC programs with a millisecond resolution timer.  It
is entirely based on "tctimer" 1.0 by Richard S. Sadowsky (released to the
public domain 8/10/88) which, in turn, is based on "tptime" by Brian Foley
and Kim Kokkonen of TurboPower Software (also public domain).  I have
slightly rewritten the code to conform to C language conventions (in tctimer,
results were passed as argument pointers rather than function returns).  In
this version, elapsedtime() returns a time in seconds rather than
milliseconds, saving a floating point division by 1000.0.

These routines reprogram the timer chip, so they probably won't work if your
program does the same.  Other than that, there should be no incompatibility
problems.  They do not interfere with Turbo's sound() and nosound()
functions.

I've tested these under Turbo C 2.0.  Note that the file tctimer.c must be
separately compiled with the standalone "tcc" compiler since it uses inline
assembly.  Attempting to compile it under "tc" will result in compile-time
error messages.

The routine "initializetimer()" MUST be called first to reset the timer.  The
calling program MUST also call "restoretimer()" before exiting to reset the
timer to its original state.


The following routines are included (see testtimer.c for examples):

/*Reprogram the timer chip to allow 1 microsecond resolution*/
void initializetimer(void);

/*Read the timer with 1 microsecond resolution*/
long readtimer(void);

/*Calculate time elapsed (in seconds) between Start and Stop*/
double    elapsedtime(long start, long stop);

/*Restore the timer chip to its normal state*/
void restoretimer(void);


Limitations
-----------
Because long integers are used to represent time, TCTIMER cannot be used to
time events longer than about 60 minutes:

  4,294,967,295 (= $FFFFFFFF, largest unsigned value represented by longint)
/     1,193,181 (timer resolution in counts/second)
---------------
          3,599
        /    60 (seconds/minute)
        -------
           59.9 minutes

This should hardly be a problem, however, since an event longer than an hour
presumably doesn't need to be timed with 1-microsecond accuracy anyway.

Also note that the process of reading the time takes time. Hence, results of
timing very short events will be skewed by the overhead of reading the timer.
The following table shows the time measured between two calls to ReadTimer,
one right after the other.

  Toshiba 1000 (4.77MHz 8088)    125 microseconds  (tctimer)
  ATT 6300 (8MHz 8086)            53     "         (tctimer)
  Deskpro 286 (8MHz 80286)        35     "         (tctimer)
  Sperry IT (7.1MHz 286, 0 wait)  32     "         (tctimer)
  IBM PS/2 model 50               25     "         (tctimer)
  PC Designs GV386 (16MHz)        27     "         (tctimer)
  PC Source Standard 286 (10MHz)  21     "         (these routines)
SHAR_EOF
fi
if test -f 'testtime.c'
then
	echo shar: "will not over-write existing file 'testtime.c'"
else
cat << \SHAR_EOF > 'testtime.c'
#include	<stdio.h>
#include	<dos.h>
#include	<conio.h>
#include	"timer.h"

void main()
{
	long	start_time;
	long	stop_time;
	double	time;

	initializetimer();
	delay(100);

	start_time=readtimer();
	stop_time=readtimer();
	time = elapsedtime(start_time, stop_time);
	printf("time between succesive calls: %f s  (%f ms)\n",
		time, time*1000.0);

	start_time=readtimer();
	delay(2);
	stop_time=readtimer();
	time = elapsedtime(start_time, stop_time);
	printf("time of a 'delay(2)' call   : %f s  (%f ms)\n",
		time, time*1000.0);

	printf("time until you press a key...");
	start_time = readtimer();
	getch();
	stop_time = readtimer();
	time = elapsedtime(start_time, stop_time);
	printf("\b\b\b\b\b\b\b\b\bed a key: %f s  (%f ms)\n",
		time, time*1000.0);

	restoretimer();
}

SHAR_EOF
fi
if test -f 'timer.c'
then
	echo shar: "will not over-write existing file 'timer.c'"
else
cat << \SHAR_EOF > 'timer.c'
#include	<dos.h>
#include	"timer.h"
#pragma		inline

#define TimerResolution    1193181.667

double
cardinal(long l)
{
	return((l<0)?4294967296.0 + (long)l : (long)l);
}

double
elapsedtime(long start, long stop)
{
	return(cardinal(stop - start) / TimerResolution);
}

void
initializetimer(void)
{

  outportb(0x043,0x034);
  asm jmp short NullJump1

NullJump1:;

  outportb(0x040,0x000);
  asm jmp short NullJump2

NullJump2:;

  outportb(0x040,0x000);

}

void
restoretimer(void)
{
  outportb(0x043,0x036);
  asm jmp short NullJump1

NullJump1:;

  outportb(0x040,0x000);
  asm jmp short NullJump2

NullJump2:;

  outportb(0x040,0x000);

}

#pragma warn -rvl	/* shut up Turbo C warning about return value */
long
readtimer(void)
{
  asm cli				/* Disable interrupts */
  asm mov  dx,020h		/* Address PIC ocw3   */
  asm mov  al,00Ah		/* Ask to read irr    */
  asm out  dx,al
  asm mov  al,00h		/* Latch timer 0 */
  asm out  043h,al
  asm in   al,dx		/* Read irr      */
  asm mov  di,ax		/* Save it in DI */
  asm in   al,040h		/* Counter --> bx*/
  asm mov  bl,al		/* LSB in BL     */
  asm in   al,040h
  asm mov  bh,al		/* MSB in BH     */
  asm not  bx			/* Need ascending counter */
  asm in   al,021h		/* Read PIC imr  */
  asm mov  si,ax		/* Save it in SI */
  asm mov  al,00FFh		/* Mask all interrupts */
  asm out  021h,al
  asm mov  ax,040h		/* read low word of time */
  asm mov  es,ax		/* from BIOS data area   */
  asm mov  dx,es:[06Ch]
  asm mov  ax,si		/* Restore imr from SI   */
  asm out  021h,al
  asm sti				/* Enable interrupts */
  asm mov  ax,di		/* Retrieve old irr  */
  asm test al,001h		/* Counter hit 0?    */
  asm jz   done			/* Jump if not       */
  asm cmp  bx,0FFh		/* Counter > 0x0FF?    */
  asm ja   done			/* Done if so        */
  asm inc  dx			/* Else count int req. */
done:;
  asm mov ax,bx			/* set function result */
}
#pragma warn .rvl	/* reset Turbo C return value warning */

SHAR_EOF
fi
if test -f 'timer.h'
then
	echo shar: "will not over-write existing file 'timer.h'"
else
cat << \SHAR_EOF > 'timer.h'
/* timer.h
 * Include file defining the timer routines.
 * Include this file in any program using them.
 */

/*Calculate time elapsed (in seconds) between Start and Stop*/
double	elapsedtime(long start, long stop);

/*Reprogram the timer chip to allow 1 microsecond resolution*/
void	initializetimer(void);

/*Restore the timer chip to its normal state*/
void	restoretimer(void);

/*Read the timer with 1 microsecond resolution*/
long	readtimer(void);
SHAR_EOF
fi
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
cat << \SHAR_EOF > 'makefile'
# makefile for either Borland's brain-dead make, or Ndmake

OBJS = testtime.obj timer.obj
MODEL = s
TCINC = d:\;c:\usr\tc\include
TCLIB = d:\;c:\usr\tc\lib

testtime.exe:	$(OBJS)
	tcc -o$*.exe $(CFLAGS) -m$(MODEL) -I$(TCINC) -L$(TCLIB) $(OBJS)

timer.c:	timer.h
testtime.c:	timer.h timer.c

.c.obj:
	tcc -c $(CFLAGS) -m$(MODEL) -I$(TCINC) $<
SHAR_EOF
fi
if test -f 'testtime.prj'
then
	echo shar: "will not over-write existing file 'testtime.prj'"
else
cat << \SHAR_EOF > 'testtime.prj'
testtime (testtime.c timer.obj timer.h)
timer (timer.c timer.h)
SHAR_EOF
fi
exit 0
#	End of shell archive


