Mail Archives: djgpp-workers/2003/04/30/00:44:04
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 -