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