|
|
1.1 root 1: /*
2: * This file contains routines which define a simple high-precision timer
3: * for PC systems, with appropriate definitions for working under a variety
4: * of different operating environments.
5: *
6: * The basic idea, which is discussed in (among other places)
7: * "Accurately Timing Windows Events Without Timer Reprogramming"
8: * Jerry Jongerius,
9: * Microsoft Systems Journal,
10: * July 1991.
11: * is to read the count register of counter/timer channel 0 on the PC, which
12: * is normally set up to generate a divide-by-65536 signal from a 1.19318MHz
13: * timebase, ie. the infamous 18.2Hz timer tick.
14: *
15: * While it is possible to read this value with low overhead if you have some
16: * cooperation from the timer tick handler, it's not too tough for normal
17: * application code to get at this, as long as it is possible to disable
18: * interrupts for a short time. This is only necessary to prevent disruption
19: * to the technique from other subsystems that use it as well.
20: */
21:
22: /*
23: * Locate the system-dependent header files we need depending on the
24: * operating environment we are being compiled under.
25: */
26:
27: #include <ccompat.h>
28:
29: #define INT_FLAG_BIT_MASK 0x200
30:
31: #if defined (__GNUC__) && defined (GNUDOS)
32:
33: #include <pc.h>
34: #include <dos.h>
35:
36: #define INPUT_BYTE(port) inportb (port)
37: #define OUTPUT_BYTE(port,byte) outportb (port, byte)
38:
39: static inline unsigned short SPL7 (void) {
40: register unsigned short temp;
41: asm ("pushf; pop %0; cli" : "=r" (temp));
42: return temp;
43: }
44:
45: static inline void SPLX (unsigned short prev) {
46: if ((prev & INT_FLAG_BIT_MASK) != 0)
47: asm ("sti");
48: }
49:
50: #define BIOS_CLOCK_INT 0x1A
51:
52: static inline unsigned short GETTICKCOUNT (void) {
53: union REGS in;
54: in.h.ah = 0;
55: int86 (BIOS_CLOCK_INT, & in, & in);
56: return in.x.dx;
57: }
58:
59: #elif __BORLANDC__
60:
61: #include <dos.h>
62: #include <conio.h>
63:
64: #define INPUT_BYTE(port) inportb (port)
65: #define OUTPUT_BYTE(port,byte) outportb (port, byte)
66:
67: #define SPL7() (_AX = _FLAGS, disable (), _AX)
68: #define SPLX(s) ((s & INT_FLAG_BIT_MASK) != 0 ? enable () : (void) 0)
69:
70: #define BIOS_DATA_SEGMENT 0x40
71: #define BIOS_TICK_COUNT_LOW 0x6C
72: #define GETTICKCOUNT() * (unsigned short far *) MK_FP (BIOS_DATA_SEGMENT, BIOS_TICK_COUNT_LOW)
73:
74: #else
75: #error unknown system type
76: #endif
77:
78: /*
79: * Returns a 32-bit unsigned value which combines the BIOS tick count with
80: * the current count value from the CTC. Since we do not have the cooperation
81: * of the tick interrupt handler, we detect overflows by reading the BIOS
82: * count before and after; we examine the sign of the value we read from the
83: * CRTC to decide which side of the fence we fell on.
84: *
85: * The BIOS tick count is actually 32-bit, but the low 16-bit part only
86: * overflows about once per hour, so who cares?
87: */
88:
89: #define CTC_PORT_CMD 0x43
90: #define CTC_PORT_0 0x40
91: #define CTC_READ_CMD 0xC2
92:
93: #ifdef __USE_PROTO
94: unsigned long getTickCount (void)
95: #else
96: unsigned long
97: getTickCount ()
98: #endif
99: {
100: register unsigned short microcount;
101: unsigned short tickcount, tickcount2;
102: register short s;
103:
104: tickcount = GETTICKCOUNT ();
105:
106: /*
107: * Once we are in a critical section, issue the read (which locks the
108: * current values in the registers until the read completes).
109: */
110:
111: s = SPL7 ();
112:
113: OUTPUT_BYTE (CTC_PORT_CMD, CTC_READ_CMD);
114: microcount = (INPUT_BYTE (CTC_PORT_0) & 0x80) != 0 ? 0x8000 : 0;
115: microcount |= INPUT_BYTE (CTC_PORT_0) >> 1;
116: microcount |= INPUT_BYTE (CTC_PORT_0) << 7;
117:
118: SPLX (s);
119:
120: if ((microcount & 0x7FFF) == 0)
121: microcount |= 0x7FFF;
122:
123: /*
124: * Turn the count-down into a count-up.
125: */
126:
127: microcount = ~ microcount;
128:
129: /*
130: * See if we need to cope with a timer wraparound condition.
131: */
132:
133: if ((tickcount2 = GETTICKCOUNT ()) != tickcount) {
134: /*
135: * OK, now which BIOS tick did our measurement fall into?
136: * Since the CTC is a upcount from zero, if the value we
137: * read has the high bit clear, go with the new value,
138: * otherwise use the old one.
139: */
140:
141: if ((microcount & 0x8000) == 0)
142: tickcount = tickcount2;
143: }
144:
145: return ((unsigned long) tickcount << 16) | microcount;
146: }
147:
148: /*
149: * Sit in a loop making sure that the values returned from the timer code
150: * monotonically increase.
151: */
152:
153: #include <stdio.h>
154:
155: #ifdef __USE_PROTO
156: void main (void)
157: #else
158: void
159: main ()
160: #endif
161: {
162: unsigned long last, current;
163:
164: current = getTickCount ();
165: while (! kbhit ()) {
166: last = current;
167: current = getTickCount ();
168: if ((long) (current - last) <= 0) {
169: break;
170: }
171: }
172: printf ("At end : %lx <= %lx\n", current, last);
173: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.