root/src/simh/sim_timer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _compute_minimum_sleep
  2. sim_idle_ms_sleep
  3. sim_os_set_thread_priority
  4. sim_os_set_thread_priority
  5. sim_os_msec
  6. sim_os_sleep
  7. sim_timer_exit
  8. sim_os_ms_sleep_init
  9. sim_os_ms_sleep
  10. sim_os_msec
  11. sim_os_sleep
  12. sim_os_ms_sleep_init
  13. sim_os_ms_sleep
  14. sim_timespec_diff
  15. sim_rtcn_init_all
  16. sim_rtcn_init
  17. sim_rtcn_init_unit
  18. sim_rtcn_calb
  19. sim_timer_init
  20. sim_show_timers
  21. sim_show_clock_queues
  22. sim_timer_clr_catchup
  23. sim_timer_set_catchup
  24. sim_timer_show_catchup
  25. sim_timer_tick_svc
  26. win32_usleep
  27. sim_usleep
  28. _timespec_to_double
  29. _double_to_timespec
  30. sim_timer_clock_tick_svc
  31. _rtcn_configure_calibrated_clock
  32. sim_timer_clock_reset
  33. sim_start_timer_services
  34. sim_stop_timer_services
  35. sim_timer_inst_per_sec
  36. sim_timer_activate
  37. sim_timer_activate_after
  38. sim_register_clock_unit_tmr
  39. _sim_coschedule_cancel

   1 /*
   2  * sim_timer.c: simulator timer library
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: de3abd63-f62a-11ec-a888-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 1993-2010 Robert M. Supnik
  11  * Copyright (c) 2021-2024 The DPS8M Development Team
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a
  14  * copy of this software and associated documentation files (the "Software"),
  15  * to deal in the Software without restriction, including without limitation
  16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17  * and/or sell copies of the Software, and to permit persons to whom the
  18  * Software is furnished to do so, subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included
  21  * in all copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  26  * IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR
  27  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  28  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  29  * OTHER DEALINGS IN THE SOFTWARE.
  30  *
  31  * Except as contained in this notice, the name of Robert M. Supnik shall
  32  * not be used in advertising or otherwise to promote the sale, use or
  33  * other dealings in this Software without prior written authorization from
  34  * Robert M. Supnik.
  35  *
  36  * ---------------------------------------------------------------------------
  37  */
  38 
  39 /*
  40  * This library includes the following routines:
  41  *
  42  * sim_timer_init -         initialize timing system
  43  * sim_os_msec  -           return elapsed time in msec
  44  * sim_os_sleep -           sleep specified number of seconds
  45  * sim_os_ms_sleep -        sleep specified number of milliseconds
  46  * sim_idle_ms_sleep -      sleep specified number of milliseconds
  47  *                          or until awakened by an asynchronous
  48  *                          event
  49  * sim_timespec_diff        subtract two timespec values
  50  * sim_timer_activate_after schedule unit for specific time
  51  */
  52 
  53 #include "sim_defs.h"
  54 #include <ctype.h>
  55 #include <math.h>
  56 
  57 #define SIM_INTERNAL_CLK (SIM_NTIMERS+(1<<30))
  58 #define SIM_INTERNAL_UNIT sim_internal_timer_unit
  59 
  60 #if defined(MIN)
  61 # undef MIN
  62 #endif /* if defined(MIN) */
  63 #define MIN(a,b)  (((a) < (b)) ? (a) : (b))
  64 
  65 #if defined(MAX)
  66 # undef MAX
  67 #endif /* if defined(MAX) */
  68 #define MAX(a,b)  (((a) > (b)) ? (a) : (b))
  69 
  70 uint32 sim_idle_ms_sleep (unsigned int msec);
  71 
  72 static int32 sim_calb_tmr           = -1;     /* the system calibrated timer */
  73 static int32 sim_calb_tmr_last      = -1;     /* shadow value when at sim> prompt */
  74 static double sim_inst_per_sec_last =  0;     /* shadow value when at sim> prompt */
  75 
  76 static uint32 sim_idle_rate_ms                           = 0;
  77 static uint32 sim_os_sleep_min_ms                        = 0;
  78 static uint32 sim_os_sleep_inc_ms                        = 0;
  79 static uint32 sim_os_clock_resoluton_ms                  = 0;
  80 static uint32 sim_os_tick_hz                             = 0;
  81 static uint32 sim_idle_calib_pct                         = 0;
  82 static UNIT *sim_clock_unit[SIM_NTIMERS+1]               = {NULL};
  83 UNIT   * volatile sim_clock_cosched_queue[SIM_NTIMERS+1] = {NULL};
  84 static int32 sim_cosched_interval[SIM_NTIMERS+1];
  85 static t_bool sim_catchup_ticks                          = FALSE;
  86 
  87 #define sleep1Samples       10
  88 
  89 static uint32 _compute_minimum_sleep (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91 uint32 i, tot, tim;
  92 
  93 sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
  94 sim_idle_ms_sleep (1);              /* Start sampling on a tick boundary */
  95 for (i = 0, tot = 0; i < sleep1Samples; i++)
  96     tot += sim_idle_ms_sleep (1);
  97 tim = tot / sleep1Samples;          /* Truncated average */
  98 sim_os_sleep_min_ms = tim;
  99 sim_idle_ms_sleep (1);              /* Start sampling on a tick boundary */
 100 for (i = 0, tot = 0; i < sleep1Samples; i++)
 101     tot += sim_idle_ms_sleep (sim_os_sleep_min_ms + 1);
 102 tim = tot / sleep1Samples;          /* Truncated average */
 103 sim_os_sleep_inc_ms = tim - sim_os_sleep_min_ms;
 104 sim_os_set_thread_priority (PRIORITY_NORMAL);
 105 return sim_os_sleep_min_ms;
 106 }
 107 
 108 uint32 sim_idle_ms_sleep (unsigned int msec)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110 return sim_os_ms_sleep (msec);
 111 }
 112 
 113 #if defined(_WIN32)
 114 /* On Windows there are several potentially disjoint threading APIs */
 115 /* in use (base win32 pthreads, libSDL provided threading, and direct */
 116 /* calls to beginthreadex), so go directly to the Win32 threading APIs */
 117 /* to manage thread priority */
 118 t_stat sim_os_set_thread_priority (int below_normal_above)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120 const static int val[3] = {THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL};
 121 
 122 if ((below_normal_above < -1) || (below_normal_above > 1))
 123     return SCPE_ARG;
 124 SetThreadPriority (GetCurrentThread(), val[1 + below_normal_above]);
 125 return SCPE_OK;
 126 }
 127 #else
 128 /* Native pthreads priority implementation */
 129 t_stat sim_os_set_thread_priority (int below_normal_above)
     /* [previous][next][first][last][top][bottom][index][help] */
 130 {
 131 int sched_policy, min_prio, max_prio;
 132 struct sched_param sched_priority;
 133 
 134 # if !defined(__gnu_hurd__)
 135 if ((below_normal_above < -1) || (below_normal_above > 1))
 136     return SCPE_ARG;
 137 
 138 pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority);
 139 #  if !defined(__PASE__)
 140 min_prio = sched_get_priority_min(sched_policy);
 141 max_prio = sched_get_priority_max(sched_policy);
 142 #  else
 143 min_prio = 1;
 144 max_prio = 127;
 145 #  endif /* if !defined(__PASE__) */
 146 switch (below_normal_above) {
 147     case PRIORITY_BELOW_NORMAL:
 148         sched_priority.sched_priority = min_prio;
 149         break;
 150     case PRIORITY_NORMAL:
 151         sched_priority.sched_priority = (max_prio + min_prio) / 2;
 152         break;
 153     case PRIORITY_ABOVE_NORMAL:
 154         sched_priority.sched_priority = max_prio;
 155         break;
 156     }
 157 pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
 158 # endif /* if !defined(__gnu_hurd__) */
 159 return SCPE_OK;
 160 }
 161 #endif
 162 
 163 /* OS-dependent timer and clock routines */
 164 
 165 #if defined (_WIN32)
 166 
 167 /* Win32 routines */
 168 
 169 const t_bool rtc_avail = TRUE;
 170 
 171 uint32 sim_os_msec (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 172 {
 173 return timeGetTime ();
 174 }
 175 
 176 void sim_os_sleep (unsigned int sec)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178 Sleep (sec * 1000);
 179 return;
 180 }
 181 
 182 void sim_timer_exit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 183 {
 184 timeEndPeriod (sim_idle_rate_ms);
 185 return;
 186 }
 187 
 188 uint32 sim_os_ms_sleep_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 189 {
 190 TIMECAPS timers;
 191 
 192 if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR)
 193     return 0;
 194 if (timers.wPeriodMin == 0)
 195     return 0;
 196 if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR)
 197     return 0;
 198 atexit (sim_timer_exit);
 199 /* return measured actual minimum sleep time */
 200 return _compute_minimum_sleep ();
 201 }
 202 
 203 uint32 sim_os_ms_sleep (unsigned int msec)
     /* [previous][next][first][last][top][bottom][index][help] */
 204 {
 205 uint32 stime = sim_os_msec();
 206 
 207 Sleep (msec);
 208 return sim_os_msec () - stime;
 209 }
 210 
 211 #else
 212 
 213 /* UNIX routines */
 214 
 215 # include <time.h>
 216 # include <sys/time.h>
 217 # include <signal.h>
 218 # include <unistd.h>
 219 # define NANOS_PER_MILLI     1000000
 220 # define MILLIS_PER_SEC      1000
 221 
 222 const t_bool rtc_avail = TRUE;
 223 
 224 uint32 sim_os_msec (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226 struct timeval cur;
 227 struct timezone foo;
 228 int st1ret;
 229 uint32 msec;
 230 
 231 st1ret = gettimeofday (&cur, &foo);
 232   if (st1ret != 0)
 233     {
 234       fprintf (stderr, "\rFATAL: gettimeofday failure! Aborting at %s[%s:%d]\r\n",
 235                __func__, __FILE__, __LINE__);
 236 # if defined(USE_BACKTRACE)
 237 #  if defined(SIGUSR2)
 238       (void)raise(SIGUSR2);
 239       /*NOTREACHED*/ /* unreachable */
 240 #  endif /* if defined(SIGUSR2) */
 241 # endif /* if defined(USE_BACKTRACE) */
 242       abort();
 243     }
 244 msec = (((uint32) cur.tv_sec) * 1000UL) + (((uint32) cur.tv_usec) / 1000UL);
 245 return msec;
 246 }
 247 
 248 void sim_os_sleep (unsigned int sec)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250 sleep (sec);
 251 return;
 252 }
 253 
 254 uint32 sim_os_ms_sleep_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 255 {
 256 return _compute_minimum_sleep ();
 257 }
 258 
 259 uint32 sim_os_ms_sleep (unsigned int milliseconds)
     /* [previous][next][first][last][top][bottom][index][help] */
 260 {
 261 uint32 stime = sim_os_msec ();
 262 struct timespec treq;
 263 
 264 treq.tv_sec  = milliseconds / MILLIS_PER_SEC;
 265 treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI;
 266 (void) nanosleep (&treq, NULL);
 267 return sim_os_msec () - stime;
 268 }
 269 
 270 #endif
 271 
 272 /* diff = min - sub */
 273 void
 274 sim_timespec_diff (struct timespec *diff, const struct timespec *min, struct timespec *sub)
     /* [previous][next][first][last][top][bottom][index][help] */
 275 {
 276 /* move the minuend value to the difference and operate there. */
 277 *diff = *min;
 278 /* Borrow as needed for the nsec value */
 279 while (sub->tv_nsec > diff->tv_nsec) {
 280     --diff->tv_sec;
 281     diff->tv_nsec += 1000000000L;
 282     }
 283 diff->tv_nsec -= sub->tv_nsec;
 284 diff->tv_sec -= sub->tv_sec;
 285 /* Normalize the result */
 286 while (diff->tv_nsec > 1000000000L) {
 287     ++diff->tv_sec;
 288     diff->tv_nsec -= 1000000000L;
 289     }
 290 }
 291 
 292 /* Forward declarations */
 293 
 294 static double _timespec_to_double              (struct timespec *time);
 295 static void   _double_to_timespec              (struct timespec *time, double dtime);
 296 static void   _rtcn_configure_calibrated_clock (int32  newtmr);
 297 static void   _sim_coschedule_cancel(UNIT      *uptr);
 298 
 299 /* OS independent clock calibration package */
 300 
 301 static int32  rtc_ticks[SIM_NTIMERS+1]                   = { 0 }; /* ticks */
 302 static uint32 rtc_hz[SIM_NTIMERS+1]                      = { 0 }; /* tick rate */
 303 static uint32 rtc_rtime[SIM_NTIMERS+1]                   = { 0 }; /* real time */
 304 static uint32 rtc_vtime[SIM_NTIMERS+1]                   = { 0 }; /* virtual time */
 305 static double rtc_gtime[SIM_NTIMERS+1]                   = { 0 }; /* instruction time */
 306 static uint32 rtc_nxintv[SIM_NTIMERS+1]                  = { 0 }; /* next interval */
 307 static int32  rtc_based[SIM_NTIMERS+1]                   = { 0 }; /* base delay */
 308 static int32  rtc_currd[SIM_NTIMERS+1]                   = { 0 }; /* current delay */
 309 static int32  rtc_initd[SIM_NTIMERS+1]                   = { 0 }; /* initial delay */
 310 static uint32 rtc_elapsed[SIM_NTIMERS+1]                 = { 0 }; /* sec since init */
 311 static uint32 rtc_calibrations[SIM_NTIMERS+1]            = { 0 }; /* calibration count */
 312 static double rtc_clock_skew_max[SIM_NTIMERS+1]          = { 0 }; /* asynchronous max skew */
 313 static double rtc_clock_start_gtime[SIM_NTIMERS+1]       = { 0 }; /* reference instruction time for clock */
 314 static double rtc_clock_tick_size[SIM_NTIMERS+1]         = { 0 }; /* 1/hz */
 315 static uint32 rtc_calib_initializations[SIM_NTIMERS+1]   = { 0 }; /* Initialization Count */
 316 static double rtc_calib_tick_time[SIM_NTIMERS+1]         = { 0 }; /* ticks time */
 317 static double rtc_calib_tick_time_tot[SIM_NTIMERS+1]     = { 0 }; /* ticks time - total*/
 318 static uint32 rtc_calib_ticks_acked[SIM_NTIMERS+1]       = { 0 }; /* ticks Acked */
 319 static uint32 rtc_calib_ticks_acked_tot[SIM_NTIMERS+1]   = { 0 }; /* ticks Acked - total */
 320 static uint32 rtc_clock_ticks[SIM_NTIMERS+1]             = { 0 }; /* ticks delivered since catchup base */
 321 static uint32 rtc_clock_ticks_tot[SIM_NTIMERS+1]         = { 0 }; /* ticks delivered since catchup base - total */
 322 static double rtc_clock_catchup_base_time[SIM_NTIMERS+1] = { 0 }; /* reference time for catchup ticks */
 323 static uint32 rtc_clock_catchup_ticks[SIM_NTIMERS+1]     = { 0 }; /* Record of catchups */
 324 static uint32 rtc_clock_catchup_ticks_tot[SIM_NTIMERS+1] = { 0 }; /* Record of catchups - total */
 325 static t_bool rtc_clock_catchup_pending[SIM_NTIMERS+1]   = { 0 }; /* clock tick catchup pending */
 326 static t_bool rtc_clock_catchup_eligible[SIM_NTIMERS+1]  = { 0 }; /* clock tick catchup eligible */
 327 static uint32 rtc_clock_time_idled[SIM_NTIMERS+1]        = { 0 }; /* total time idled */
 328 static uint32 rtc_clock_calib_skip_idle[SIM_NTIMERS+1]   = { 0 }; /* Calibrations skipped due to idling */
 329 static uint32 rtc_clock_calib_gap2big[SIM_NTIMERS+1]     = { 0 }; /* Calibrations skipped Gap Too Big */
 330 static uint32 rtc_clock_calib_backwards[SIM_NTIMERS+1]   = { 0 }; /* Calibrations skipped Clock Running Backwards */
 331 
 332 UNIT sim_timer_units[SIM_NTIMERS+1];                     /* one for each timer and one for an */
 333                                                          /* internal clock if no clocks are registered */
 334 UNIT sim_internal_timer_unit;                            /* Internal calibration timer */
 335 UNIT sim_throttle_unit;                                  /* one for throttle */
 336 
 337 t_stat sim_timer_tick_svc (UNIT *uptr);
 338 
 339 #define DBG_TRC       0x008                 /* tracing */
 340 #define DBG_CAL       0x010                 /* calibration activities */
 341 #define DBG_TIM       0x020                 /* timer thread activities */
 342 #define DBG_ACK       0x080                 /* interrupt acknowledgement activities */
 343 DEBTAB sim_timer_debug[] = {
 344   {"TRACE", DBG_TRC, "Trace routine calls"},
 345   {"IACK",  DBG_ACK, "interrupt acknowledgement activities"},
 346   {"CALIB", DBG_CAL, "Calibration activities"},
 347   {"TIME",  DBG_TIM, "Activation and scheduling activities"},
 348   {0}
 349 };
 350 
 351 /* Forward device declarations */
 352 extern DEVICE sim_timer_dev;
 353 extern DEVICE sim_throttle_dev;
 354 
 355 void sim_rtcn_init_all (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 356 {
 357 int32 tmr;
 358 
 359 for (tmr = 0; tmr <= SIM_NTIMERS; tmr++)
 360     if (rtc_initd[tmr] != 0)
 361         sim_rtcn_init (rtc_initd[tmr], tmr);
 362 return;
 363 }
 364 
 365 int32 sim_rtcn_init (int32 time, int32 tmr)
     /* [previous][next][first][last][top][bottom][index][help] */
 366 {
 367 return sim_rtcn_init_unit (NULL, time, tmr);
 368 }
 369 
 370 int32 sim_rtcn_init_unit (UNIT *uptr, int32 time, int32 tmr)
     /* [previous][next][first][last][top][bottom][index][help] */
 371 {
 372 if (time == 0)
 373     time = 1;
 374 if (tmr == SIM_INTERNAL_CLK)
 375     tmr = SIM_NTIMERS;
 376 else {
 377     if ((tmr < 0) || (tmr >= SIM_NTIMERS))
 378         return time;
 379     }
 380 /*
 381  * If we'd previously succeeded in calibrating a tick value, then use that
 382  * delay as a better default to setup when we're re-initialized.
 383  * Re-initializing happens on any boot or after any breakpoint/continue.
 384  */
 385 if (rtc_currd[tmr])
 386     time = rtc_currd[tmr];
 387 if (!uptr)
 388     uptr = sim_clock_unit[tmr];
 389 sim_debug (DBG_CAL, &sim_timer_dev, "_sim_rtcn_init_unit(unit=%s, time=%d, tmr=%d)\n", sim_uname(uptr), time, tmr);
 390 if (uptr) {
 391     if (!sim_clock_unit[tmr])
 392         sim_register_clock_unit_tmr (uptr, tmr);
 393     }
 394 rtc_clock_start_gtime[tmr]        = sim_gtime();
 395 rtc_rtime[tmr]                    = sim_os_msec ();
 396 rtc_vtime[tmr]                    = rtc_rtime[tmr];
 397 rtc_nxintv[tmr]                   = 1000;
 398 rtc_ticks[tmr]                    = 0;
 399 rtc_hz[tmr]                       = 0;
 400 rtc_based[tmr]                    = time;
 401 rtc_currd[tmr]                    = time;
 402 rtc_initd[tmr]                    = time;
 403 rtc_elapsed[tmr]                  = 0;
 404 rtc_calibrations[tmr]             = 0;
 405 rtc_clock_ticks_tot[tmr]         += rtc_clock_ticks[tmr];
 406 rtc_clock_ticks[tmr]              = 0;
 407 rtc_calib_tick_time_tot[tmr]     += rtc_calib_tick_time[tmr];
 408 rtc_calib_tick_time[tmr]          = 0;
 409 rtc_clock_catchup_pending[tmr]    = FALSE;
 410 rtc_clock_catchup_eligible[tmr]   = FALSE;
 411 rtc_clock_catchup_ticks_tot[tmr] += rtc_clock_catchup_ticks[tmr];
 412 rtc_clock_catchup_ticks[tmr]      = 0;
 413 rtc_calib_ticks_acked_tot[tmr]   += rtc_calib_ticks_acked[tmr];
 414 rtc_calib_ticks_acked[tmr]        = 0;
 415 ++rtc_calib_initializations[tmr];
 416 _rtcn_configure_calibrated_clock (tmr);
 417 return time;
 418 }
 419 
 420 int32 sim_rtcn_calb (uint32 ticksper, int32 tmr)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422 if (tmr == SIM_INTERNAL_CLK)
 423     tmr = SIM_NTIMERS;
 424 else {
 425     if ((tmr < 0) || (tmr >= SIM_NTIMERS))
 426         return 10000;
 427     }
 428 if (rtc_hz[tmr] != ticksper) {                          /* changing tick rate? */
 429     rtc_hz[tmr] = ticksper;
 430     rtc_clock_tick_size[tmr] = 1.0/ticksper;
 431     _rtcn_configure_calibrated_clock (tmr);
 432     rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec()/ticksper);
 433     }
 434 if (sim_clock_unit[tmr] == NULL) {                      /* Not using TIMER units? */
 435     rtc_clock_ticks[tmr] += 1;
 436     rtc_calib_tick_time[tmr] += rtc_clock_tick_size[tmr];
 437     }
 438 if (rtc_clock_catchup_pending[tmr]) {                   /* catchup tick? */
 439     ++rtc_clock_catchup_ticks[tmr];                     /* accumulating which were catchups */
 440     rtc_clock_catchup_pending[tmr] = FALSE;
 441     }
 442 return rtc_currd[tmr];                                  /* return now avoiding counting catchup tick in calibration */
 443 }
 444 
 445 /* sim_timer_init - get minimum sleep time available on this host */
 446 
 447 t_bool sim_timer_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 448 {
 449 int tmr;
 450 uint32 clock_start, clock_last, clock_now;
 451 
 452 sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_init()\n");
 453 for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
 454     sim_timer_units[tmr].action = &sim_timer_tick_svc;
 455     sim_timer_units[tmr].flags  = UNIT_DIS | UNIT_IDLE;
 456     }
 457 SIM_INTERNAL_UNIT.flags = UNIT_DIS | UNIT_IDLE;
 458 sim_register_internal_device (&sim_timer_dev);
 459 sim_register_clock_unit_tmr (&SIM_INTERNAL_UNIT, SIM_INTERNAL_CLK);
 460 sim_idle_rate_ms = sim_os_ms_sleep_init ();             /* get OS timer rate */
 461 
 462 clock_last = clock_start = sim_os_msec ();
 463 sim_os_clock_resoluton_ms = 1000;
 464 do {
 465     uint32 clock_diff;
 466 
 467     clock_now = sim_os_msec ();
 468     clock_diff = clock_now - clock_last;
 469     if ((clock_diff > 0) && (clock_diff < sim_os_clock_resoluton_ms))
 470         sim_os_clock_resoluton_ms = clock_diff;
 471     clock_last = clock_now;
 472     } while (clock_now < clock_start + 100);
 473 sim_os_tick_hz = 1000/(sim_os_clock_resoluton_ms * (sim_idle_rate_ms/sim_os_clock_resoluton_ms));
 474 return (sim_idle_rate_ms != 0);
 475 }
 476 
 477 /* sim_show_timers - show running timer information */
 478 t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 479 {
 480 int tmr, clocks;
 481 struct timespec now;
 482 time_t time_t_now;
 483 int32 calb_tmr = (sim_calb_tmr == -1) ? sim_calb_tmr_last : sim_calb_tmr;
 484 
 485 for (tmr=clocks=0; tmr<=SIM_NTIMERS; ++tmr) {
 486     if (0 == rtc_initd[tmr])
 487         continue;
 488 
 489     if (sim_clock_unit[tmr]) {
 490         ++clocks;
 491         fprintf (st, "%s clock device is %s%s%s\n",
 492                  sim_name,
 493                  (tmr == SIM_NTIMERS) ? "Internal Calibrated Timer(" : "",
 494                  sim_uname(sim_clock_unit[tmr]),
 495                  (tmr == SIM_NTIMERS) ? ")" : "");
 496         }
 497 
 498     fprintf (st, "%s%sTimer %d:\n", "",
 499              rtc_hz[tmr] ? "Calibrated " : "Uncalibrated ",
 500              tmr);
 501     if (rtc_hz[tmr]) {
 502         fprintf (st, "  Running at:                %lu Hz\n",
 503                  (unsigned long)rtc_hz[tmr]);
 504         fprintf (st, "  Tick Size:                 %s\n",
 505                  sim_fmt_secs (rtc_clock_tick_size[tmr]));
 506         fprintf (st, "  Ticks in current second:   %lu\n",
 507                  (unsigned long)rtc_ticks[tmr]);
 508         }
 509     fprintf (st, "  Seconds Running:           %lu (%s)\n",
 510              (unsigned long)rtc_elapsed[tmr],
 511              sim_fmt_secs ((double)rtc_elapsed[tmr]));
 512     if (tmr == calb_tmr) {
 513         fprintf (st, "  Calibration Opportunities: %lu\n",
 514                  (unsigned long)rtc_calibrations[tmr]);
 515         if (sim_idle_calib_pct)
 516             fprintf (st, "  Calib Skip Idle Thresh %%:  %lu\n",
 517                      (unsigned long)sim_idle_calib_pct);
 518         if (rtc_clock_calib_skip_idle[tmr])
 519             fprintf (st, "  Calibs Skip While Idle:    %lu\n",
 520                      (unsigned long)rtc_clock_calib_skip_idle[tmr]);
 521         if (rtc_clock_calib_backwards[tmr])
 522             fprintf (st, "  Calibs Skip Backwards:     %lu\n",
 523                      (unsigned long)rtc_clock_calib_backwards[tmr]);
 524         if (rtc_clock_calib_gap2big[tmr])
 525             fprintf (st, "  Calibs Skip Gap Too Big:   %lu\n",
 526                      (unsigned long)rtc_clock_calib_gap2big[tmr]);
 527         }
 528     if (rtc_gtime[tmr])
 529         fprintf (st, "  Instruction Time:          %.0f\n",
 530                  rtc_gtime[tmr]);
 531     fprintf (st, "  Current Insts Per Tick:    %lu\n",
 532              (unsigned long)rtc_currd[tmr]);
 533     fprintf (st, "  Initializations:           %lu\n",
 534              (unsigned long)rtc_calib_initializations[tmr]);
 535     fprintf (st, "  Total Ticks:               %lu\n",
 536              (unsigned long)rtc_clock_ticks_tot[tmr]+(unsigned long)rtc_clock_ticks[tmr]);
 537     if (rtc_clock_skew_max[tmr] != 0.0)
 538         fprintf (st, "  Peak Clock Skew:           %s%s\n",
 539                  sim_fmt_secs (fabs(rtc_clock_skew_max[tmr])),
 540                  (rtc_clock_skew_max[tmr] < 0) ? " fast" : " slow");
 541     if (rtc_calib_ticks_acked[tmr])
 542         fprintf (st, "  Ticks Acked:               %lu\n",
 543                  (unsigned long)rtc_calib_ticks_acked[tmr]);
 544     if (rtc_calib_ticks_acked_tot[tmr]+rtc_calib_ticks_acked[tmr] != rtc_calib_ticks_acked[tmr]) //-V584
 545         fprintf (st, "  Total Ticks Acked:         %lu\n",
 546                  (unsigned long)rtc_calib_ticks_acked_tot[tmr]+(unsigned long)rtc_calib_ticks_acked[tmr]);
 547     if (rtc_calib_tick_time[tmr])
 548         fprintf (st, "  Tick Time:                 %s\n",
 549                  sim_fmt_secs (rtc_calib_tick_time[tmr]));
 550     if (rtc_calib_tick_time_tot[tmr]+rtc_calib_tick_time[tmr] != rtc_calib_tick_time[tmr])
 551         fprintf (st, "  Total Tick Time:           %s\n",
 552                  sim_fmt_secs (rtc_calib_tick_time_tot[tmr]+rtc_calib_tick_time[tmr]));
 553     if (rtc_clock_catchup_ticks[tmr])
 554         fprintf (st, "  Catchup Ticks Sched:       %lu\n",
 555                  (unsigned long)rtc_clock_catchup_ticks[tmr]);
 556     if (rtc_clock_catchup_ticks_tot[tmr]+rtc_clock_catchup_ticks[tmr] != rtc_clock_catchup_ticks[tmr]) //-V584
 557         fprintf (st, "  Total Catchup Ticks Sched: %lu\n",
 558                  (unsigned long)rtc_clock_catchup_ticks_tot[tmr]+(unsigned long)rtc_clock_catchup_ticks[tmr]);
 559     clock_gettime (CLOCK_REALTIME, &now);
 560     time_t_now = (time_t)now.tv_sec;
 561     fprintf (st, "  Wall Clock Time Now:       %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
 562     if (rtc_clock_catchup_eligible[tmr]) {
 563         _double_to_timespec (&now, rtc_clock_catchup_base_time[tmr]+rtc_calib_tick_time[tmr]);
 564         time_t_now = (time_t)now.tv_sec;
 565         fprintf (st, "  Catchup Tick Time:         %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
 566         _double_to_timespec (&now, rtc_clock_catchup_base_time[tmr]);
 567         time_t_now = (time_t)now.tv_sec;
 568         fprintf (st, "  Catchup Base Time:         %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000));
 569         }
 570     if (rtc_clock_time_idled[tmr])
 571         fprintf (st, "  Total Time Idled:          %s\n",   sim_fmt_secs (rtc_clock_time_idled[tmr]/1000.0));
 572     }
 573 if (clocks == 0)
 574     fprintf (st, "%s clock device is not specified, co-scheduling is unavailable\n", sim_name);
 575 return SCPE_OK;
 576 }
 577 
 578 t_stat sim_show_clock_queues (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 579 {
 580 int tmr;
 581 
 582 for (tmr=0; tmr<=SIM_NTIMERS; ++tmr) {
 583     if (sim_clock_unit[tmr] == NULL)
 584         continue;
 585     if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
 586         int32 accum;
 587 
 588         fprintf (st, "%s clock (%s) co-schedule event queue status\n",
 589                  sim_name, sim_uname(sim_clock_unit[tmr]));
 590         accum = 0;
 591         for (uptr = sim_clock_cosched_queue[tmr]; uptr != QUEUE_LIST_END; uptr = uptr->next) { //-V763
 592             if ((dptr = find_dev_from_unit (uptr)) != NULL) { //-V763
 593                 fprintf (st, "  %s", sim_dname (dptr));
 594                 if (dptr->numunits > 1)
 595                     fprintf (st, " unit %d", (int32) (uptr - dptr->units));
 596                 }
 597             else
 598                 fprintf (st, "  Unknown");
 599             if (accum > 0)
 600                 fprintf (st, " after %d ticks", accum);
 601             fprintf (st, "\n");
 602             accum = accum + uptr->time;
 603             }
 604         }
 605     }
 606 return SCPE_OK;
 607 }
 608 
 609 REG sim_timer_reg[] = {
 610     { NULL }
 611     };
 612 
 613 /* Clear catchup */
 614 
 615 t_stat sim_timer_clr_catchup (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 616 {
 617 if (sim_catchup_ticks)
 618     sim_catchup_ticks = FALSE;
 619 return SCPE_OK;
 620 }
 621 
 622 t_stat sim_timer_set_catchup (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 623 {
 624 if (!sim_catchup_ticks)
 625     sim_catchup_ticks = TRUE;
 626 return SCPE_OK;
 627 }
 628 
 629 t_stat sim_timer_show_catchup (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 630 {
 631 fprintf (st, "Calibrated Ticks%s", sim_catchup_ticks ? " with Catchup Ticks" : "");
 632 return SCPE_OK;
 633 }
 634 
 635 MTAB sim_timer_mod[] = {
 636   { MTAB_VDV, MTAB_VDV, "CATCHUP", "CATCHUP", \
 637       &sim_timer_set_catchup, &sim_timer_show_catchup, NULL, "Enables/Displays Clock Tick catchup mode" },
 638   { MTAB_VDV, 0, NULL, "NOCATCHUP", \
 639       &sim_timer_clr_catchup, NULL, NULL, "Disables Clock Tick catchup mode" },
 640   { 0 },
 641 };
 642 
 643 static t_stat sim_timer_clock_reset (DEVICE *dptr);
 644 
 645 DEVICE sim_timer_dev = {
 646     "TIMER", sim_timer_units, sim_timer_reg, sim_timer_mod,
 647     SIM_NTIMERS+1, 0, 0, 0, 0, 0,
 648     NULL, NULL, &sim_timer_clock_reset, NULL, NULL, NULL,
 649     NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_timer_debug};
 650 
 651 /* Clock assist activities */
 652 t_stat sim_timer_tick_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 653 {
 654 int tmr = (int)(uptr-sim_timer_units);
 655 t_stat stat;
 656 
 657 rtc_clock_ticks[tmr] += 1;
 658 rtc_calib_tick_time[tmr] += rtc_clock_tick_size[tmr];
 659 /*
 660  * Some devices may depend on executing during the same instruction or
 661  * immediately after the clock tick event.  To satisfy this, we directly
 662  * run the clock event here and if it completes successfully, schedule any
 663  * currently coschedule units to run now.  Ticks should never return a
 664  * non-success status, while co-schedule activities might, so they are
 665  * queued to run from sim_process_event
 666  */
 667 if (sim_clock_unit[tmr]->action == NULL)
 668     return SCPE_IERR;
 669 stat = sim_clock_unit[tmr]->action (sim_clock_unit[tmr]);
 670 --sim_cosched_interval[tmr];                    /* Countdown ticks */
 671 if (stat == SCPE_OK) {
 672     if (rtc_clock_catchup_eligible[tmr]) {      /* calibration started? */
 673         struct timespec now;
 674         double skew;
 675 
 676         clock_gettime(CLOCK_REALTIME, &now);
 677         skew = (_timespec_to_double(&now) - (rtc_calib_tick_time[tmr]+rtc_clock_catchup_base_time[tmr]));
 678 
 679         if (fabs(skew) > fabs(rtc_clock_skew_max[tmr]))
 680             rtc_clock_skew_max[tmr]   = skew;
 681         }
 682     while ((sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) &&
 683            (sim_cosched_interval[tmr] < sim_clock_cosched_queue[tmr]->time)) {
 684         UNIT *cptr                    = sim_clock_cosched_queue[tmr];
 685         sim_clock_cosched_queue[tmr]  = cptr->next;
 686         cptr->next                    = NULL;
 687         cptr->cancel                  = NULL;
 688         _sim_activate (cptr, 0);
 689         }
 690     if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)
 691         sim_cosched_interval[tmr]     = sim_clock_cosched_queue[tmr]->time;
 692     else
 693         sim_cosched_interval[tmr]     = 0;
 694     }
 695 sim_timer_activate_after (uptr, 1000000/rtc_hz[tmr]);
 696 return stat;
 697 }
 698 
 699 #if !defined(__CYGWIN__) && \
 700   ( defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || \
 701     defined(CROSS_MINGW32) || defined(CROSS_MINGW64) )
 702 void win32_usleep(__int64 usec)
     /* [previous][next][first][last][top][bottom][index][help] */
 703 {
 704   HANDLE timer;
 705   LARGE_INTEGER ft;
 706 
 707   ft.QuadPart = -(10*usec);
 708 
 709   timer = CreateWaitableTimer(NULL, TRUE, NULL);
 710   SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
 711   WaitForSingleObject(timer, INFINITE);
 712   CloseHandle(timer);
 713 }
 714 #endif /* if !defined(__CYGWIN__) &&
 715             ( defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) ||
 716              defined(CROSS_MINGW32) || defined(CROSS_MINGW64) ) */
 717 
 718 int
 719 sim_usleep(useconds_t tusleep)
     /* [previous][next][first][last][top][bottom][index][help] */
 720 {
 721 #if ( !defined(__APPLE__) && !defined(__OpenBSD__) )
 722 # if !defined(__CYGWIN__) && \
 723   ( defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || \
 724     defined(CROSS_MINGW32) || defined(CROSS_MINGW64) )
 725   win32_usleep(tusleep);
 726 
 727   return 0;
 728 # else
 729 #  if !defined(__PASE__)
 730   struct timespec rqt;
 731   rqt.tv_sec  = tusleep / 1000000L;
 732   rqt.tv_nsec = (tusleep % 1000000L) * 1000L;
 733 
 734   return clock_nanosleep(CLOCK_MONOTONIC, 0, &rqt, NULL);
 735 #  else
 736   return usleep(tusleep);
 737 #  endif /* if !defined(__PASE__) */
 738 # endif /* if !defined(__CYGWIN__) &&
 739             ( defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) ||
 740              defined(CROSS_MINGW32) || defined(CROSS_MINGW64) ) */
 741 #else
 742 # if defined(__APPLE__)
 743   struct timespec rqt;
 744   rqt.tv_sec  = tusleep / 1000000L;
 745   rqt.tv_nsec = (tusleep % 1000000L) * 1000L;
 746   return nanosleep(&rqt, NULL);
 747 # else
 748   return usleep(tusleep);
 749 # endif /* if defined(__APPLE__) */
 750 #endif /* if ( !defined(__APPLE__) && !defined(__OpenBSD__) ) */
 751 }
 752 
 753 static double _timespec_to_double (struct timespec *time)
     /* [previous][next][first][last][top][bottom][index][help] */
 754 {
 755 return ((double)time->tv_sec)+(double)(time->tv_nsec)/1000000000.0;
 756 }
 757 
 758 static void _double_to_timespec (struct timespec *time, double dtime)
     /* [previous][next][first][last][top][bottom][index][help] */
 759 {
 760 time->tv_sec  = (time_t)floor(dtime);
 761 time->tv_nsec = (long)((dtime-floor(dtime))*1000000000.0);
 762 }
 763 
 764 #define CLK_TPS 10
 765 #define CLK_INIT (SIM_INITIAL_IPS/CLK_TPS)
 766 static int32 sim_int_clk_tps;
 767 
 768 static t_stat sim_timer_clock_tick_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 769 {
 770 sim_rtcn_calb (sim_int_clk_tps, SIM_INTERNAL_CLK);
 771 sim_activate_after (uptr, 1000000/sim_int_clk_tps);     /* reactivate unit */
 772 return SCPE_OK;
 773 }
 774 
 775 static void _rtcn_configure_calibrated_clock (int32 newtmr)
     /* [previous][next][first][last][top][bottom][index][help] */
 776 {
 777 int32 tmr;
 778 
 779 /* Look for a timer running slower than the host system clock */
 780 sim_int_clk_tps = MIN(CLK_TPS, sim_os_tick_hz);
 781 for (tmr=0; tmr<SIM_NTIMERS; tmr++) {
 782     if ((rtc_hz[tmr]) &&
 783         (rtc_hz[tmr] <= (uint32)sim_os_tick_hz))
 784         break;
 785     }
 786 if (tmr == SIM_NTIMERS) {                   /* None found? */
 787     if ((tmr != newtmr) && (!sim_is_active (&SIM_INTERNAL_UNIT))) {
 788         /* Start the internal timer */
 789         sim_calb_tmr = SIM_NTIMERS;
 790         sim_debug (DBG_CAL, &sim_timer_dev,
 791                    "_rtcn_configure_calibrated_clock() - Starting Internal Calibrated Timer at %dHz\n",
 792                    sim_int_clk_tps);
 793         SIM_INTERNAL_UNIT.action = &sim_timer_clock_tick_svc;
 794         SIM_INTERNAL_UNIT.flags = UNIT_DIS | UNIT_IDLE;
 795         sim_activate_abs (&SIM_INTERNAL_UNIT, 0);
 796         sim_rtcn_init_unit (&SIM_INTERNAL_UNIT, (CLK_INIT*CLK_TPS)/sim_int_clk_tps, SIM_INTERNAL_CLK);
 797         }
 798     return;
 799     }
 800 if ((tmr == newtmr) &&
 801     (sim_calb_tmr == newtmr))               /* already set? */
 802     return;
 803 if (sim_calb_tmr == SIM_NTIMERS) {      /* was old the internal timer? */
 804     sim_debug (DBG_CAL, &sim_timer_dev,
 805                "_rtcn_configure_calibrated_clock() - Stopping Internal Calibrated Timer, New Timer = %d (%dHz)\n",
 806                tmr, rtc_hz[tmr]);
 807     rtc_initd[SIM_NTIMERS] = 0;
 808     rtc_hz[SIM_NTIMERS] = 0;
 809     sim_cancel (&SIM_INTERNAL_UNIT);
 810     /* Migrate any coscheduled devices to the standard queue and they will requeue themselves */
 811     while (sim_clock_cosched_queue[SIM_NTIMERS] != QUEUE_LIST_END) {
 812         UNIT *uptr = sim_clock_cosched_queue[SIM_NTIMERS];
 813         _sim_coschedule_cancel (uptr);
 814         _sim_activate (uptr, 1);
 815         }
 816     }
 817 else {
 818     sim_debug (DBG_CAL, &sim_timer_dev,
 819                "_rtcn_configure_calibrated_clock() - Changing Calibrated Timer from %d (%dHz) to %d (%dHz)\n",
 820                sim_calb_tmr, rtc_hz[sim_calb_tmr], tmr, rtc_hz[tmr]);
 821     sim_calb_tmr = tmr;
 822     }
 823 sim_calb_tmr = tmr;
 824 }
 825 
 826 static t_stat sim_timer_clock_reset (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 827 {
 828 sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_clock_reset()\n");
 829 _rtcn_configure_calibrated_clock (sim_calb_tmr);
 830 if (sim_switches & SWMASK ('P')) {
 831     sim_cancel (&SIM_INTERNAL_UNIT);
 832     sim_calb_tmr = -1;
 833     }
 834 return SCPE_OK;
 835 }
 836 
 837 void sim_start_timer_services (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 838 {
 839 sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services()\n");
 840 _rtcn_configure_calibrated_clock (sim_calb_tmr);
 841 }
 842 
 843 void sim_stop_timer_services (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 844 {
 845 int tmr;
 846 
 847 sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services()\n");
 848 
 849 for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
 850     int32 accum;
 851 
 852     if (sim_clock_unit[tmr]) {
 853         /* Stop clock assist unit and make sure the clock unit has a tick queued */
 854         sim_cancel (&sim_timer_units[tmr]);
 855         if (rtc_hz[tmr])
 856             sim_activate (sim_clock_unit[tmr], rtc_currd[tmr]);
 857         /* Move coscheduled units to the standard event queue */
 858         accum = 1;
 859         while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
 860             UNIT *cptr = sim_clock_cosched_queue[tmr];
 861 
 862             sim_clock_cosched_queue[tmr] = cptr->next;
 863             cptr->next                   = NULL;
 864             cptr->cancel                 = NULL;
 865 
 866             accum += cptr->time;
 867             _sim_activate (cptr, accum*rtc_currd[tmr]);
 868             }
 869         }
 870     }
 871 sim_cancel (&SIM_INTERNAL_UNIT);                    /* Make sure Internal Timer is stopped */
 872 sim_calb_tmr_last     = sim_calb_tmr;                   /* Save calibrated timer value for display */
 873 sim_inst_per_sec_last = sim_timer_inst_per_sec ();  /* Save execution rate for display */
 874 sim_calb_tmr          = -1;
 875 }
 876 
 877 /* Instruction Execution rate. */
 878 
 879 double sim_timer_inst_per_sec (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 880 {
 881 double inst_per_sec = SIM_INITIAL_IPS;
 882 
 883 if (sim_calb_tmr == -1)
 884     return inst_per_sec;
 885 inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*rtc_hz[sim_calb_tmr];
 886 if (0 == inst_per_sec)
 887     inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*sim_int_clk_tps;
 888 return inst_per_sec;
 889 }
 890 
 891 t_stat sim_timer_activate (UNIT *uptr, int32 interval)
     /* [previous][next][first][last][top][bottom][index][help] */
 892 {
 893 return sim_timer_activate_after (uptr, (uint32)((interval * 1000000.0) / sim_timer_inst_per_sec ()));
 894 }
 895 
 896 t_stat sim_timer_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
 897 {
 898 int inst_delay, tmr;
 899 double inst_delay_d, inst_per_sec;
 900 
 901 /* If this is a clock unit, we need to schedule the related timer unit instead */
 902 for (tmr=0; tmr<=SIM_NTIMERS; tmr++)
 903     if (sim_clock_unit[tmr] == uptr) {
 904         uptr = &sim_timer_units[tmr];
 905         break;
 906         }
 907 if (sim_is_active (uptr))                               /* already active? */
 908     return SCPE_OK;
 909 inst_per_sec = sim_timer_inst_per_sec ();
 910 inst_delay_d = ((inst_per_sec*usec_delay)/1000000.0);
 911 /* Bound delay to avoid overflow.  */
 912 /* Long delays are usually canceled before they expire */
 913 if (inst_delay_d > (double)0x7fffffff)
 914     inst_delay_d = (double)0x7fffffff;
 915 inst_delay = (int32)inst_delay_d;
 916 if ((inst_delay == 0) && (usec_delay != 0))
 917     inst_delay = 1;     /* Minimum non-zero delay is 1 instruction */
 918 sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - queue addition %s at %d (%d usecs)\n",
 919            sim_uname(uptr), inst_delay, usec_delay);
 920 return _sim_activate (uptr, inst_delay);                /* queue it now */
 921 }
 922 
 923 t_stat sim_register_clock_unit_tmr (UNIT *uptr, int32 tmr)
     /* [previous][next][first][last][top][bottom][index][help] */
 924 {
 925 if (tmr == SIM_INTERNAL_CLK)
 926     tmr = SIM_NTIMERS;
 927 else {
 928     if ((tmr < 0) || (tmr >= SIM_NTIMERS))
 929         return SCPE_IERR;
 930     }
 931 if (NULL == uptr) {                         /* deregistering? */
 932     while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
 933         UNIT *uptr = sim_clock_cosched_queue[tmr];
 934 
 935         _sim_coschedule_cancel (uptr);
 936         _sim_activate (uptr, 1);
 937         }
 938     sim_clock_unit[tmr] = NULL;
 939     return SCPE_OK;
 940     }
 941 if (NULL == sim_clock_unit[tmr])
 942     sim_clock_cosched_queue[tmr] = QUEUE_LIST_END;
 943 sim_clock_unit[tmr] = uptr;
 944 uptr->dynflags |= UNIT_TMR_UNIT;
 945 sim_timer_units[tmr].flags = UNIT_DIS | (sim_clock_unit[tmr] ? UNIT_IDLE : 0); //-V547
 946 return SCPE_OK;
 947 }
 948 
 949 /* Cancel a unit on the coschedule queue */
 950 static void _sim_coschedule_cancel (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 951 {
 952 if (uptr->next) {                           /* On a queue? */
 953     int tmr;
 954 
 955     for (tmr=0; tmr<SIM_NTIMERS; tmr++) {
 956         if (uptr == sim_clock_cosched_queue[tmr]) {
 957             sim_clock_cosched_queue[tmr] = uptr->next;
 958             uptr->next = NULL;
 959             }
 960         else {
 961             UNIT *cptr;
 962             for (cptr = sim_clock_cosched_queue[tmr];
 963                 (cptr != QUEUE_LIST_END);
 964                 cptr = cptr->next)
 965                 if (cptr->next == (uptr)) {
 966                     cptr->next = (uptr)->next;
 967                     uptr->next = NULL;
 968                     break;
 969                     }
 970             }
 971         if (uptr->next == NULL) {           /* found? */
 972             uptr->cancel = NULL;
 973             sim_debug (SIM_DBG_EVENT, &sim_timer_dev, "Canceled Clock Coscheduled Event for %s\n", sim_uname(uptr));
 974             return;
 975             }
 976         }
 977     }
 978 }
 979 

/* [previous][next][first][last][top][bottom][index][help] */