delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/04/30/00:44:04

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

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 <dpmi.h>
 #include <libc/bss.h>
+#include <dos.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/exceptn.h>
+
+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
 

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019