X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f Subject: Final version of gettimeofday() patch To: djgpp-workers AT delorie DOT com X-Mailer: Lotus Notes Release 7.0.2 September 26, 2006 Message-ID: From: Gordon DOT Schumacher AT seagate DOT com Date: Wed, 28 Feb 2007 15:36:12 -0700 X-MIMETrack: Serialize by Router on SV-GW1/Seagate Internet(Release 7.0.1 HF29|March 07, 2006) at 02/28/2007 02:36:15 PM MIME-Version: 1.0 Content-type: text/plain; charset=US-ASCII X-Proofpoint-FWRule: outbound2 X-Proofpoint-Virus-Version: vendor=fsecure engine=4.65.5502:2.3.11,1.2.37,4.0.164 definitions=2007-02-28_06:2007-02-28,2007-02-27,2007-02-28 signatures=0 Reply-To: djgpp-workers AT delorie DOT com I've now got what I believe to be a finalized version of my proposed changes to gettimeofday(). I'm running it to good effect here, and you can feel free to try compiling it with -DTEST to build a sample application that should check for drift against what the time that DOS int21 reports. On my test systems the drift was negligible. (And yes, I realize that I'm defining the clock frequency to far more digits than necessary; better too many than too few, though.) The include of is simply to pick up the macro definitions for likely() and unlikely(); you can compile it just fine without that header (which is new in my testing) by commenting out that header include and adding the following instead: #define likely(x) x #define unlikely(x) x I will include my proposal for in another message. --- src/libc/dos/dos/gettimeo.c.orig 1995-03-11 03:42:42.000000000 -0700 +++ src/libc/dos/dos/gettimeo.c 2007-02-28 15:28:57.156250000 -0700 @@ -1,44 +1,164 @@ +/* Copyright (C) 2007 Gordon Schumacher, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include +#include +#include +#include + +#undef CLOCKS_PER_SEC +/* +Thanks to Rod Pemberton for the math for the clock frequency. +The exact calculation is as follows: + 4 * ((4.5Mhz * 455) / 572) = 14.31818... MHz +(4.5Mhz US TV bandwith/channel, 455 colorburst phase changes/line, + 572 total lines/frame including sync) + + 14.318Mhz / 12 = 1.1931818... Mhz +(The clock has a divisor of 12) + + 1.1931818... Mhz / 65536 ~= 18.2065Hz +(65536 cycles per tick) + +Remember that clock() multiplies by 5! +*/ +//#define CLOCKS_PER_SEC ((4 * ((4.5 * 455) / 572)) / (12 * 65536)) +#define CLOCKS_PER_SEC 91.032548384232954545454545454545F +#define CLOCK_T_MAX INT_MAX int gettimeofday(struct timeval *tv, struct timezone *tz) { - __dpmi_regs r; - struct tm tmblk; - struct timeval tv_tmp; - - - if (!tv) - tv = &tv_tmp; - - r.h.ah = 0x2c; - __dpmi_int(0x21, &r); - - tv->tv_usec = r.h.dl * 10000; - tmblk.tm_sec = r.h.dh; - tmblk.tm_min = r.h.cl; - tmblk.tm_hour = r.h.ch; - - r.h.ah = 0x2a; - __dpmi_int(0x21, &r); - - tmblk.tm_mday = r.h.dl; - tmblk.tm_mon = r.h.dh - 1; - tmblk.tm_year = (r.x.cx & 0x7ff) - 1900; - - tmblk.tm_wday = tmblk.tm_yday = tmblk.tm_gmtoff = 0; - tmblk.tm_zone = 0; - tmblk.tm_isdst = -1; - - tv->tv_sec = mktime(&tmblk); - - if(tz) - { - struct tm *tmloc = localtime(&(tv->tv_sec)); - tz->tz_minuteswest = - tmloc->tm_gmtoff / 60; - tz->tz_dsttime = tmloc->tm_isdst; - } - return 0; + static clock_t startclock = 0; + static struct timeval starttime; + struct timeval tv_tmp; + + if (!tv) + tv = &tv_tmp; + + if (unlikely(startclock == 0)) + { + __dpmi_regs r; + struct tm tmblk; + + startclock = clock(); + + r.h.ah = 0x2c; + __dpmi_int(0x21, &r); + starttime.tv_usec = r.h.dl * 10000; + tmblk.tm_sec = r.h.dh; + tmblk.tm_min = r.h.cl; + tmblk.tm_hour = r.h.ch; + + r.h.ah = 0x2a; + __dpmi_int(0x21, &r); + + tmblk.tm_mday = r.h.dl; + tmblk.tm_mon = r.h.dh - 1; + tmblk.tm_year = (r.x.cx & 0x7ff) - 1900; + + tmblk.tm_wday = tmblk.tm_yday = tmblk.tm_gmtoff = 0; + tmblk.tm_zone = 0; + tmblk.tm_isdst = -1; + + starttime.tv_sec = mktime(&tmblk); + + tv->tv_sec = starttime.tv_sec; + tv->tv_usec = starttime.tv_usec; + } + else + { + double secs; + clock_t curclock = clock(); + unsigned long clockdiff; + + if (likely(curclock >= startclock)) + clockdiff = (unsigned long) (curclock - startclock); + else + { + clockdiff = ((unsigned long) CLOCK_T_MAX + curclock) - startclock; + startclock = 0; // Recalibrate now... + } + + secs = clockdiff / CLOCKS_PER_SEC; + tv->tv_sec = starttime.tv_sec + secs; + tv->tv_usec = starttime.tv_usec + ((secs - (long) secs) * 1000000); + if (unlikely(tv->tv_usec >= 1000000)) + { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } + + // Recalibrate against the RTC about once an hour + // This should keep any error within one tick + if (unlikely(clockdiff > 65535)) + startclock = 0; + } + + if (tz) + { + struct tm *tmloc = localtime(&(tv->tv_sec)); + tz->tz_minuteswest = - tmloc->tm_gmtoff / 60; + tz->tz_dsttime = tmloc->tm_isdst; + } + return 0; } + +#if defined(TEST) + #include + +int main() +{ + int start, end, i; + struct tm tmblk; + struct timeval rtc, tod; + time_t rtc_secs, tod_secs; + char timestr[25]; + unsigned long maxusec = 0; + __dpmi_regs r; + double maxposerror = 0.0F, maxnegerror = 0.0F; + + while (!kbhit()) + { + double curerror; + + r.h.ah = 0x2c; + __dpmi_int(0x21, &r); + rtc.tv_usec = r.h.dl * 10000; + tmblk.tm_sec = r.h.dh; + tmblk.tm_min = r.h.cl; + tmblk.tm_hour = r.h.ch; + + r.h.ah = 0x2a; + __dpmi_int(0x21, &r); + + tmblk.tm_mday = r.h.dl; + tmblk.tm_mon = r.h.dh - 1; + tmblk.tm_year = (r.x.cx & 0x7ff) - 1900; + + tmblk.tm_wday = tmblk.tm_yday = tmblk.tm_gmtoff = 0; + tmblk.tm_zone = 0; + tmblk.tm_isdst = -1; + rtc_secs = rtc.tv_sec = mktime(&tmblk); + + gettimeofday(&tod, NULL); + tod_secs = tod.tv_sec; + + if (tod.tv_usec > maxusec) + maxusec = tod.tv_usec; + curerror = difftime(rtc_secs, tod_secs) + ((rtc.tv_usec - tod.tv_usec) / 1000000.0F); + if (curerror > maxposerror) + maxposerror = curerror; + if (curerror < maxnegerror) + maxnegerror = curerror; + + strftime(timestr, sizeof(timestr), "%m/%d/%y %H:%M:%S", localtime(&tod_secs)); + printf("\rThe time now is %s.%ld ", timestr, tod.tv_usec); + } + printf("\nMaximum value of usec is %ld\n", maxusec); + printf("Maximum errors are %f and %f\n", maxposerror, maxnegerror); + return 0; +} +#endif + +