File: dos\uclock.c

    1 /*
    2 **  UCLOCK.C
    3 **
    4 **  Contains routines to perform microsecond accuracy timing
    5 **  operations.
    6 **
    7 **  Adapted from public domain source originally by David L. Fox
    8 **  Modified by Bob Stout
    9 */
   10 
   11 #include "uclock.h"
   12 
   13 /* Constants */
   14 
   15 #define CONTVAL   0x34    /* == 00110100 Control byte for 8253 timer.   */
   16                           /* Sets timer 0 to 2-byte read/write,         */
   17                           /* mode 2, binary.                            */
   18 #define T0DATA    0x40    /* Timer 0 data port address.                 */
   19 #define TMODE     0x43    /* Timer mode port address.                   */
   20 #define BIOS_DS   0x40    /* BIOS data segment.                         */
   21 #define B_TIKP    0x6c    /* Address of BIOS (18.2/s) tick count.       */
   22 #define SCALE    10000    /* Scale factor for timer ticks.              */
   23 
   24 /* The following values assume 18.2 BIOS ticks per second resulting from
   25    the 8253 being clocked at 1.19 MHz. */
   26 
   27 #define us_BTIK  54925    /* Micro sec per BIOS clock tick.             */
   28 #define f_BTIK    4595    /* Fractional part of usec per BIOS tick.     */
   29 #define us_TTIK   8381    /* Usec per timer tick * SCALE. (4/4.77 MHz)  */
   30 
   31 static int init = 0;
   32 
   33 /*
   34 **  usec_clock()
   35 **
   36 **  An analog of the clock() function, usec_clock() returns a number of
   37 **  type uclock_t (defined in UCLOCK.H) which represents the number of
   38 **  microseconds past midnight. Analogous to CLK_TCK is UCLK_TCK, the
   39 **  number which a usec_clock() reading must be divided by to yield
   40 **  a number of seconds.
   41 */
   42 
   43 uclock_t usec_clock(void)
   44 {
   45       unsigned char msb, lsb;
   46       unsigned int tim_ticks;
   47       static uclock_t last, init_count;
   48       static uclock_t far *c_ptr;
   49       uclock_t count, us_tmp;
   50 
   51       if (!init)
   52       {
   53             c_ptr = (uclock_t far *)MK_FP(BIOS_DS, B_TIKP);
   54             init  = 1;        /* First call, we have to set up timer.   */
   55             int_off(); 
   56             outp(TMODE, CONTVAL);   /* Write new control byte.          */
   57             outp(T0DATA, 0);        /* Initial count = 65636.           */
   58             outp(T0DATA, 0);
   59             init_count = *c_ptr;
   60             int_on();
   61             return 0;               /* First call returns zero.         */
   62       }
   63 
   64       /* Read PIT channel 0 count - see text                            */
   65 
   66       int_off();        /* Don't want an interrupt while getting time.  */
   67       outp(TMODE, 0);                           /* Latch count.         */
   68       lsb = (unsigned char)inp(T0DATA);         /* Read count.          */
   69       msb = (unsigned char)inp(T0DATA);
   70 
   71       /* Get BIOS tick count (read BIOS ram directly for speed and
   72          to avoid turning on interrupts).                               */
   73 
   74       count =  *c_ptr;
   75       int_on();                     /* Interrupts back on.              */
   76       if ((-1) == init)             /* Restart count                    */
   77       {
   78             init_count = count;
   79             init = 1;
   80       }
   81 
   82       /* Merge PIT channel 0 count with BIOS tick count                 */
   83 
   84       if (count < init_count)
   85             count += last;
   86       else  last = count;
   87       count -= init_count;
   88       tim_ticks = (unsigned)(-1) - ((msb << 8) | lsb);
   89       us_tmp    = count * us_BTIK;
   90       return (us_tmp + ((long)tim_ticks * us_TTIK + us_tmp % SCALE) / SCALE);
   91 }
   92 
   93 /*
   94 **  restart_uclock()
   95 **
   96 **  Since usec_clock() bases its return value on a differential value,
   97 **  a potential exists for problems in programs which run continuously
   98 **  for more than 24 hours. In such an application, it's necessary, at
   99 **  least once a day, to reset usec_clock's starting count.
  100 */
  101 
  102 void restart_uclock(void)
  103 {
  104       if (init)
  105             init = -1;
  106 }
  107