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