1 /* $NetBSD: chutest.c,v 1.5 2016/01/08 21:35:35 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 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 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 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 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 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 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 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 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