From: sandmann AT clio DOT rice DOT edu (Charles Sandmann) Message-Id: <10304300444.AA21224@clio.rice.edu> Subject: uclock again To: djgpp-workers AT delorie DOT com Date: Tue, 29 Apr 2003 23:44:02 -0500 (CDT) In-Reply-To: <3EAEE6FE.5603E13F@phekda.freeserve.co.uk> from "Richard Dawe" at Apr 29, 2003 09:56:30 PM X-Mailer: ELM [version 2.5 PL2] Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Reply-To: djgpp-workers AT delorie DOT com Errors-To: nobody AT delorie DOT com X-Mailing-List: djgpp-workers AT delorie DOT com X-Unsubscribes-To: listserv AT delorie DOT com Precedence: bulk I tried to test this under Windows NT 3.1 on my ancient 486 - but could not since DJGPP images don't seem to run there anymore. (Some from V2B1 would run, but I don't have time to build a toolchain with something that old just to test this program ...). I disabled the version check code and the exception catching works under DOS/CWSDPMI. If someone has NT/2K/XP on a 486, please give it a try. --- uclock.c_ Tue Dec 11 21:28:06 2001 +++ uclock.c Tue Apr 29 23:00:48 2003 @@ -1,2 +1,3 @@ +/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ @@ -11,4 +12,22 @@ #include #include +#include +#include +#include +#include + +typedef void (*SignalHandler) (int); + +/* Catch rdtsc exception if opcode not legal and always return 0LL */ +static void catch_rdtsc(int val) +{ + short *eip = (short *)__djgpp_exception_state->__eip; + if (*eip == 0x310f) { /* rdtsc opcode */ + __djgpp_exception_state->__eip += 2; /* skip over it */ + __djgpp_exception_state->__edx = 0; /* EDX = 0 */ + longjmp(__djgpp_exception_state, 0); /* EAX = 0 */ + } + return; +} static int uclock_bss = -1; @@ -30,4 +49,38 @@ uclock_t rv; + _farsetsel(_dos_ds); + + if (_os_trueversion == 0x532) { /* Windows NT, 2000, XP */ + static double multiplier; + static unsigned long btics; + + if (uclock_bss != __bss_count) { + SignalHandler saveold; + saveold = signal(SIGILL, catch_rdtsc); + tics = _farnspeekl(0x46c); + while ( (btics = _farnspeekl(0x46c)) == tics); + base = _rdtsc(); + signal(SIGILL, saveold); + if (base == 0) + multiplier = 0.0; + else { + while ( (tics = _farnspeekl(0x46c)) == btics); + if (tics < btics) tics = btics + 1; /* Midnight */ + multiplier = ((tics - btics)*65536) / (double)(_rdtsc() - base); + uclock_bss = __bss_count; + } + } + if (multiplier != 0.0) { + rv = (_rdtsc() - base) * multiplier; + tics = _farnspeekl(0x46c) - btics; + while (tics <= 0) tics += 0x1800b0; /* Midnight */ + if ((unsigned long)(rv >> 16) != tics) { /* Recalibrate */ + rv = ((uclock_t)tics) << 16; + multiplier = (tics*65536) / (double)(_rdtsc() - base); + } + return rv; + } + } + if (uclock_bss != __bss_count) { @@ -52,5 +105,4 @@ if the timer worked in mode 3. So we simply wait for one clock tick when we run on Windows. */ - _farsetsel(_dos_ds); otics = _farnspeekl(0x46c); do { @@ -63,9 +115,9 @@ /* Make sure the numbers we get are consistent */ do { - otics = _farpeekl(_dos_ds, 0x46c); + otics = _farnspeekl(0x46c); outportb(0x43, 0x00); lsb = inportb(0x40); msb = inportb(0x40); - tics = _farpeekl(_dos_ds, 0x46c); + tics = _farnspeekl(0x46c); } while (otics != tics); --- uclock.tx_ Sun Apr 13 20:59:32 2003 +++ uclock.txh Tue Apr 29 23:22:30 2003 @@ -38,4 +38,14 @@ this problem. +Windows NT, 2000 and XP attempt to use the @code{rdtsc} feature of newer +CPUs instead of the interval timer, because the timer tick and interval timer +are not coordinated. During calibration the @code{SIGILL} signal handler +is replaced to protect against systems which do not support or allow +@code{rdtsc}. If @code{rdtsc} is available, uclock will keep the upper +bits of the returned value consistent with the bios tick counter by +re-calibration if needed. If @code{rdtsc} is not available, these systems +fall back to interval timer usage, which may show an absolute error of 65536 +uclock ticks in the values and not be monotonically increasing. + @subheading Return Value