1 /* $NetBSD: chutest.c,v 1.6 2020/05/25 20:47:19 christos Exp $ */
2
3 /* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
4 * chutest - test the CHU clock
5 */
6
7 #ifdef HAVE_CONFIG_H
8 # include <config.h>
9 #endif
10 #include <stdio.h>
11 #include <fcntl.h>
12 #ifdef HAVE_UNISTD_H
13 # include <unistd.h>
14 #endif
15 #ifdef HAVE_STROPTS_H
16 # include <stropts.h>
17 #else
18 # ifdef HAVE_SYS_STROPTS_H
19 # include <sys/stropts.h>
20 # endif
21 #endif
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <sys/ioctl.h>
26 #include <sys/time.h>
27 #include <sys/file.h>
28 #ifdef HAVE_TERMIOS_H
29 # include <termios.h>
30 #else
31 # ifdef HAVE_SGTTY_H
32 # include <sgtty.h>
33 # endif
34 #endif
35
36 #include "ntp_fp.h"
37 #include "ntp.h"
38 #include "ntp_unixtime.h"
39 #include "ntp_calendar.h"
40
41 #ifdef CHULDISC
42 # ifdef HAVE_SYS_CHUDEFS_H
43 # include <sys/chudefs.h>
44 # endif
45 #endif
46
47
48 #ifndef CHULDISC
49 #define NCHUCHARS (10)
50
51 struct chucode {
52 u_char codechars[NCHUCHARS]; /* code characters */
53 u_char ncodechars; /* number of code characters */
54 u_char chustatus; /* not used currently */
55 struct timeval codetimes[NCHUCHARS]; /* arrival times */
56 };
57 #endif
58
59 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
60
61 char const *progname;
62
63 int dofilter = 0; /* set to 1 when we should run filter algorithm */
64 int showtimes = 0; /* set to 1 when we should show char arrival times */
65 int doprocess = 0; /* set to 1 when we do processing analogous to driver */
66 #ifdef CHULDISC
67 int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
68 #endif
69 #ifdef STREAM
70 int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
71 #endif
72
73 struct timeval lasttv;
74 struct chucode chudata;
75
76 void error(char *fmt, char *s1, char *s2);
77 void init_chu(void);
78 int openterm(char *dev);
79 int process_raw(int s);
80 int process_ldisc(int s);
81 void raw_filter(unsigned int c, struct timeval *tv);
82 void chufilter(struct chucode *chuc, l_fp *rtime);
83
84
85 /*
86 * main - parse arguments and handle options
87 */
88 int
main(int argc,char * argv[])89 main(
90 int argc,
91 char *argv[]
92 )
93 {
94 int c;
95 int errflg = 0;
96 extern int ntp_optind;
97
98 progname = argv[0];
99 while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
100 switch (c) {
101 case 'c':
102 #ifdef STREAM
103 usechuldisc = 1;
104 break;
105 #endif
106 #ifdef CHULDISC
107 usechuldisc = 1;
108 break;
109 #endif
110 #ifndef STREAM
111 #ifndef CHULDISC
112 (void) fprintf(stderr,
113 "%s: CHU line discipline not available on this machine\n",
114 progname);
115 exit(2);
116 #endif
117 #endif
118 case 'd':
119 ++debug;
120 break;
121 case 'f':
122 dofilter = 1;
123 break;
124 case 'p':
125 doprocess = 1;
126 case 't':
127 showtimes = 1;
128 break;
129 default:
130 errflg++;
131 break;
132 }
133 if (errflg || ntp_optind+1 != argc) {
134 #ifdef STREAM
135 (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
136 progname);
137 #endif
138 #ifdef CHULDISC
139 (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
140 progname);
141 #endif
142 #ifndef STREAM
143 #ifndef CHULDISC
144 (void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
145 progname);
146 #endif
147 #endif
148 exit(2);
149 }
150
151 (void) gettimeofday(&lasttv, (struct timezone *)0);
152 c = openterm(argv[ntp_optind]);
153 init_chu();
154 #ifdef STREAM
155 if (usechuldisc)
156 process_ldisc(c);
157 else
158 #endif
159 #ifdef CHULDISC
160 if (usechuldisc)
161 process_ldisc(c);
162 else
163 #endif
164 process_raw(c);
165 /*NOTREACHED*/
166 }
167
168
169 /*
170 * openterm - open a port to the CHU clock
171 */
172 int
openterm(char * dev)173 openterm(
174 char *dev
175 )
176 {
177 int s;
178 struct sgttyb ttyb;
179
180 if (debug)
181 (void) fprintf(stderr, "Doing open...");
182 if ((s = open(dev, O_RDONLY, 0777)) < 0)
183 error("open(%s)", dev, "");
184 if (debug)
185 (void) fprintf(stderr, "open okay\n");
186
187 if (debug)
188 (void) fprintf(stderr, "Setting exclusive use...");
189 if (ioctl(s, TIOCEXCL, (char *)0) < 0)
190 error("ioctl(TIOCEXCL)", "", "");
191 if (debug)
192 (void) fprintf(stderr, "done\n");
193
194 ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
195 ttyb.sg_erase = ttyb.sg_kill = 0;
196 ttyb.sg_flags = EVENP|ODDP|RAW;
197 if (debug)
198 (void) fprintf(stderr, "Setting baud rate et al...");
199 if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
200 error("ioctl(TIOCSETP, raw)", "", "");
201 if (debug)
202 (void) fprintf(stderr, "done\n");
203
204 #ifdef CHULDISC
205 if (usechuldisc) {
206 int ldisc;
207
208 if (debug)
209 (void) fprintf(stderr, "Switching to CHU ldisc...");
210 ldisc = CHULDISC;
211 if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
212 error("ioctl(TIOCSETD, CHULDISC)", "", "");
213 if (debug)
214 (void) fprintf(stderr, "okay\n");
215 }
216 #endif
217 #ifdef STREAM
218 if (usechuldisc) {
219
220 if (debug)
221 (void) fprintf(stderr, "Poping off streams...");
222 while (ioctl(s, I_POP, 0) >=0) ;
223 if (debug)
224 (void) fprintf(stderr, "okay\n");
225 if (debug)
226 (void) fprintf(stderr, "Pushing CHU stream...");
227 if (ioctl(s, I_PUSH, "chu") < 0)
228 error("ioctl(I_PUSH, \"chu\")", "", "");
229 if (debug)
230 (void) fprintf(stderr, "okay\n");
231 }
232 #endif
233 return s;
234 }
235
236
237 /*
238 * process_raw - process characters in raw mode
239 */
240 int
process_raw(int s)241 process_raw(
242 int s
243 )
244 {
245 u_char c;
246 int n;
247 struct timeval tv;
248 struct timeval difftv;
249
250 while ((n = read(s, &c, sizeof(char))) > 0) {
251 (void) gettimeofday(&tv, (struct timezone *)0);
252 if (dofilter)
253 raw_filter((unsigned int)c, &tv);
254 else {
255 difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
256 difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
257 if (difftv.tv_usec < 0) {
258 difftv.tv_sec--;
259 difftv.tv_usec += 1000000;
260 }
261 (void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
262 c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
263 difftv.tv_usec);
264 lasttv = tv;
265 }
266 }
267
268 if (n == 0) {
269 (void) fprintf(stderr, "%s: zero returned on read\n", progname);
270 exit(1);
271 } else
272 error("read()", "", "");
273 }
274
275
276 /*
277 * raw_filter - run the line discipline filter over raw data
278 */
279 void
raw_filter(unsigned int c,struct timeval * tv)280 raw_filter(
281 unsigned int c,
282 struct timeval *tv
283 )
284 {
285 static struct timeval diffs[10];
286 struct timeval diff;
287 l_fp ts;
288
289 if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
290 if (debug)
291 (void) fprintf(stderr,
292 "character %02x failed BCD test\n", c);
293 chudata.ncodechars = 0;
294 return;
295 }
296
297 if (chudata.ncodechars > 0) {
298 diff.tv_sec = tv->tv_sec
299 - chudata.codetimes[chudata.ncodechars].tv_sec;
300 diff.tv_usec = tv->tv_usec
301 - chudata.codetimes[chudata.ncodechars].tv_usec;
302 if (diff.tv_usec < 0) {
303 diff.tv_sec--;
304 diff.tv_usec += 1000000;
305 } /*
306 if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
307 if (debug)
308 (void) fprintf(stderr,
309 "character %02x failed time test\n");
310 chudata.ncodechars = 0;
311 return;
312 } */
313 }
314
315 chudata.codechars[chudata.ncodechars] = c;
316 chudata.codetimes[chudata.ncodechars] = *tv;
317 if (chudata.ncodechars > 0)
318 diffs[chudata.ncodechars] = diff;
319 if (++chudata.ncodechars == 10) {
320 if (doprocess) {
321 TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
322 ts.l_ui += JAN_1970;
323 chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
324 } else {
325 register int i;
326
327 for (i = 0; i < chudata.ncodechars; i++) {
328 (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
329 chudata.codechars[i] & 0xf,
330 (chudata.codechars[i] >>4 ) & 0xf,
331 chudata.codetimes[i].tv_sec,
332 chudata.codetimes[i].tv_usec,
333 diffs[i].tv_sec, diffs[i].tv_usec);
334 }
335 }
336 chudata.ncodechars = 0;
337 }
338 }
339
340
341 /* #ifdef CHULDISC*/
342 /*
343 * process_ldisc - process line discipline
344 */
345 int
process_ldisc(int s)346 process_ldisc(
347 int s
348 )
349 {
350 struct chucode chu;
351 int n;
352 register int i;
353 struct timeval diff;
354 l_fp ts;
355 void chufilter();
356
357 while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
358 if (n != sizeof chu) {
359 (void) fprintf(stderr, "Expected %d, got %d\n",
360 sizeof chu, n);
361 continue;
362 }
363
364 if (doprocess) {
365 TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
366 ts.l_ui += JAN_1970;
367 chufilter(&chu, &ts);
368 } else {
369 for (i = 0; i < NCHUCHARS; i++) {
370 if (i == 0)
371 diff.tv_sec = diff.tv_usec = 0;
372 else {
373 diff.tv_sec = chu.codetimes[i].tv_sec
374 - chu.codetimes[i-1].tv_sec;
375 diff.tv_usec = chu.codetimes[i].tv_usec
376 - chu.codetimes[i-1].tv_usec;
377 if (diff.tv_usec < 0) {
378 diff.tv_sec--;
379 diff.tv_usec += 1000000;
380 }
381 }
382 (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
383 chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
384 chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
385 diff.tv_sec, diff.tv_usec);
386 }
387 }
388 }
389 if (n == 0) {
390 (void) fprintf(stderr, "%s: zero returned on read\n", progname);
391 exit(1);
392 } else
393 error("read()", "", "");
394 }
395 /*#endif*/
396
397
398 /*
399 * error - print an error message
400 */
401 void
error(char * fmt,char * s1,char * s2)402 error(
403 char *fmt,
404 char *s1,
405 char *s2
406 )
407 {
408 (void) fprintf(stderr, "%s: ", progname);
409 (void) fprintf(stderr, fmt, s1, s2);
410 (void) fprintf(stderr, ": ");
411 perror("");
412 exit(1);
413 }
414
415 /*
416 * Definitions
417 */
418 #define MAXUNITS 4 /* maximum number of CHU units permitted */
419 #define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */
420 #define NCHUCODES 9 /* expect 9 CHU codes per minute */
421
422 /*
423 * When CHU is operating optimally we want the primary clock distance
424 * to come out at 300 ms. Thus, peer.distance in the CHU peer structure
425 * is set to 290 ms and we compute delays which are at least 10 ms long.
426 * The following are 290 ms and 10 ms expressed in u_fp format
427 */
428 #define CHUDISTANCE 0x00004a3d
429 #define CHUBASEDELAY 0x0000028f
430
431 /*
432 * To compute a quality for the estimate (a pseudo delay) we add a
433 * fixed 10 ms for each missing code in the minute and add to this
434 * the sum of the differences between the remaining offsets and the
435 * estimated sample offset.
436 */
437 #define CHUDELAYPENALTY 0x0000028f
438
439 /*
440 * Other constant stuff
441 */
442 #define CHUPRECISION (-9) /* what the heck */
443 #define CHUREFID "CHU\0"
444
445 /*
446 * Default fudge factors
447 */
448 #define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
449 #define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
450
451 /*
452 * Hacks to avoid excercising the multiplier. I have no pride.
453 */
454 #define MULBY10(x) (((x)<<3) + ((x)<<1))
455 #define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
456 #define MULBY24(x) (((x)<<4) + ((x)<<3))
457
458 /*
459 * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
460 * as an l_fp fraction, NZPOBITS is the number of significant bits
461 * in ZEROPTONE.
462 */
463 #define ZEROPTONE 0x1999999a
464 #define NZPOBITS 29
465
466 /*
467 * The CHU table. This gives the expected time of arrival of each
468 * character after the on-time second and is computed as follows:
469 * The CHU time code is sent at 300 bps. Your average UART will
470 * synchronize at the edge of the start bit and will consider the
471 * character complete at the center of the first stop bit, i.e.
472 * 0.031667 ms later. Thus the expected time of each interrupt
473 * is the start bit time plus 0.031667 seconds. These times are
474 * in chutable[]. To this we add such things as propagation delay
475 * and delay fudge factor.
476 */
477 #define CHARDELAY 0x081b4e80
478
479 static u_long chutable[NCHUCHARS] = {
480 0x2147ae14 + CHARDELAY, /* 0.130 (exactly) */
481 0x2ac08312 + CHARDELAY, /* 0.167 (exactly) */
482 0x34395810 + CHARDELAY, /* 0.204 (exactly) */
483 0x3db22d0e + CHARDELAY, /* 0.241 (exactly) */
484 0x472b020c + CHARDELAY, /* 0.278 (exactly) */
485 0x50a3d70a + CHARDELAY, /* 0.315 (exactly) */
486 0x5a1cac08 + CHARDELAY, /* 0.352 (exactly) */
487 0x63958106 + CHARDELAY, /* 0.389 (exactly) */
488 0x6d0e5604 + CHARDELAY, /* 0.426 (exactly) */
489 0x76872b02 + CHARDELAY, /* 0.463 (exactly) */
490 };
491
492 /*
493 * Keep the fudge factors separately so they can be set even
494 * when no clock is configured.
495 */
496 static l_fp propagation_delay;
497 static l_fp fudgefactor;
498 static l_fp offset_fudge;
499
500 /*
501 * We keep track of the start of the year, watching for changes.
502 * We also keep track of whether the year is a leap year or not.
503 * All because stupid CHU doesn't include the year in the time code.
504 */
505 static u_long yearstart;
506
507 /*
508 * Imported from the timer module
509 */
510 extern u_long current_time;
511 extern struct event timerqueue[];
512
513 /*
514 * init_chu - initialize internal chu driver data
515 */
516 void
init_chu(void)517 init_chu(void)
518 {
519
520 /*
521 * Initialize fudge factors to default.
522 */
523 propagation_delay.l_ui = 0;
524 propagation_delay.l_uf = DEFPROPDELAY;
525 fudgefactor.l_ui = 0;
526 fudgefactor.l_uf = DEFFILTFUDGE;
527 offset_fudge = propagation_delay;
528 L_ADD(&offset_fudge, &fudgefactor);
529
530 yearstart = 0;
531 }
532
533
534 void
chufilter(struct chucode * chuc,l_fp * rtime)535 chufilter(
536 struct chucode *chuc,
537 l_fp *rtime
538 )
539 {
540 register int i;
541 register u_long date_ui;
542 register u_long tmp;
543 register u_char *code;
544 int isneg;
545 int imin;
546 int imax;
547 u_long reftime;
548 l_fp off[NCHUCHARS];
549 l_fp ts;
550 int day, hour, minute, second;
551 static u_char lastcode[NCHUCHARS];
552
553 /*
554 * We'll skip the checks made in the kernel, but assume they've
555 * been done. This means that all characters are BCD and
556 * the intercharacter spacing isn't unreasonable.
557 */
558
559 /*
560 * print the code
561 */
562 for (i = 0; i < NCHUCHARS; i++)
563 printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
564 ((chuc->codechars[i]>>4) & 0xf) + '0');
565 printf("\n");
566
567 /*
568 * Format check. Make sure the two halves match.
569 */
570 for (i = 0; i < NCHUCHARS/2; i++)
571 if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
572 (void) printf("Bad format, halves don't match\n");
573 return;
574 }
575
576 /*
577 * Break out the code into the BCD nibbles. Only need to fiddle
578 * with the first half since both are identical. Note the first
579 * BCD character is the low order nibble, the second the high order.
580 */
581 code = lastcode;
582 for (i = 0; i < NCHUCHARS/2; i++) {
583 *code++ = chuc->codechars[i] & 0xf;
584 *code++ = (chuc->codechars[i] >> 4) & 0xf;
585 }
586
587 /*
588 * If the first nibble isn't a 6, we're up the creek
589 */
590 code = lastcode;
591 if (*code++ != 6) {
592 (void) printf("Bad format, no 6 at start\n");
593 return;
594 }
595
596 /*
597 * Collect the day, the hour, the minute and the second.
598 */
599 day = *code++;
600 day = MULBY10(day) + *code++;
601 day = MULBY10(day) + *code++;
602 hour = *code++;
603 hour = MULBY10(hour) + *code++;
604 minute = *code++;
605 minute = MULBY10(minute) + *code++;
606 second = *code++;
607 second = MULBY10(second) + *code++;
608
609 /*
610 * Sanity check the day and time. Note that this
611 * only occurs on the 31st through the 39th second
612 * of the minute.
613 */
614 if (day < 1 || day > 366
615 || hour > 23 || minute > 59
616 || second < 31 || second > 39) {
617 (void) printf("Failed date sanity check: %d %d %d %d\n",
618 day, hour, minute, second);
619 return;
620 }
621
622 /*
623 * Compute seconds into the year.
624 */
625 tmp = (u_long)(MULBY24((day-1)) + hour); /* hours */
626 tmp = MULBY60(tmp) + (u_long)minute; /* minutes */
627 tmp = MULBY60(tmp) + (u_long)second; /* seconds */
628
629 /*
630 * Now the fun begins. We demand that the received time code
631 * be within CLOCK_WAYTOOBIG of the receive timestamp, but
632 * there is uncertainty about the year the timestamp is in.
633 * Use the current year start for the first check, this should
634 * work most of the time.
635 */
636 date_ui = tmp + yearstart;
637 #define CLOCK_WAYTOOBIG 1000 /* revived from ancient sources */
638 if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
639 && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
640 goto codeokay; /* looks good */
641
642 /*
643 * Trouble. Next check is to see if the year rolled over and, if
644 * so, try again with the new year's start.
645 */
646 date_ui = calyearstart(rtime->l_ui, NULL);
647 if (date_ui != yearstart) {
648 yearstart = date_ui;
649 date_ui += tmp;
650 (void) printf("time %u, code %u, difference %d\n",
651 date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
652 if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
653 && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
654 goto codeokay; /* okay this time */
655 }
656
657 ts.l_uf = 0;
658 ts.l_ui = yearstart;
659 printf("yearstart %s\n", prettydate(&ts));
660 printf("received %s\n", prettydate(rtime));
661 ts.l_ui = date_ui;
662 printf("date_ui %s\n", prettydate(&ts));
663
664 /*
665 * Here we know the year start matches the current system
666 * time. One remaining possibility is that the time code
667 * is in the year previous to that of the system time. This
668 * is only worth checking if the receive timestamp is less
669 * than CLOCK_WAYTOOBIG seconds into the new year.
670 */
671 if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
672 date_ui = tmp;
673 date_ui += calyearstart(yearstart - CLOCK_WAYTOOBIG,
674 NULL);
675 if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
676 goto codeokay;
677 }
678
679 /*
680 * One last possibility is that the time stamp is in the year
681 * following the year the system is in. Try this one before
682 * giving up.
683 */
684 date_ui = tmp;
685 date_ui += calyearstart(yearstart + (400 * SECSPERDAY),
686 NULL);
687 if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
688 printf("Date hopelessly off\n");
689 return; /* hopeless, let it sync to other peers */
690 }
691
692 codeokay:
693 reftime = date_ui;
694 /*
695 * We've now got the integral seconds part of the time code (we hope).
696 * The fractional part comes from the table. We next compute
697 * the offsets for each character.
698 */
699 for (i = 0; i < NCHUCHARS; i++) {
700 register u_long tmp2;
701
702 off[i].l_ui = date_ui;
703 off[i].l_uf = chutable[i];
704 tmp = chuc->codetimes[i].tv_sec + JAN_1970;
705 TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
706 M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
707 }
708
709 /*
710 * Here is a *big* problem. What one would normally
711 * do here on a machine with lots of clock bits (say
712 * a Vax or the gizmo board) is pick the most positive
713 * offset and the estimate, since this is the one that
714 * is most likely suffered the smallest interrupt delay.
715 * The trouble is that the low order clock bit on an IBM
716 * RT, which is the machine I had in mind when doing this,
717 * ticks at just under the millisecond mark. This isn't
718 * precise enough. What we can do to improve this is to
719 * average all 10 samples and rely on the second level
720 * filtering to pick the least delayed estimate. Trouble
721 * is, this means we have to divide a 64 bit fixed point
722 * number by 10, a procedure which really sucks. Oh, well.
723 * First compute the sum.
724 */
725 date_ui = 0;
726 tmp = 0;
727 for (i = 0; i < NCHUCHARS; i++)
728 M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
729 if (M_ISNEG(date_ui, tmp))
730 isneg = 1;
731 else
732 isneg = 0;
733
734 /*
735 * Here is a multiply-by-0.1 optimization that should apply
736 * just about everywhere. If the magnitude of the sum
737 * is less than 9 we don't have to worry about overflow
738 * out of a 64 bit product, even after rounding.
739 */
740 if (date_ui < 9 || date_ui > 0xfffffff7) {
741 register u_long prod_ui;
742 register u_long prod_uf;
743
744 prod_ui = prod_uf = 0;
745 /*
746 * This code knows the low order bit in 0.1 is zero
747 */
748 for (i = 1; i < NZPOBITS; i++) {
749 M_LSHIFT(date_ui, tmp);
750 if (ZEROPTONE & (1<<i))
751 M_ADD(prod_ui, prod_uf, date_ui, tmp);
752 }
753
754 /*
755 * Done, round it correctly. Prod_ui contains the
756 * fraction.
757 */
758 if (prod_uf & 0x80000000)
759 prod_ui++;
760 if (isneg)
761 date_ui = 0xffffffff;
762 else
763 date_ui = 0;
764 tmp = prod_ui;
765 /*
766 * date_ui is integral part, tmp is fraction.
767 */
768 } else {
769 register u_long prod_ovr;
770 register u_long prod_ui;
771 register u_long prod_uf;
772 register u_long highbits;
773
774 prod_ovr = prod_ui = prod_uf = 0;
775 if (isneg)
776 highbits = 0xffffffff; /* sign extend */
777 else
778 highbits = 0;
779 /*
780 * This code knows the low order bit in 0.1 is zero
781 */
782 for (i = 1; i < NZPOBITS; i++) {
783 M_LSHIFT3(highbits, date_ui, tmp);
784 if (ZEROPTONE & (1<<i))
785 M_ADD3(prod_ovr, prod_uf, prod_ui,
786 highbits, date_ui, tmp);
787 }
788
789 if (prod_uf & 0x80000000)
790 M_ADDUF(prod_ovr, prod_ui, (u_long)1);
791 date_ui = prod_ovr;
792 tmp = prod_ui;
793 }
794
795 /*
796 * At this point we have the mean offset, with the integral
797 * part in date_ui and the fractional part in tmp. Store
798 * it in the structure.
799 */
800 /*
801 * Add in fudge factor.
802 */
803 M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
804
805 /*
806 * Find the minimun and maximum offset
807 */
808 imin = imax = 0;
809 for (i = 1; i < NCHUCHARS; i++) {
810 if (L_ISGEQ(&off[i], &off[imax])) {
811 imax = i;
812 } else if (L_ISGEQ(&off[imin], &off[i])) {
813 imin = i;
814 }
815 }
816
817 L_ADD(&off[imin], &offset_fudge);
818 if (imin != imax)
819 L_ADD(&off[imax], &offset_fudge);
820 (void) printf("mean %s, min %s, max %s\n",
821 mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
822 lfptoa(&off[imax], 8));
823 }
824