1 /* Copyright (C) 1993, 1992 Nathan Sidwell */ 2 /* RCS $Id: timer.c,v 4.10 1995/12/22 11:56:52 nathan Exp $ */ 3 /*{{{ the problem with real time under unix*/ 4 /* 5 * Getting accurate, stable timing is difficult. An auto repeating 6 * interrupt cannot be used, because that has rude behaviour, if your 7 * slightly too slow. So we must fire off the next IRQ ourselves, 8 * preferably inside the interrupt routine. But, there will be some 9 * slippage, due to the time for the kernel to deliver the interrupt, and 10 * us to set off the next one. 11 * A clever way of getting accurate average behaviour is to keep a 12 * record of how long the previous IRQ actually took, and use that to 13 * generate a correction factor, to be used for the next IRQ request. 14 * But, that doesn't converge, because it's fighting the granularity of the 15 * kernel's preemptive scheduler. For instance, if we want a rate of 16 * 37mS, but the scheduling is in 10mS chunks, we'll be (at least) 17 * 3mS too slow, we adjust the delta to be -3 (34mS), and next time we're 18 * also 3mS too slow, so we adjust it again to -6 (31mS), and the same 19 * happens, so its now a delta of -9 (28mS). This time we're 20 * 7mS too fast so the delta changes again to -2 (35mS). As you can 21 * see, the delta keeps changing. On average the timing should be ok, but 22 * some frames will be too fast and some too slow, which gives 23 * jerky animation. 24 * Another thing which exacerbates this is that the IRQ is set in uS, but 25 * the time can only be go to mS accuracy. 26 * So you have a choice, smooth animation, but not quite accurate, or 27 * jerky animation, but accurate on average. I've elected for smooth 28 * animation. 29 * 30 * Now to complicate things further. The elapsed game time is now 31 * kept, on a per screen basis. Because the game can be paused and there 32 * are the history screens, the clock cannot be simply used. I could count 33 * animation frames and scale by the interrupt time, but this will always 34 * underestimate (due to the slipage above), and may be completely wrong 35 * because the interrupts are being missed (but see below). 36 * Most systems will have a gettimeofday which gives usec precision of the 37 * clock (but not usec accuracy wrt real time, but should be good enough 38 * relative to itself). I use this, stopping and starting at appropriate 39 * points, to get the elapsed time. 40 * 41 * In order to get meaningful score comparisons between different 42 * hardware, I measure the percentage of missed frames. If this gets too 43 * large, I use the error factor to increase the frame time, and 44 * scale the scores downwards appropriately. The actual frame time 45 * should converge asymtotically to the minimum that the hardware 46 * can support, if the hardware is not fast enough for the game. 47 * 48 * And now to complicate things further still, some OS's round the 49 * alarm time downwards, resulting in the signal arriving earlier than 50 * expected. To cope with these, I have added the busywait code. 51 * During the score scaling code, the mean frame time is examined, and 52 * if too short, the busy wait code is activated. This code can be 53 * forced by using the busywait flag. 54 */ 55 /*}}}*/ 56 #include "xmris.h" 57 #ifdef TRANSPUTER 58 #include <process.h> 59 #else 60 #include <sys/time.h> 61 #include <signal.h> 62 #endif 63 #ifdef SVR4 64 #ifndef SYSV 65 #define SYSV 66 #endif 67 #endif 68 #define BUSYWAIT 69 /*{{{ signal_hold, signal_release & signal_pause*/ 70 /* signal_hold blocks the supplied signal and returns a value to be used by 71 * signal_pause and signal_release 72 * signal_pause uses the value supplied to return to the signal mask inuse 73 * before the preceeding signal_hold, waits for a signal, then 74 * reblocks the signal 75 * signal_release returns the block mask to what it was before signal_hold 76 */ 77 #ifndef TRANSPUTER 78 #ifdef POSIX 79 # define MASK sigset_t 80 # define signal_hold(signal, maskp) \ 81 {\ 82 MASK temp; \ 83 sigemptyset(&temp); \ 84 sigaddset(&temp, signal); \ 85 sigprocmask(SIG_BLOCK, &temp, (maskp)); \ 86 } 87 # define signal_release(maskp) sigprocmask(SIG_SETMASK, maskp, (MASK *)NULL) 88 # define signal_pause(maskp) sigsuspend(maskp) 89 #else 90 # ifdef __hpux /* hpux is a weird mixture of BSD & SYSV */ 91 /* don't know if this is right */ 92 # define MASK int 93 # define signal_hold(signal, maskp) \ 94 (*(maskp) = sigblock(sigmask(signal))) 95 # define signal_release(maskp) sigsetmask(*(maskp)) 96 # define signal_pause(maskp) sigpause(*(maskp)) 97 # else 98 # ifdef SYSV 99 /* the signals are already masks, use the supplied signal number 100 * for the returned mask, to toggle the blocked mask. 101 * sigpause does not automatically block the signal again, so that must be 102 * done. 103 */ 104 # define MASK int 105 # define signal_hold(signal, maskp) \ 106 (*(maskp) = ((void)sighold(signal), signal)) 107 # define signal_release(maskp) sigrelse(*(maskp)) 108 # define signal_pause(maskp) (sigpause(*(maskp)), sighold(*(maskp))) 109 # define USESIGSET 110 # else 111 /* signals are bit numbers, so we must construct the bit mask. 112 * hold returns the previous mask, so we can use that for pause and release 113 * sigpause reblocks the signal after being signalled, so no need to 114 * reblock it 115 */ 116 # define MASK int 117 # define signal_hold(signal, maskp) \ 118 (*(maskp) = sigblock(sigmask(signal))) 119 # define signal_release(maskp) sigsetmask(*(maskp)) 120 # define signal_pause(maskp) sigpause(*(maskp)) 121 # endif /* SYSV */ 122 # endif /* __hpux */ 123 #endif /* POSIX */ 124 #endif /* TRANSPUTER */ 125 /*}}}*/ 126 /*{{{ get current time*/ 127 /* TICKTIME specifies how many microseconds in each timer tick 128 * tick_t is the typedef of the timer return value 129 * BUSYWAIT is set if the timer is precise enough for busywait code 130 * to work 131 */ 132 #ifdef TRANSPUTER 133 # define gettick(ptr) (*(ptr) = ProcTime()) 134 # define TICKTIME (unsigned long)64 135 # define tickdelta(later, earlier) ProcTimeMinus(later, earlier) 136 # define tickafter(later, earlier) ProcTimerAfter(later, earlier) 137 # define tickadd(time, interval) ProcTimePlus(time, interval) 138 typedef int tick_t; 139 #else 140 # ifdef USETIME 141 # define gettick(ptr) (*(ptr) = time((time_t *)NULL)) 142 # define TICKTIME (unsigned long)1000000 143 # define tickdelta(later, earlier) (unsigned long)((later) - (earlier)) 144 # define tickadd(time, interval) ((time) + (interval)) 145 # define tickafter(later, earlier) \ 146 (tickdelta(later, earlier) < ~(~(unsigned long)0 >> 1)) 147 typedef time_t tick_t; 148 # undef BUSYWAIT 149 # else 150 /* say whether gettimeofday needs a timezone argument 151 * as far as I know only SYSV doesn't -- this needs checking 152 */ 153 #ifdef POSIX 154 # define TIMEZONE struct timezone 155 #else 156 # ifdef __hpux 157 # define TIMEZONE struct timezone 158 # else 159 # ifdef SYSV 160 # define TIMEZONE VOID 161 # else 162 # define TIMEZONE struct timezone 163 # endif /* SYSV */ 164 # endif /* __hpux */ 165 #endif /* POSIX */ 166 # define TICKTIME (unsigned long)1000 167 # define gettick(ptr) \ 168 {\ 169 struct timeval timeofday; \ 170 gettimeofday(&timeofday, (TIMEZONE *)NULL); \ 171 *(ptr) = (unsigned long)timeofday.tv_sec * (1000000 / TICKTIME) + \ 172 (unsigned long)timeofday.tv_usec / (1000000 / TICKTIME); \ 173 } 174 # define tickdelta(later, earlier) ((later) - (earlier)) 175 # define tickadd(time, interval) ((time) + (interval)) 176 # define tickafter(later, earlier) \ 177 (tickdelta(later, earlier) < ~(~(unsigned long)0 >> 1)) 178 typedef unsigned long tick_t; 179 # endif /* USETIME */ 180 #endif /* TRANSPUTER */ 181 /*}}}*/ 182 /*{{{ timer*/ 183 static struct 184 { 185 VOIDFUNC (*handler) PROTOARG((int)); /* original handler */ 186 unsigned long usec; /* interval time in usec */ 187 #ifdef TRANSPUTER 188 tick_t delay; /* tickdelay waiting */ 189 tick_t timeout; /* when the next one should timeout */ 190 #else 191 struct itimerval interval; /* internal interval time */ 192 unsigned VOLATILE elapsed; /* timer elapsed */ 193 unsigned VOLATILE waiting; /* waiting for the interrupt */ 194 #ifdef BUSYWAIT 195 unsigned busywait; /* busywaiting is turned on */ 196 unsigned VOLATILE restarted; /* restarted in signal handler */ 197 tick_t timeout; /* timeout */ 198 tick_t delay; /* interval delay */ 199 #endif /* BUSYWAIT */ 200 #endif /* TRANSPUTER */ 201 unsigned state; /* timing state */ 202 tick_t game; /* start of game tick */ 203 tick_t start; /* timing start */ 204 tick_t stop; /* timing stop */ 205 unsigned count; /* frame count */ 206 unsigned missed; /* missed count */ 207 } timer; 208 /*}}}*/ 209 #ifndef NDEBUG 210 /*{{{ debug*/ 211 /*{{{ typedef struct*/ 212 typedef struct 213 { 214 char CONST *text; 215 unsigned elapsed; 216 unsigned waiting; 217 unsigned restarted; 218 } RECORD; 219 /*}}}*/ 220 static RECORD list[64]; 221 static unsigned next; 222 /*}}}*/ 223 #endif 224 /*{{{ prototypes*/ 225 #ifndef TRANSPUTER 226 static VOIDFUNC timer_alarm PROTOARG((int)); 227 #endif /* TRANSPUTER */ 228 #ifndef NDEBUG 229 static VOIDFUNC timer_debug PROTOARG((char CONST *)); 230 #endif 231 /*}}}*/ 232 #ifdef TRANSPUTER 233 /*{{{ void sleep(delay)*/ 234 extern VOIDFUNC sleep 235 FUNCARG((delay), 236 unsigned delay 237 ) 238 { 239 ProcWait((int)(delay * (1000000 / TICKTIME))); 240 return; 241 } 242 /*}}}*/ 243 #endif /* TRANSPUTER */ 244 #ifndef TRANSPUTER 245 /*{{{ void timer_alarm(sig)*/ 246 static VOIDFUNC timer_alarm 247 /* ARGSUSED */ 248 FUNCARG((sig), 249 int sig 250 ) 251 { 252 /* 253 * Most calls are undefined in a signal handler 254 * (only signal, exit, longjump & abort are guaranteed to work) 255 * This should work, except on _really_ weird library implementations, 256 * because timer.waiting is only true when the main thread is stuck 257 * in a wait() call. 258 */ 259 #ifndef NDEBUG 260 timer_debug("In handler"); 261 #endif 262 #ifndef USESIGSET 263 signal(SIGALRM, timer_alarm); 264 #endif 265 timer.elapsed = !timer.waiting; 266 if(timer.waiting) 267 { 268 timer.waiting = 0; 269 #ifdef BUSYWAIT 270 if(timer.busywait) 271 { 272 tick_t now; 273 274 gettick(&now); 275 if(tickafter(now, timer.timeout)) 276 { 277 timer.timeout = tickadd(timer.timeout, timer.delay); 278 timer.restarted = 1; 279 setitimer(ITIMER_REAL, &timer.interval, 280 (struct itimerval *)NULL); 281 } 282 } 283 else 284 { 285 timer.restarted = 1; 286 #else 287 { 288 #endif 289 setitimer(ITIMER_REAL, &timer.interval, 290 (struct itimerval *)NULL); 291 } 292 } 293 return; 294 } 295 /*}}}*/ 296 #endif /* TRANSPUTER */ 297 /*{{{ void timer_close()*/ 298 extern VOIDFUNC timer_close FUNCARGVOID 299 /* 300 * closes the timer stuff 301 */ 302 { 303 #ifndef TRANSPUTER 304 if(data.busywait == False) 305 { 306 MASK mask; 307 308 signal_hold(SIGALRM, &mask); 309 timer.interval.it_value.tv_usec = 0; 310 timer.interval.it_interval.tv_usec = 0; 311 while(!timer.elapsed) 312 signal_pause(&mask); 313 signal_release(&mask); 314 setitimer(ITIMER_REAL, &timer.interval, (struct itimerval *)NULL); 315 #ifdef USESIGSET 316 sigset(SIGALRM, timer.handler); 317 #else 318 signal(SIGALRM, timer.handler); 319 #endif /* USESIGSET */ 320 } 321 #endif /* TRANSPUTER */ 322 return; 323 } 324 /*}}}*/ 325 #ifndef NDEBUG 326 /*{{{ static void timer_debug(text)*/ 327 static VOIDFUNC timer_debug 328 FUNCARG((text), 329 char CONST *text 330 ) 331 { 332 RECORD *ptr; 333 334 ptr = &list[next]; 335 if(next++ == 63) 336 next = 0; 337 ptr->text = text; 338 ptr->elapsed = timer.elapsed; 339 ptr->waiting = timer.waiting; 340 ptr->restarted = timer.restarted; 341 return; 342 } 343 /*}}}*/ 344 #endif 345 /*{{{ void timer_open()*/ 346 extern VOIDFUNC timer_open FUNCARGVOID 347 /* 348 * initialize the timer stuff 349 * this means installing the alarm signal handler 350 * and starting the first tick 351 */ 352 { 353 #ifdef TRANSPUTER 354 assert(ProcGetPriority()); 355 gettick(&timer.timeout); 356 #else 357 if(data.busywait != False) 358 # ifdef BUSYWAIT 359 { 360 timer.busywait = 1; 361 gettick(&timer.timeout); 362 } 363 # else 364 { 365 fprintf(stderr, "Busywait code not included.\n"); 366 data.busywait = False; 367 } 368 # endif /* BUSYWAIT */ 369 timer.interval.it_interval.tv_sec = 0; 370 timer.interval.it_interval.tv_usec = 0; 371 timer.interval.it_value.tv_sec = 0; 372 timer.interval.it_value.tv_usec = 0; 373 if(data.busywait == False) 374 #ifdef USESIGSET 375 timer.handler = sigset(SIGALRM, timer_alarm); 376 #else 377 timer.handler = signal(SIGALRM, timer_alarm); 378 #endif /* USESIGSET */ 379 timer.waiting = 0; 380 timer.elapsed = 1; 381 #endif /* TRANSPUTER */ 382 #ifndef NDEBUG 383 timer_debug("Init"); 384 #endif 385 global.missed = 0; 386 global.dilation = FRAME_SCALE; 387 global.scale = SCORE_SCALE; 388 return; 389 } 390 /*}}}*/ 391 /*{{{ int timer_set(tick, state)*/ 392 extern unsigned timer_set 393 FUNCARG((tick, state), 394 unsigned long tick 395 ARGSEP unsigned state 396 ) 397 /* 398 * sets the timer tick and fiddles the timer state 399 * if the tick is zero, then the timer state only is altered 400 */ 401 { 402 unsigned value; 403 404 if(tick) 405 { 406 { 407 /* stupid compilers with broken stringizizing 408 * and stupid compilers which don't understand \ 409 * outside of #define */ 410 unsigned t1, t2; 411 412 t1 = timer.state == TIMING_OFF || timer.state == TIMING_PAUSE; 413 t2 = state == TIMING_OFF || state == TIMING_PAUSE; 414 assert(t1 || t2); 415 } 416 timer.usec = tick; 417 #ifdef TRANSPUTER 418 timer.delay = (tick_t)(tick / TICKTIME); 419 #else 420 #ifdef BUSYWAIT 421 timer.delay = (tick_t)(tick / TICKTIME); 422 #endif /* BUSYWAIT */ 423 timer.interval.it_value.tv_usec = tick; 424 #endif /* TRASNPUTER */ 425 } 426 value = timer.state; 427 switch(state) 428 { 429 /*{{{ case TIMING_ON:*/ 430 case TIMING_ON: 431 { 432 if(timer.state == TIMING_OFF) 433 global.msec = 0; 434 if(timer.state != TIMING_ON) 435 { 436 gettick(&timer.start); 437 if(timer.state == TIMING_OFF) 438 timer.game = timer.start; 439 else 440 { 441 tick_t now; 442 443 gettick(&now); 444 timer.game += tickdelta(now, timer.stop); 445 } 446 timer.state = TIMING_ON; 447 timer.count = 0; 448 } 449 break; 450 } 451 /*}}}*/ 452 /*{{{ case TIMING_OFF:*/ 453 case TIMING_OFF: 454 { 455 if(timer.state != TIMING_OFF) 456 { 457 if(timer.state == TIMING_ON) 458 gettick(&timer.stop); 459 global.msec = tickdelta(timer.stop, timer.game) * 460 TICKTIME / (unsigned long)1000; 461 timer.state = TIMING_OFF; 462 } 463 break; 464 } 465 /*}}}*/ 466 /*{{{ case TIMING_PAUSE:*/ 467 case TIMING_PAUSE: 468 { 469 if(timer.state == TIMING_ON) 470 { 471 timer.state = TIMING_PAUSE; 472 gettick(&timer.stop); 473 } 474 break; 475 } 476 /*}}}*/ 477 default: 478 assert(0); 479 } 480 return value; 481 } 482 /*}}}*/ 483 /*{{{ void timer_wait()*/ 484 extern VOIDFUNC timer_wait FUNCARGVOID 485 /* 486 * waits for the next timer interrupt 487 * if this has already gone by, then we immediately return 488 * If we arrive here before the interrupt, the interrupt is retriggered 489 * in the signal handler (timer_alarm), for minimum slipage. If 490 * we're too late, then the interrupt has to be restarted here. (If 491 * the signal handler did it, things rapidly get out of hand.) 492 * 493 * the transputer code is realy simple, and doesn't give ANY slippage 494 * provided the system is fast enough. If too slow, slip is inserted 495 * as required. 496 */ 497 { 498 int point; 499 500 #ifdef TRANSPUTER 501 /*{{{ wait for it*/ 502 { 503 tick_t now; 504 505 gettick(&now); 506 if(timeafter(now, timer.timeout)) 507 { 508 point = 1; 509 timer.timeout = now; 510 timer.missed++; 511 } 512 else 513 { 514 point = -1; 515 ProcAfter(timer.timeout); 516 } 517 timer.timeout = tickadd(timer.timeout, timer.delay); 518 } 519 /*}}}*/ 520 #else 521 { 522 MASK mask; 523 524 signal_hold(SIGALRM, &mask); 525 if(!timer.elapsed && data.busywait == False) 526 { 527 point = -1; 528 #ifndef NDEBUG 529 timer_debug("Starting wait"); 530 #endif 531 timer.waiting = 1; 532 while(timer.waiting) 533 signal_pause(&mask); 534 #ifndef NDEBUG 535 timer_debug("Done wait"); 536 #endif 537 #ifdef BUSYWAIT 538 if(!timer.restarted) 539 point = 0; 540 timer.restarted = 0; 541 #endif /* BUSYWAIT */ 542 } 543 else 544 { 545 timer.elapsed = 0; 546 #ifdef BUSYWAIT 547 if(timer.busywait) 548 point = 0; 549 else 550 #endif /* BUSYWAIT */ 551 { 552 point = 1; 553 setitimer(ITIMER_REAL, &timer.interval, (struct itimerval *)NULL); 554 timer.missed++; 555 } 556 } 557 signal_release(&mask); 558 } 559 #ifdef BUSYWAIT 560 /*{{{ busywait?*/ 561 if(!point) 562 { 563 tick_t now; 564 565 gettick(&now); 566 if(tickafter(now, timer.timeout)) 567 { 568 point = 1; 569 timer.missed++; 570 timer.timeout = tickadd(now, timer.delay); 571 } 572 else 573 { 574 while(!tickafter(now, timer.timeout)) 575 { 576 usleep(tickdelta(timer.timeout, now) * TICKTIME); 577 gettick(&now); 578 } 579 timer.timeout = tickadd(timer.timeout, timer.delay); 580 point = -1; 581 } 582 if(data.busywait == False) 583 setitimer(ITIMER_REAL, &timer.interval, (struct itimerval *)NULL); 584 } 585 /*}}}*/ 586 #endif /* BUSYWAIT */ 587 #endif /* TRANSPUTER */ 588 if(timer.state != TIMING_ON) 589 /* EMPTY */; 590 else if(!timer.count) 591 { 592 timer.missed = 0; 593 gettick(&timer.start); 594 } 595 else if(timer.count == FRAME_RATIO_UPDATE) 596 { 597 unsigned dilation; 598 int xold, xnew; 599 unsigned long usec; 600 601 gettick(&timer.stop); 602 usec = (unsigned long)tickdelta(timer.stop, timer.start) * TICKTIME; 603 dilation = (unsigned)(usec / FRAME_RATIO_UPDATE * 604 FRAME_SCALE / timer.usec); 605 #ifndef TRANSPUTER 606 #ifdef BUSYWAIT 607 if(!timer.busywait && dilation * 100 < FRAME_SCALE * 97) 608 { 609 fprintf(stderr, "%s:Timing too quick, using partial busywait.\n", 610 myname); 611 timer.timeout = tickadd(timer.stop, timer.delay); 612 timer.busywait = 1; 613 } 614 #endif /* BUSYWAIT */ 615 #endif /* TRANSPUTER */ 616 if(dilation <= FRAME_SCALE) 617 dilation = FRAME_SCALE; 618 else if(timer.missed <= (global.dilation == FRAME_SCALE ? 619 FRAME_MISS_START : FRAME_MISS_STOP)) 620 dilation = FRAME_SCALE; 621 xold = WINDOW_WIDTH - (int)((unsigned long)WINDOW_WIDTH * 622 FRAME_SCALE / global.dilation); 623 xnew = WINDOW_WIDTH - (int)((unsigned long)WINDOW_WIDTH * 624 FRAME_SCALE / dilation); 625 /*{{{ swap?*/ 626 if(xold > xnew) 627 { 628 int t; 629 630 t = xold; 631 xold = xnew; 632 xnew = t; 633 } 634 /*}}}*/ 635 XDrawLine(display.display, display.window, GCN(GC_LOAD), 636 xold, PIXELY(CELLS_DOWN, CELL_HEIGHT), 637 xnew, PIXELY(CELLS_DOWN, CELL_HEIGHT)); 638 global.dilation = dilation; 639 timer.start = timer.stop; 640 timer.missed = 0; 641 timer.count = 1; 642 /*{{{ set score scale*/ 643 { 644 unsigned long scale; 645 unsigned last; 646 647 scale = (unsigned long)SCORE_SCALE * SCORE_SCALE * 648 FRAME_SCALE / global.dilation; 649 dilation = SCORE_SCALE; 650 do 651 { 652 last = dilation; 653 dilation = (unsigned)(((unsigned long)dilation * dilation + 654 scale) / dilation / 2); 655 } 656 while(dilation != last); 657 global.scale = dilation; 658 } 659 /*}}}*/ 660 } 661 timer.count++; 662 /*{{{ plot load point?*/ 663 if(point < 0) 664 { 665 if(global.missed) 666 { 667 XDrawPoint(display.display, display.window, GCN(GC_LOAD), 668 (int)(WINDOW_WIDTH - global.missed), 669 PIXELY(CELLS_DOWN, CELL_HEIGHT)); 670 global.missed--; 671 } 672 } 673 else if(point > 0) 674 { 675 if(global.missed < WINDOW_WIDTH) 676 { 677 global.missed++; 678 XDrawPoint(display.display, display.window, GCN(GC_LOAD), 679 (int)(WINDOW_WIDTH - global.missed), 680 PIXELY(CELLS_DOWN, CELL_HEIGHT)); 681 } 682 } 683 /*}}}*/ 684 return; 685 } 686 /*}}}*/ 687