From: sandmann AT clio DOT rice DOT edu (Charles Sandmann) Message-Id: <10304290440.AA26174@clio.rice.edu> Subject: uclock proposed patch To: djgpp-workers AT delorie DOT com (DJGPP developers) Date: Mon, 28 Apr 2003 23:40:04 -0500 (CDT) 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 For Windows 2000 and XP; I'll try a test on NT4 and on a 486 running NT 3.1 if I get a chance. Autocalibrates as needed to always keep the tic counter aligned. This might cause some jitter due to the inaccuracy of the NT tic counter, but since it's not a real time OS and multitasking who knows what really happened. Installs signal handler, should never been seen in real life (no one is running Windows NT 3.5 or earlier which were the last versions to support 486s which didn't have rdtsc). If they are, it falls back to tic accuracy. Comments? --- uclock.c_ Tue Dec 11 21:28:06 2001 +++ uclock.c Mon Apr 28 23:25:18 2003 @@ -11,4 +11,21 @@ #include #include +#include +#include +#include +#include + +/* Catch rdtsc exception and always return 0LL */ +static void catch_rdtsc(int val) +{ + short *eip = (short *)__djgpp_exception_state->__eip; + if(*eip == 0x310f) { + __djgpp_exception_state->__eip += 2; + __djgpp_exception_state->__edx = 0; + longjmp(__djgpp_exception_state, 0); + } + return; +} + static int uclock_bss = -1; @@ -30,4 +47,32 @@ uclock_t rv; + _farsetsel(_dos_ds); + + if(_os_trueversion == 0x532) { /* Windows NT, 2000, XP */ + static double multiplier; + static unsigned long btics; + uclock_t rval; + + if (uclock_bss != __bss_count) { + signal(SIGILL, catch_rdtsc); + tics = _farnspeekl(0x46c); + while ( (btics = _farnspeekl(0x46c)) == tics); + base = _rdtsc(); + if (base == 0) base = -1LL; + while ( (tics = _farnspeekl(0x46c)) == btics); + if (tics < btics) tics = btics + 1; /* Midnight */ + multiplier = ((tics - btics)*65536) / (double)(_rdtsc() - base); + uclock_bss = __bss_count; + } + rval = (_rdtsc() - base) * multiplier; + tics = _farnspeekl(0x46c) - btics; + while (tics <= 0) tics += 0x1800b0; /* Midnight */ + if( (unsigned long)(rval >> 16) != tics) { /* Recalibrate */ + rval = ((uclock_t)tics) << 16; + multiplier = (tics*65536) / (double)(_rdtsc() - base); + } + return rval; + } + if (uclock_bss != __bss_count) { @@ -52,5 +97,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 +107,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);