Watt-32 tcp/ip  2.2 dev-rel.10
gettod.c
Go to the documentation of this file.
1 
7 /* Copyright (c) 1997-2002 Gisle Vanem <gvanem@yahoo.no>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  * must display the following acknowledgement:
19  * This product includes software developed by Gisle Vanem
20  * Bergen, Norway.
21  *
22  * THIS SOFTWARE IS PROVIDED BY ME (Gisle Vanem) AND CONTRIBUTORS ``AS IS''
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL I OR CONTRIBUTORS BE LIABLE FOR ANY
26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *
34  * 10.aug 1996 - Created
35  *
36  */
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stddef.h>
41 #include <time.h>
42 #include <errno.h>
43 
44 #include "wattcp.h"
45 #include "misc.h"
46 #include "timer.h"
47 #include "x32vm.h"
48 #include "cpumodel.h"
49 #include "strings.h"
50 #include "gettod.h"
51 
52 #define STEVE 1
53 
54 #if defined(WIN32)
55  #include <sys/timeb.h>
56 #else
57  static long utc_offset = 0; /* UTC to local offset in seconds */
58 #endif
59 
60 /*
61  * Timezone defines.
62  */
63 #if defined(__WATCOMC__)
64  #define _timezone timezone
65 #elif defined(__TURBOC__) && (__TURBOC__ <= 0x410) /* TCC/BCC <= 3.1 */
66  #define _timezone timezone
67 #elif defined(__POCC__)
68  #define _timezone 0
69 #elif defined(_MSC_VER) && (_MSC_VER <= 600)
70  #define _timezone timezone
71 #endif
72 
73 #if defined(__CCDL__)
74 #define int386 _int386
75 #endif
76 
77 #ifndef __inline
78 #define __inline
79 #endif
80 
81 static void get_zone (struct timezone *tz, time_t now)
82 {
83  struct tm res;
84  struct tm *tm = localtime_r (&now, &res);
85 
86  if (tm && tz)
87  {
88 #ifdef __DJGPP__
89  tz->tz_minuteswest = - res.__tm_gmtoff / 60;
90 #else
91  tz->tz_minuteswest = - (int)_timezone;
92 #endif
93  tz->tz_dsttime = res.tm_isdst;
94  }
95 }
96 
97 #if defined(WIN32)
98 /*
99  * Stolen and modified from APR (Apache Portable Runtime):
100  * Number of micro-seconds between the beginning of the Windows epoch
101  * (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970).
102  *
103  * This assumes all Win32 compilers have 64-bit support.
104  */
105 #define DELTA_EPOCH_IN_USEC U64_SUFFIX (11644473600000000)
106 
107 static __inline uint64 FileTimeToUnixEpoch (const FILETIME *ft)
108 {
109  uint64 res = (uint64) ft->dwHighDateTime << 32;
110 
111  res |= ft->dwLowDateTime;
112  res /= 10; /* from 100 nano-sec periods to usec */
113  res -= DELTA_EPOCH_IN_USEC; /* from Win epoch to Unix epoch */
114  return (res);
115 }
116 
117 W32_FUNC int W32_CALL W32_NAMESPACE (gettimeofday) (
118  struct timeval *tv,
119  struct timezone *tz)
120 {
121  if (!tv && !tz)
122  {
123  SOCK_ERRNO (EINVAL);
124  return (-1);
125  }
126 
127  if (tv)
128  {
129  FILETIME ft;
130  uint64 tim;
131 
132  GetSystemTimeAsFileTime (&ft);
133  tim = FileTimeToUnixEpoch (&ft);
134  tv->tv_sec = (long) (tim / 1000000L);
135  tv->tv_usec = (long) (tim % 1000000L);
136  }
137  if (tz)
138  get_zone (tz, tv->tv_sec);
139  return (0);
140 }
141 #else
142 
146 void set_utc_offset (void)
147 {
148  struct timezone tz = { 0, 0 };
149 
150 #if !defined(__POCC__)
151  tzset();
152 #endif
153  get_zone (&tz, time(NULL));
154  utc_offset = 60 * tz.tz_minuteswest;
155 
156 #ifdef TEST_PROG
157  printf ("Minutes west %d, DST %d\n", tz.tz_minuteswest, tz.tz_dsttime);
158 #endif
159 }
160 #endif /* WIN32 */
161 
162 
163 #if (DOSX == 0)
164 int W32_CALL W32_NAMESPACE (gettimeofday) (struct timeval *tv, struct timezone *tz)
165 {
166  union REGS reg;
167  struct tm tm;
168 
169  if (!tv)
170  {
171  SOCK_ERRNO (EINVAL);
172  return (-1);
173  }
174 
175  reg.h.ah = 0x2C;
176  int86 (0x21, &reg, &reg);
177 
178  tv->tv_usec = reg.h.dl * 10000UL;
179  tm.tm_sec = reg.h.dh;
180  tm.tm_min = reg.h.cl;
181  tm.tm_hour = reg.h.ch;
182 
183  reg.h.ah = 0x2A;
184  int86 (0x21, &reg, &reg);
185 
186  tm.tm_mday = reg.h.dl;
187  tm.tm_mon = reg.h.dh - 1;
188  tm.tm_year = (reg.x.cx & 0x7FF) - 1900;
189  tm.tm_wday = tm.tm_yday = 0;
190  tm.tm_isdst = -1;
191 
192  tv->tv_sec = mktime (&tm);
193 
194  if (tz)
195  get_zone (tz, tv->tv_sec);
196  return (0);
197 }
198 
199 #elif (DOSX & (PHARLAP|X32VM))
200 int W32_CALL W32_NAMESPACE (gettimeofday) (struct timeval *tv, struct timezone *tz)
201 {
202  SWI_REGS reg;
203  struct tm tm;
204 
205  if (!tv)
206  {
207  SOCK_ERRNO (EINVAL);
208  return (-1);
209  }
210 
211  reg.r_ax = 0x2C00;
212  _dx_real_int (0x21, &reg);
213 
214  tv->tv_usec = loBYTE (reg.r_dx) * 10000UL;
215  tm.tm_sec = hiBYTE (reg.r_dx);
216  tm.tm_min = loBYTE (reg.r_cx);
217  tm.tm_hour = hiBYTE (reg.r_cx);
218 
219  reg.r_ax = 0x2A00;
220  _dx_real_int (0x21, &reg);
221 
222  tm.tm_mday = loBYTE (reg.r_dx);
223  tm.tm_mon = hiBYTE (reg.r_dx) - 1;
224  tm.tm_year = (reg.r_cx & 0x7FF) - 1900;
225  tm.tm_wday = tm.tm_yday = 0;
226  tm.tm_isdst = -1;
227 
228  tv->tv_sec = mktime (&tm);
229  if (tz)
230  get_zone (tz, tv->tv_sec);
231  return (0);
232 }
233 
234 #elif defined(__MSDOS__) && (defined(WATCOM386) || defined(BORLAND386) || defined(__CCDL__))
235 int W32_CALL W32_NAMESPACE (gettimeofday) (struct timeval *tv, struct timezone *tz)
236 {
237  union REGS reg;
238  struct tm tm;
239 
240  if (!tv)
241  {
242  SOCK_ERRNO (EINVAL);
243  return (-1);
244  }
245 
246  reg.x.eax = 0x2C00;
247  int386 (0x21, &reg, &reg);
248 
249  tv->tv_usec = reg.h.dl * 10000UL;
250  tm.tm_sec = reg.h.dh;
251  tm.tm_min = reg.h.cl;
252  tm.tm_hour = reg.h.ch;
253 
254  reg.x.eax = 0x2A00;
255  int386 (0x21, &reg, &reg);
256 
257  tm.tm_mday = reg.h.dl;
258  tm.tm_mon = reg.h.dh - 1;
259  tm.tm_year = (reg.x.ecx & 0x7FF) - 1900;
260  tm.tm_wday = tm.tm_yday = 0;
261  tm.tm_isdst = -1;
262 
263  tv->tv_sec = mktime (&tm);
264  if (tz)
265  get_zone (tz, tv->tv_sec);
266  return (0);
267 }
268 #endif /* DOSX == 0 */
269 
270 
271 #if defined(HAVE_UINT64) && defined(__MSDOS__)
272 /*
273  * Return hardware time-of-day in microseconds.
274  * NOTE: hardware counter (lo) counts down from 65536 at a rate of
275  * 1.19318 MHz (4.77/4).
276  */
277 static __inline uint64 microsec_clock (void)
278 {
279  DWORD hi;
280  long lo;
281  uint64 rc;
282 
283  do
284  {
285  hi = PEEKL (0, BIOS_CLK);
286  lo = clockbits();
287  }
288  while (hi != PEEKL(0, BIOS_CLK)); /* tick count changed, try again */
289  lo = 0 - lo;
290  rc = ((uint64)hi << 16) + lo;
291  return (rc * U64_SUFFIX(4000000) / U64_SUFFIX(4770000));
292 }
293 
294 #if (DOSX)
295 /*
296  * Return CPU time-stamp counter in microseconds since epoch
297  * (1 Jan 1970 UTC) or from '*base'.
298  */
299 static uint64 tsc_microsec (const uint64 *base)
300 {
301  static uint64 time_ofs = U64_SUFFIX(-1);
302  static time_t start = 0;
303  uint64 rc, now;
304 
305  if (clocks_per_usec == 0)
306  return (0);
307 
308  if (time_ofs == U64_SUFFIX(-1))
309  {
310  start = time (NULL);
311  while (time(NULL) == start)
312  ENABLE();
313  time_ofs = GET_RDTSC();
314  }
315  if (base)
316  now = *base;
317  else now = GET_RDTSC();
318  rc = (now - time_ofs) / clocks_per_usec;
319  rc += U64_SUFFIX(1000000) * (start+1);
320  return (rc);
321 }
322 
323 static void adjust_cpu_clock (const struct timeval *tv)
324 {
325  static DWORD last = 0;
326  DWORD tick = PEEKL (0, BIOS_CLK);
327 
328  if (((tick % 5) == 0) && last != tick) /* 260msec passed */
329  {
330  static double sample[4];
331  static int idx = 0;
332 
333  if (idx < DIM(sample))
334  {
335  struct timeval tv0;
336 
337  gettimeofday (&tv0, NULL);
338  sample[idx++] = timeval_diff (&tv0, tv);
339  }
340  else
341  {
342  double diff = 0.0;
343  int i, corr;
344 
345  for (i = 0; i < DIM(sample); i++)
346  diff += sample[i];
347  idx = 0;
348  diff = diff / DIM(sample);
349  corr = abs ((int)last-(int)tick) / 8;
350  corr = diff < 0.0 ? -corr : +corr;
351  clocks_per_usec += corr;
352 
353 #ifdef TEST_PROG
354  printf ("diff %.6f, corr %d\n", diff/1E6, corr);
355 #endif
356  }
357  last = tick;
358  }
359 }
360 
361 /*
362  * Return a 'timeval' from a CPU timestamp.
363  */
364 BOOL get_tv_from_tsc (const struct ulong_long *tsc, struct timeval *tv)
365 {
366  uint64 usecs;
367 
368  if (!tsc || !tv || !has_rdtsc)
369  return (FALSE);
370 
371  usecs = tsc_microsec ((const uint64*)tsc);
372  if (!usecs)
373  return (FALSE);
374 
375  tv->tv_usec = (long) (usecs % U64_SUFFIX(1000000));
376  tv->tv_sec = (time_t) ((usecs - tv->tv_usec) / U64_SUFFIX(1000000));
377  return (TRUE);
378 }
379 #endif /* DOSX */
380 #endif /* HAVE_UINT64 && __MSDOS__ */
381 
382 
383 /*
384  * A high-resolution [1us] version of gettimeofday() needed in
385  * select_s() etc. Should return 'tv_sec' in UTC.
386  */
387 #if defined(WIN32)
388 int W32_CALL gettimeofday2 (struct timeval *tv, struct timezone *tz)
389 {
390  init_misc();
391  return gettimeofday (tv, tz);
392 }
393 
394 #else
395 
396 int W32_CALL gettimeofday2 (struct timeval *tv, struct timezone *tz)
397 {
398  init_misc();
399 
400  if (user_tick_active)
401  {
402  tv->tv_usec = (long) (1000UL * (user_tick_msec % 1000UL));
403  tv->tv_sec = (DWORD) user_tick_base + (user_tick_msec / 1000UL);
404 
405  if (tz)
406  get_zone (tz, tv->tv_sec);
407  return (0);
408  }
409 
410 #if defined(HAVE_UINT64)
411 #if (DOSX)
412  if (has_rdtsc && use_rdtsc)
413  {
414  uint64 usecs = tsc_microsec (NULL);
415 
416  if (!usecs)
417  return (-1);
418 
419  tv->tv_usec = (long) (usecs % U64_SUFFIX(1000000));
420  tv->tv_sec = (time_t) ((usecs - tv->tv_usec) / U64_SUFFIX(1000000));
421  if (tz)
422  get_zone (tz, tv->tv_sec);
423  adjust_cpu_clock (tv);
424  return (0);
425  }
426 #endif
427 
428  if (has_8254)
429  {
430  static time_t secs = 0; /* seconds since midnight */
431  uint64 usecs = microsec_clock(); /* usec day-clock */
432 
433 #if STEVE
434  secs = time (NULL);
435 #else
436  static uint64 last = 0;
437  if (secs == 0 || usecs < last) /* not init or wrapped */
438  {
439  secs = time (NULL);
440  secs -= (secs % (24*3600));
441  }
442  last = usecs;
443 #endif
444  tv->tv_usec = (long) (usecs % U64_SUFFIX(1000000));
445 #if STEVE
446  tv->tv_sec = (time_t) secs;
447 #else
448  tv->tv_sec = (time_t) ((usecs - tv->tv_usec) / U64_SUFFIX(1000000) + (uint64)secs);
449 #endif
450  tv->tv_sec += utc_offset;
451 
452  if (tz)
453  get_zone (tz, tv->tv_sec);
454  return (0);
455  }
456 #endif /* HAVE_UINT64 */
457 
458  return gettimeofday (tv, tz);
459 }
460 #endif
461 
462 
463 #if defined(TEST_PROG) /* for djgpp/Watcom/HighC */
464 
465 #ifndef __MSDOS__
466 #error "For MS-DOS only"
467 #endif
468 
469 #include "printk.h"
470 
471 static __inline BOOL day_rollover (void)
472 {
473  return PEEKB (0, 0x470);
474 }
475 
476 #if !defined(__DJGPP__) && 0
477 static void usleep (DWORD usec)
478 {
479  struct timeval now, start, expiry;
480 
481  gettimeofday2 (&start, NULL);
482  expiry.tv_sec = start.tv_sec + (usec / 1000000UL);
483  expiry.tv_usec = start.tv_usec + (usec % 1000000UL);
484  while (expiry.tv_usec >= 1000000UL)
485  {
486  expiry.tv_usec -= 1000000UL;
487  expiry.tv_sec++;
488  }
489  while (1)
490  {
491  gettimeofday2 (&now, NULL);
492  if (now.tv_sec > expiry.tv_sec ||
493  (now.tv_sec == expiry.tv_sec && now.tv_usec > expiry.tv_usec))
494  break;
495  }
496 }
497 #endif
498 
499 int main (int argc, char **argv)
500 {
501  DWORD loops = 0;
502  BOOL do_cmp = FALSE;
503  BOOL use_isr = FALSE;
504  int ch;
505 
506  putenv ("USE_RDTSC=0");
507 
508  init_misc();
509 
510  while ((ch = getopt(argc, argv, "?chir")) != EOF)
511  switch (ch)
512  {
513  case 'c':
514  do_cmp = TRUE;
515  break;
516  case 'h':
517  hires_timer (0);
518  break;
519  case 'i':
520  init_timer_isr();
521  use_isr = TRUE;
522  break;
523  case 'r':
524  if (use_isr)
525  {
526  puts ("Cannot use both `-i' and `-r'");
527  return (-1);
528  }
529  putenv ("USE_RDTSC=1");
530  break;
531  case '?':
532  default:
533  printf ("Usage: %s [-chir]\n"
534  " -c compare gettimeofday2() and gettimeofday()\n"
535  " -h don't use hi-res 8254 PIT\n"
536  " -i use timer ISR\n"
537  " -r use RDTSC for gettimeofday2()\n",
538  argv[0]);
539  return (0);
540  }
541 
542  if (do_cmp)
543  puts ("gettimeofday2(), gettimeofday(), tv2-tv ctime rollover");
544  else puts ("gettimeofday2(), sleep, (ctime)");
545 
546  puts ("------------------------------------------------------------------------");
547 
548  while (!kbhit())
549  {
550  struct timeval tv, tv2;
551  struct timeval last;
552  double delta;
553 
554  gettimeofday (&tv, NULL);
555  gettimeofday2 (&tv2, NULL);
556 
557  if (loops > 0)
558  delta = timeval_diff (&tv2, &last);
559  else delta = 0.0;
560  last.tv_sec = tv2.tv_sec;
561  last.tv_usec = tv2.tv_usec;
562 
563  if (do_cmp)
564  {
565  printf ("%10ld.%06lu, %10ld.%06lu, %12.6f",
566  (long)tv2.tv_sec, tv2.tv_usec, (long)tv.tv_sec, tv.tv_usec,
567  timeval_diff(&tv2,&tv)/1E6);
568  }
569  else
570  printf ("%10ld.%06lu, %.6f", (long)tv2.tv_sec, tv2.tv_usec, delta/1E6);
571 
572  printf (", %.8s, %d\n",
573  ctime(&tv2.tv_sec)+11, day_rollover());
574 
575 #ifdef __DJGPP__ /* don't use delay(). It doesn't work under Win2K/XP */
576  usleep (1000000);
577 #else
578  sleep (1);
579 #endif
580  loops++;
581  }
582  return (0);
583 }
584 #endif /* TEST_PROG */
585 
struct tm * localtime_r(const time_t *t, struct tm *res)
A reentrant localtime().
Definition: misc.c:886
BOOL has_rdtsc
Never set in Watt-32.
Definition: timer.c:52
DWORD clocks_per_usec
Measured CPU speed (MHz)
Definition: timer.c:62
Core definitions.
BOOL use_rdtsc
Ditto.
Definition: timer.c:53
unsigned long long uint64
our unsigned "long long" type
Definition: wattcp.h:60
Definition: x32vm.h:16
void set_utc_offset(void)
Called from init_timers() once.
Definition: gettod.c:146
Definition: wtime.h:38
BOOL has_8254
Do we have a working 8254 timer chip?
Definition: timer.c:56
int hires_timer(int on)
Control use of high-resolution timer.
Definition: timer.c:377
void W32_CALL init_misc(void)
Initialise various stuff.
Definition: misc.c:324
Definition: wtime.h:47
int main(int argc, char **argv)
Definition: echo.c:223