4.4BSD/usr/src/contrib/X11R5-hp300/mit/server/ddx/hpbsd/input/get_tv.c

static char *RCSID = "$Header: /host/kaukau/disk2/X11R5/R5-hp300/mit/server/ddx/hpbsd/input/RCS/get_tv.c,v 1.1 1992/09/30 03:14:10 root Exp $";
/*
 * An alternative to gettimeofday(2) that is about 50 times faster
 * if used frequently.  HP-PA specific.
 *                     Dave Holt,  GSY Performance, dah@cup.hp.com
 */

#include <time.h>
#include <assert.h>

#ifdef NOTDEFINED /* The following is not currently used.  But 	
		   * I will leave it for possible future use.
		   * --kam 4/22/92
		   */

#ifdef DEBUG
#include <stdio.h>
#else /* Not DEBUG.  Don't bother with printf's. */
#define printf(s, a)
#endif /* DEBUG */

#define MILLION 1000000
#define BUNCH 8192		/* must be < 32767 */

unsigned int cr16();		/* in cr16.s (assembly code) */
static struct timeval slow_get_tv();
void *malloc();
static set_tix2usec();

/* This initial value for last_tv forces initialization of tix2usec[]. */
static struct timeval last_tv = {0, 2000000};
static unsigned int last_tix;

/*
 * tix2usec[BUNCH] is a table built by set_tix2usec() which converts
 * from CR16 ticks to microseconds:
 *     microseconds = tix2usec[ticks >> shift]
 */
static unsigned short *tix2usec;
static int shift;

struct timeval get_tv()		/* the fast path */
{
    unsigned int tix, delta_tix;
    long delta_usec;

    tix = cr16();
    delta_tix = tix - last_tix;
    last_tix = tix;
    if (delta_tix < BUNCH) {	/* we can use the array */
	delta_usec = tix2usec[delta_tix >> shift];
	last_tv.tv_usec += delta_usec;
	if (last_tv.tv_usec < MILLION) {
	    return(last_tv);
	}
    }
    return(slow_get_tv());
}

static struct timeval slow_get_tv()	/* the slow path */
{
    struct timezone tz;
    static int first_time = 1;

    if (first_time) {
	set_tix2usec();
	first_time = 0;
    }
    last_tix = cr16();
    gettimeofday(&last_tv, &tz);
    return(last_tv);
}

/* 
 * set_tix2usec() builds it by observing gettimeofday and CR16 over a
 * reasonable period (~50ms).
 */
static set_tix2usec()
{
    struct timeval tv1, tv2;
    struct timezone tz;
    int i, tix_per_entry, delta_us;
    unsigned int tix1, tix2, cr16(), delta_tix;
    double tix_per_usec, usec_per_tix;

    /* make sure the code is in memory before we time it */
    tix1 = cr16();
    gettimeofday(&tv1, &tz);

    do {
	tix1 = cr16();
	gettimeofday(&tv1, &tz);
	do {
	    tix2 = cr16();
	    gettimeofday(&tv2, &tz);
	    delta_us = (tv2.tv_sec - tv1.tv_sec) * MILLION +
	      (tv2.tv_usec - tv1.tv_usec);
	    delta_tix = tix2 - tix1;
	} while (delta_us < 50000); /* loop for at least 50ms */
    } while (tv2.tv_sec - tv1.tv_sec > 2); /* retry if delta is too big */

    tix_per_usec = 1.0 * delta_tix / delta_us;
    usec_per_tix = 1 / tix_per_usec;

    /* Table should convert at least 10ms deltas. */
    tix_per_entry = tix_per_usec * 10000 / BUNCH;
    for (shift = 0;
	 (1 << shift) < tix_per_entry;
	 shift++) {
    }

    tix2usec = (unsigned short *) malloc(BUNCH * sizeof(unsigned short));
    assert(tix2usec != NULL);

    for (i = 0; i < BUNCH; i++) {
	tix2usec[i] = i * (1 << shift) * usec_per_tix;
    }

    printf("tix_per_usec = %lf\n", tix_per_usec);
    printf("shift = %d\n", shift);
}

#endif /* NOTDEFINED */


void
calibrate_cr16(TicksPerMilli)

unsigned long *TicksPerMilli;

{

    struct timeval start, end;
    register unsigned long cr_start, cr_end;
    unsigned long total_ticks = 0, total_millis = 0;
    int i, j;

    for(i = 0; i < 6; i++)
    {
        cr_start = cr16();
        gettimeofday(&start, NULL);

	/* waste some time */
	for(j=0; j< 200000; j++)
		;
	cr_end = cr16();
	gettimeofday(&end, NULL);

	if(start.tv_usec > end.tv_usec)
	{
	    end.tv_usec += 1000000;
	    end.tv_sec--;
	}

	/* if we have a rollover during all of this, toss this one. */

	if(cr_end < cr_start)
	{
	    i--;
	}
	else
	{
	    total_millis += (end.tv_sec - start.tv_sec) * 1000 +
			    (end.tv_usec - start.tv_usec) / 1000;
	    total_ticks += cr_end - cr_start;
	}
    }

    *TicksPerMilli = total_ticks/total_millis;

}