|
|
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.