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