• C

Millisecond timing without assembly


I am interested in having millisecond timing for
psychology experiments.  The timer...() routines below
were given to me, but dont seem to work properly on
my pentium (200 Mhz).  You will see i have been trying to
test them by timing the vertical refresh, just giving
output every second, or most recently just printing out
timer0count().  Things are generally ok for a little while
but then crazy numbers appear.  (I am making a DOS program
in Borland 5, and then running in a DOS window, or in
DOS mode)

timer0Init() seems to be the critical routine.  I am
hoping one of you can see something obviously wrong with
it, or suggest an alternative source of code.


Mark Chappell

#include <time.h>
#include <process.h>
#include <dos.h>

#include <stdio.h>


/* control register addr of 8253 timer counter chip */
#define CTRL_REG 0x0043
/* word to initialise channel 0 */
#define CTRL_INIT_WORD 0x0036
/* addr of channel 0 */
#define CH2 0x0040
/* number of system clock ticks in a timer tick */

#define CLOCK_DIVISOR 1193
/* #define CLOCK_DIVISOR 1193
       1.193 MHz divided by 1193 gives a timer tick every 1 ms */

/* BIOS interrupt number for tick generated by 8253 channel 0 */
#define TICK_INT_NR 0x1C

/* Below taken from setvect Example.  MC 25/11/97 */
#ifdef __cplusplus
    #define __CPPARGS ...
    #define __CPPARGS

static int initialised = 0;  /* false */
/* static int ch = 0, notstalled = 1 */;
static unsigned long tickCnt = 0 /* , timecount = 0 */ ;
static void interrupt (*origTickHandler)(__CPPARGS); /* arg was empty MC */
static void interrupt newTickHandler(__CPPARGS); /* arg was void MC */

void timer0Init(void), timer0Dispose(void), timer0Reset(void);
unsigned long timer0TickCount(void);

unsigned char ch;
int i, ok, c;
long arr[1000];


for(i = 0; i < 100; i++)
    printf("%u ", timer0TickCount());
/*   while(timer0TickCount() < (i + 1) * 1000)
      if(timer0TickCount() > 30000)
            printf("%d ", timer0TickCount());
   printf("\n%d ", i + 1);      */

for(i = 0; i < 0; i++)
   ok = 0;  ch = 1;
   while(ch != 0)
      ch = inportb(0x3DA)&8;
/*      ok++;
      printf("h1 %X ", ch);
      if(ok > 1000)
            exit(0);              */
  /*    printf("\n");                */

       while(ch == 0)
            ch = inportb(0x3DA)&8;
/*       ok++;
     printf("h2 %X ", ch);
      if(ok > 1000)
            exit(0);            */
/*   printf("\n");                   */

arr[i] = timer0TickCount();

   printf("%u ", timer0TickCount());


for(i = 0; i < 100; i++)
      printf("%ld ", arr[i]);

void timer0Init(void)
      if (!initialised)
            initialised = 1;  /* true */
            /* install new ISR for bios tick interrupt, and save
current one */
            origTickHandler = getvect(TICK_INT_NR);
            setvect(TICK_INT_NR, newTickHandler);
            /* set channel 0 count period */
            outport(CTRL_REG, CTRL_INIT_WORD);
             outportb(CH2, CLOCK_DIVISOR & 0x00FF); /*  LSB     */
            outportb(CH2, CLOCK_DIVISOR >> 8);  /* MSB */
   /*   outportb(CH2, CLOCK_DIVISOR);      */
        /* init tick count */
      tickCnt = 0;

void timer0Dispose(void)
      if (initialised)
            initialised = 0;  /* false */
            /* restore channel 0 count period to 18.203 Hz */
            outport(CTRL_REG, CTRL_INIT_WORD);
            outportb(CH2, 0);
            outportb(CH2, 0);
            /* reinstall original ISR for bios tick */
            setvect(TICK_INT_NR, origTickHandler);

/* the new interrupt handler for the BIOS tick interrupt */
static void interrupt newTickHandler(__CPPARGS)

void timer0Reset(void)
      tickCnt = 0;

unsigned long timer0TickCount(void)
      return (tickCnt);
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

I have a similar piece of code which essently times the vertical retrace which I wrote a while back. Just having a quick look at it, I have at the very end of my interrupt handler the following
This was with BC3.11 and I'm not sure if you'll need it with BC5. Also BC3.11 docs say don't compile with 'Test stack overflow' on, I don't know if you are doing this or it is required with BC5.

Just a few thoughts.

Tommy HuiEngineerCommented:
If this is running on a Pentium, you can use its time counter, using the rdtsc instruction. There's code to do this with Visual C++, you search for pentium counter on the web. There may be code to do this with Borland C++. The counter is maintained by the hardware, so it is very accurate. You might want to search the web for the Zen Timer by Michael Abrash.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
markc2Author Commented:
Thanks, i found ztimer22.zip, after a bit.  I am wondering
if there is a more recent version (it seems to be early
to mid 96); i cant seem to find it.  With this i seem
to be getting sensible timing.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.