| 1 | /* sim_timer.c: simulator timer library\r |
| 2 | \r |
| 3 | Copyright (c) 1993-2008, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller)\r |
| 27 | 18-Jun-07 RMS Modified idle to exclude counted delays\r |
| 28 | 22-Mar-07 RMS Added sim_rtcn_init_all\r |
| 29 | 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato)\r |
| 30 | Added throttle support\r |
| 31 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r |
| 32 | 02-Jan-04 RMS Split out from SCP\r |
| 33 | \r |
| 34 | This library includes the following routines:\r |
| 35 | \r |
| 36 | sim_timer_init - initialize timing system\r |
| 37 | sim_rtc_init - initialize calibration\r |
| 38 | sim_rtc_calb - calibrate clock\r |
| 39 | sim_timer_init - initialize timing system\r |
| 40 | sim_idle - virtual machine idle\r |
| 41 | sim_os_msec - return elapsed time in msec\r |
| 42 | sim_os_sleep - sleep specified number of seconds\r |
| 43 | sim_os_ms_sleep - sleep specified number of milliseconds\r |
| 44 | \r |
| 45 | The calibration, idle, and throttle routines are OS-independent; the _os_\r |
| 46 | routines are not.\r |
| 47 | */\r |
| 48 | \r |
| 49 | #include "sim_defs.h"\r |
| 50 | #include <ctype.h>\r |
| 51 | \r |
| 52 | t_bool sim_idle_enab = FALSE; /* global flag */\r |
| 53 | \r |
| 54 | static uint32 sim_idle_rate_ms = 0;\r |
| 55 | static uint32 sim_throt_ms_start = 0;\r |
| 56 | static uint32 sim_throt_ms_stop = 0;\r |
| 57 | static uint32 sim_throt_type = 0;\r |
| 58 | static uint32 sim_throt_val = 0;\r |
| 59 | static uint32 sim_throt_state = 0;\r |
| 60 | static int32 sim_throt_wait = 0;\r |
| 61 | extern int32 sim_interval, sim_switches;\r |
| 62 | extern FILE *sim_log;\r |
| 63 | extern UNIT *sim_clock_queue;\r |
| 64 | \r |
| 65 | t_stat sim_throt_svc (UNIT *uptr);\r |
| 66 | \r |
| 67 | UNIT sim_throt_unit = { UDATA (&sim_throt_svc, 0, 0) };\r |
| 68 | \r |
| 69 | /* OS-dependent timer and clock routines */\r |
| 70 | \r |
| 71 | /* VMS */\r |
| 72 | \r |
| 73 | #if defined (VMS)\r |
| 74 | \r |
| 75 | #if defined (__VAX)\r |
| 76 | #define sys$gettim SYS$GETTIM\r |
| 77 | #endif\r |
| 78 | \r |
| 79 | #include <starlet.h>\r |
| 80 | #include <lib$routines.h>\r |
| 81 | #include <unistd.h>\r |
| 82 | \r |
| 83 | const t_bool rtc_avail = TRUE;\r |
| 84 | \r |
| 85 | uint32 sim_os_msec ()\r |
| 86 | {\r |
| 87 | uint32 quo, htod, tod[2];\r |
| 88 | int32 i;\r |
| 89 | \r |
| 90 | sys$gettim (tod); /* time 0.1usec */\r |
| 91 | \r |
| 92 | /* To convert to msec, must divide a 64b quantity by 10000. This is actually done\r |
| 93 | by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the\r |
| 94 | high 32b of which are discarded. This can probably be done by a clever multiply...\r |
| 95 | */\r |
| 96 | \r |
| 97 | quo = htod = 0;\r |
| 98 | for (i = 0; i < 64; i++) { /* 64b quo */\r |
| 99 | htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */\r |
| 100 | tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1);\r |
| 101 | tod[0] = tod[0] << 1;\r |
| 102 | quo = quo << 1; /* shift quo */\r |
| 103 | if (htod >= 10000) { /* divd work? */\r |
| 104 | htod = htod - 10000; /* subtract */\r |
| 105 | quo = quo | 1; /* set quo bit */\r |
| 106 | }\r |
| 107 | }\r |
| 108 | return quo;\r |
| 109 | }\r |
| 110 | \r |
| 111 | void sim_os_sleep (unsigned int sec)\r |
| 112 | {\r |
| 113 | sleep (sec);\r |
| 114 | return;\r |
| 115 | }\r |
| 116 | \r |
| 117 | uint32 sim_os_ms_sleep_init (void)\r |
| 118 | {\r |
| 119 | #if defined (__VAX)\r |
| 120 | return 10; /* VAX/VMS is 10ms */\r |
| 121 | #else\r |
| 122 | return 1; /* Alpha/VMS is 1ms */\r |
| 123 | #endif\r |
| 124 | }\r |
| 125 | \r |
| 126 | uint32 sim_os_ms_sleep (unsigned int msec)\r |
| 127 | {\r |
| 128 | uint32 stime = sim_os_msec ();\r |
| 129 | uint32 qtime[2];\r |
| 130 | int32 nsfactor = -10000;\r |
| 131 | static int32 zero = 0;\r |
| 132 | \r |
| 133 | lib$emul (&msec, &nsfactor, &zero, qtime);\r |
| 134 | sys$setimr (2, qtime, 0, 0);\r |
| 135 | sys$waitfr (2);\r |
| 136 | return sim_os_msec () - stime;\r |
| 137 | }\r |
| 138 | \r |
| 139 | /* Win32 routines */\r |
| 140 | \r |
| 141 | #elif defined (_WIN32)\r |
| 142 | \r |
| 143 | #include <windows.h>\r |
| 144 | \r |
| 145 | const t_bool rtc_avail = TRUE;\r |
| 146 | \r |
| 147 | uint32 sim_os_msec ()\r |
| 148 | {\r |
| 149 | if (sim_idle_rate_ms) return timeGetTime ();\r |
| 150 | else return GetTickCount ();\r |
| 151 | }\r |
| 152 | \r |
| 153 | void sim_os_sleep (unsigned int sec)\r |
| 154 | {\r |
| 155 | Sleep (sec * 1000);\r |
| 156 | return;\r |
| 157 | }\r |
| 158 | \r |
| 159 | void sim_timer_exit (void)\r |
| 160 | {\r |
| 161 | timeEndPeriod (sim_idle_rate_ms);\r |
| 162 | return;\r |
| 163 | }\r |
| 164 | \r |
| 165 | uint32 sim_os_ms_sleep_init (void)\r |
| 166 | {\r |
| 167 | TIMECAPS timers;\r |
| 168 | \r |
| 169 | if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR)\r |
| 170 | return 0;\r |
| 171 | if ((timers.wPeriodMin == 0) || (timers.wPeriodMin > SIM_IDLE_MAX))\r |
| 172 | return 0;\r |
| 173 | if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR)\r |
| 174 | return 0;\r |
| 175 | atexit (sim_timer_exit);\r |
| 176 | Sleep (1);\r |
| 177 | Sleep (1);\r |
| 178 | Sleep (1);\r |
| 179 | Sleep (1);\r |
| 180 | Sleep (1);\r |
| 181 | return timers.wPeriodMin; /* sim_idle_rate_ms */\r |
| 182 | }\r |
| 183 | \r |
| 184 | uint32 sim_os_ms_sleep (unsigned int msec)\r |
| 185 | {\r |
| 186 | uint32 stime = sim_os_msec();\r |
| 187 | \r |
| 188 | Sleep (msec);\r |
| 189 | return sim_os_msec () - stime;\r |
| 190 | }\r |
| 191 | \r |
| 192 | /* OS/2 routines, from Bruce Ray */\r |
| 193 | \r |
| 194 | #elif defined (__OS2__)\r |
| 195 | \r |
| 196 | const t_bool rtc_avail = FALSE;\r |
| 197 | \r |
| 198 | uint32 sim_os_msec ()\r |
| 199 | {\r |
| 200 | return 0;\r |
| 201 | }\r |
| 202 | \r |
| 203 | void sim_os_sleep (unsigned int sec)\r |
| 204 | {\r |
| 205 | return;\r |
| 206 | }\r |
| 207 | \r |
| 208 | uint32 sim_os_ms_sleep_init (void)\r |
| 209 | {\r |
| 210 | return FALSE;\r |
| 211 | }\r |
| 212 | \r |
| 213 | uint32 sim_os_ms_sleep (unsigned int msec)\r |
| 214 | {\r |
| 215 | return 0;\r |
| 216 | }\r |
| 217 | \r |
| 218 | /* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */\r |
| 219 | \r |
| 220 | #elif defined (__MWERKS__) && defined (macintosh)\r |
| 221 | \r |
| 222 | #include <Timer.h>\r |
| 223 | #include <Mactypes.h>\r |
| 224 | #include <sioux.h>\r |
| 225 | #include <unistd.h>\r |
| 226 | #include <siouxglobals.h>\r |
| 227 | #define NANOS_PER_MILLI 1000000\r |
| 228 | #define MILLIS_PER_SEC 1000\r |
| 229 | \r |
| 230 | const t_bool rtc_avail = TRUE;\r |
| 231 | \r |
| 232 | uint32 sim_os_msec (void)\r |
| 233 | {\r |
| 234 | unsigned long long micros;\r |
| 235 | UnsignedWide macMicros;\r |
| 236 | unsigned long millis;\r |
| 237 | \r |
| 238 | Microseconds (&macMicros);\r |
| 239 | micros = *((unsigned long long *) &macMicros);\r |
| 240 | millis = micros / 1000LL;\r |
| 241 | return (uint32) millis;\r |
| 242 | }\r |
| 243 | \r |
| 244 | void sim_os_sleep (unsigned int sec)\r |
| 245 | {\r |
| 246 | sleep (sec);\r |
| 247 | return;\r |
| 248 | }\r |
| 249 | \r |
| 250 | uint32 sim_os_ms_sleep_init (void)\r |
| 251 | {\r |
| 252 | return 1;\r |
| 253 | }\r |
| 254 | \r |
| 255 | uint32 sim_os_ms_sleep (unsigned int milliseconds)\r |
| 256 | {\r |
| 257 | uint32 stime = sim_os_msec ();\r |
| 258 | struct timespec treq;\r |
| 259 | \r |
| 260 | treq.tv_sec = milliseconds / MILLIS_PER_SEC;\r |
| 261 | treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI;\r |
| 262 | (void) nanosleep (&treq, NULL);\r |
| 263 | return sim_os_msec () - stime;\r |
| 264 | }\r |
| 265 | \r |
| 266 | #else\r |
| 267 | \r |
| 268 | /* UNIX routines */\r |
| 269 | \r |
| 270 | #include <time.h>\r |
| 271 | #include <sys/time.h>\r |
| 272 | #include <unistd.h>\r |
| 273 | #define NANOS_PER_MILLI 1000000\r |
| 274 | #define MILLIS_PER_SEC 1000\r |
| 275 | #define sleep1Samples 100\r |
| 276 | \r |
| 277 | const t_bool rtc_avail = TRUE;\r |
| 278 | \r |
| 279 | uint32 sim_os_msec ()\r |
| 280 | {\r |
| 281 | struct timeval cur;\r |
| 282 | struct timezone foo;\r |
| 283 | uint32 msec;\r |
| 284 | \r |
| 285 | gettimeofday (&cur, &foo);\r |
| 286 | msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000);\r |
| 287 | return msec;\r |
| 288 | }\r |
| 289 | \r |
| 290 | void sim_os_sleep (unsigned int sec)\r |
| 291 | {\r |
| 292 | sleep (sec);\r |
| 293 | return;\r |
| 294 | }\r |
| 295 | \r |
| 296 | uint32 sim_os_ms_sleep_init (void)\r |
| 297 | {\r |
| 298 | #if defined (_POSIX_SOURCE) /* POSIX-compliant */\r |
| 299 | \r |
| 300 | struct timespec treq;\r |
| 301 | uint32 msec;\r |
| 302 | \r |
| 303 | if (clock_getres (CLOCK_REALTIME, &treq) != 0)\r |
| 304 | return 0;\r |
| 305 | msec = (treq.tv_nsec + (NANOS_PER_MILLI - 1)) / NANOS_PER_MILLI;\r |
| 306 | if (msec > SIM_IDLE_MAX) return 0;\r |
| 307 | return msec;\r |
| 308 | \r |
| 309 | #else /* others */\r |
| 310 | \r |
| 311 | uint32 i, t1, t2, tot, tim;\r |
| 312 | \r |
| 313 | for (i = 0, tot = 0; i < sleep1Samples; i++) {\r |
| 314 | t1 = sim_os_msec ();\r |
| 315 | sim_os_ms_sleep (1);\r |
| 316 | t2 = sim_os_msec ();\r |
| 317 | tot += (t2 - t1);\r |
| 318 | }\r |
| 319 | tim = (tot + (sleep1Samples - 1)) / sleep1Samples;\r |
| 320 | if (tim == 0) tim = 1;\r |
| 321 | else if (tim > SIM_IDLE_MAX) tim = 0;\r |
| 322 | return tim;\r |
| 323 | \r |
| 324 | #endif\r |
| 325 | }\r |
| 326 | \r |
| 327 | uint32 sim_os_ms_sleep (unsigned int milliseconds)\r |
| 328 | {\r |
| 329 | uint32 stime = sim_os_msec ();\r |
| 330 | struct timespec treq;\r |
| 331 | \r |
| 332 | treq.tv_sec = milliseconds / MILLIS_PER_SEC;\r |
| 333 | treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI;\r |
| 334 | (void) nanosleep (&treq, NULL);\r |
| 335 | return sim_os_msec () - stime;\r |
| 336 | }\r |
| 337 | \r |
| 338 | #endif\r |
| 339 | \r |
| 340 | /* OS independent clock calibration package */\r |
| 341 | \r |
| 342 | static int32 rtc_ticks[SIM_NTIMERS] = { 0 }; /* ticks */\r |
| 343 | static int32 rtc_hz[SIM_NTIMERS] = { 0 }; /* tick rate */\r |
| 344 | static uint32 rtc_rtime[SIM_NTIMERS] = { 0 }; /* real time */\r |
| 345 | static uint32 rtc_vtime[SIM_NTIMERS] = { 0 }; /* virtual time */\r |
| 346 | static uint32 rtc_nxintv[SIM_NTIMERS] = { 0 }; /* next interval */\r |
| 347 | static int32 rtc_based[SIM_NTIMERS] = { 0 }; /* base delay */\r |
| 348 | static int32 rtc_currd[SIM_NTIMERS] = { 0 }; /* current delay */\r |
| 349 | static int32 rtc_initd[SIM_NTIMERS] = { 0 }; /* initial delay */\r |
| 350 | \r |
| 351 | void sim_rtcn_init_all (void)\r |
| 352 | {\r |
| 353 | uint32 i;\r |
| 354 | \r |
| 355 | for (i = 0; i < SIM_NTIMERS; i++) {\r |
| 356 | if (rtc_initd[i] != 0) sim_rtcn_init (rtc_initd[i], i);\r |
| 357 | }\r |
| 358 | return;\r |
| 359 | }\r |
| 360 | \r |
| 361 | int32 sim_rtcn_init (int32 time, int32 tmr)\r |
| 362 | {\r |
| 363 | if (time == 0) time = 1;\r |
| 364 | if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return time;\r |
| 365 | rtc_rtime[tmr] = sim_os_msec ();\r |
| 366 | rtc_vtime[tmr] = rtc_rtime[tmr];\r |
| 367 | rtc_nxintv[tmr] = 1000;\r |
| 368 | rtc_ticks[tmr] = 0;\r |
| 369 | rtc_hz[tmr] = 0;\r |
| 370 | rtc_based[tmr] = time;\r |
| 371 | rtc_currd[tmr] = time;\r |
| 372 | rtc_initd[tmr] = time;\r |
| 373 | return time;\r |
| 374 | }\r |
| 375 | \r |
| 376 | int32 sim_rtcn_calb (int32 ticksper, int32 tmr)\r |
| 377 | {\r |
| 378 | uint32 new_rtime, delta_rtime;\r |
| 379 | int32 delta_vtime;\r |
| 380 | \r |
| 381 | if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return 10000;\r |
| 382 | rtc_hz[tmr] = ticksper;\r |
| 383 | rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */\r |
| 384 | if (rtc_ticks[tmr] < ticksper) return rtc_currd[tmr]; /* 1 sec yet? */\r |
| 385 | rtc_ticks[tmr] = 0; /* reset ticks */\r |
| 386 | if (!rtc_avail) return rtc_currd[tmr]; /* no timer? */\r |
| 387 | new_rtime = sim_os_msec (); /* wall time */\r |
| 388 | if (new_rtime < rtc_rtime[tmr]) { /* time running backwards? */\r |
| 389 | rtc_rtime[tmr] = new_rtime; /* reset wall time */\r |
| 390 | return rtc_currd[tmr]; /* can't calibrate */\r |
| 391 | }\r |
| 392 | delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */\r |
| 393 | rtc_rtime[tmr] = new_rtime; /* adv wall time */\r |
| 394 | rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */\r |
| 395 | if (delta_rtime > 30000) /* gap too big? */\r |
| 396 | return rtc_initd[tmr]; /* can't calibr */\r |
| 397 | if (delta_rtime == 0) /* gap too small? */\r |
| 398 | rtc_based[tmr] = rtc_based[tmr] * ticksper; /* slew wide */\r |
| 399 | else rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) /\r |
| 400 | ((double) delta_rtime)); /* new base rate */\r |
| 401 | delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr]; /* gap */\r |
| 402 | if (delta_vtime > SIM_TMAX) delta_vtime = SIM_TMAX; /* limit gap */\r |
| 403 | else if (delta_vtime < -SIM_TMAX) delta_vtime = -SIM_TMAX;\r |
| 404 | rtc_nxintv[tmr] = 1000 + delta_vtime; /* next wtime */\r |
| 405 | rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) /\r |
| 406 | 1000.0); /* next delay */\r |
| 407 | if (rtc_based[tmr] <= 0) rtc_based[tmr] = 1; /* never negative or zero! */\r |
| 408 | if (rtc_currd[tmr] <= 0) rtc_currd[tmr] = 1; /* never negative or zero! */\r |
| 409 | return rtc_currd[tmr];\r |
| 410 | }\r |
| 411 | \r |
| 412 | /* Prior interfaces - default to timer 0 */\r |
| 413 | \r |
| 414 | int32 sim_rtc_init (int32 time)\r |
| 415 | {\r |
| 416 | return sim_rtcn_init (time, 0);\r |
| 417 | }\r |
| 418 | \r |
| 419 | int32 sim_rtc_calb (int32 ticksper)\r |
| 420 | {\r |
| 421 | return sim_rtcn_calb (ticksper, 0);\r |
| 422 | }\r |
| 423 | \r |
| 424 | /* sim_timer_init - get minimum sleep time available on this host */\r |
| 425 | \r |
| 426 | t_bool sim_timer_init (void)\r |
| 427 | {\r |
| 428 | sim_idle_enab = FALSE; /* init idle off */\r |
| 429 | sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */\r |
| 430 | return (sim_idle_rate_ms != 0);\r |
| 431 | }\r |
| 432 | \r |
| 433 | /* sim_idle - idle simulator until next event or for specified interval\r |
| 434 | \r |
| 435 | Inputs:\r |
| 436 | tmr = calibrated timer to use\r |
| 437 | \r |
| 438 | Must solve the linear equation\r |
| 439 | \r |
| 440 | ms_to_wait = w * ms_per_wait\r |
| 441 | \r |
| 442 | Or\r |
| 443 | w = ms_to_wait / ms_per_wait\r |
| 444 | */\r |
| 445 | \r |
| 446 | t_bool sim_idle (uint32 tmr, t_bool sin_cyc)\r |
| 447 | {\r |
| 448 | uint32 cyc_ms, w_ms, w_idle, act_ms;\r |
| 449 | int32 act_cyc;\r |
| 450 | \r |
| 451 | if ((sim_clock_queue == NULL) || /* clock queue empty? */\r |
| 452 | ((sim_clock_queue->flags & UNIT_IDLE) == 0)) { /* event not idle-able? */\r |
| 453 | if (sin_cyc) sim_interval = sim_interval - 1;\r |
| 454 | return FALSE;\r |
| 455 | }\r |
| 456 | cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000; /* cycles per msec */\r |
| 457 | if ((sim_idle_rate_ms == 0) || (cyc_ms == 0)) { /* not possible? */\r |
| 458 | if (sin_cyc) sim_interval = sim_interval - 1;\r |
| 459 | return FALSE;\r |
| 460 | }\r |
| 461 | w_ms = (uint32) sim_interval / cyc_ms; /* ms to wait */\r |
| 462 | w_idle = w_ms / sim_idle_rate_ms; /* intervals to wait */\r |
| 463 | if (w_idle == 0) { /* none? */\r |
| 464 | if (sin_cyc) sim_interval = sim_interval - 1;\r |
| 465 | return FALSE;\r |
| 466 | }\r |
| 467 | act_ms = sim_os_ms_sleep (w_idle); /* wait */\r |
| 468 | act_cyc = act_ms * cyc_ms;\r |
| 469 | if (sim_interval > act_cyc)\r |
| 470 | sim_interval = sim_interval - act_cyc;\r |
| 471 | else sim_interval = 1;\r |
| 472 | return TRUE;\r |
| 473 | }\r |
| 474 | \r |
| 475 | /* Set idling - implicitly disables throttling */\r |
| 476 | \r |
| 477 | t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 478 | {\r |
| 479 | if (sim_idle_rate_ms == 0) return SCPE_NOFNC;\r |
| 480 | if ((val != 0) && (sim_idle_rate_ms > (uint32) val))\r |
| 481 | return SCPE_NOFNC;\r |
| 482 | sim_idle_enab = TRUE;\r |
| 483 | if (sim_throt_type != SIM_THROT_NONE) {\r |
| 484 | sim_set_throt (0, NULL);\r |
| 485 | printf ("Throttling disabled\n");\r |
| 486 | if (sim_log) fprintf (sim_log, "Throttling disabled\n");\r |
| 487 | }\r |
| 488 | return SCPE_OK;\r |
| 489 | }\r |
| 490 | \r |
| 491 | /* Clear idling */\r |
| 492 | \r |
| 493 | t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 494 | {\r |
| 495 | sim_idle_enab = FALSE;\r |
| 496 | return SCPE_OK;\r |
| 497 | }\r |
| 498 | \r |
| 499 | /* Show idling */\r |
| 500 | \r |
| 501 | t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 502 | {\r |
| 503 | fprintf (st, sim_idle_enab? "idle enabled": "idle disabled");\r |
| 504 | return SCPE_OK;\r |
| 505 | }\r |
| 506 | \r |
| 507 | /* Throttling package */\r |
| 508 | \r |
| 509 | t_stat sim_set_throt (int32 arg, char *cptr)\r |
| 510 | {\r |
| 511 | char *tptr, c;\r |
| 512 | t_value val;\r |
| 513 | \r |
| 514 | if (arg == 0) {\r |
| 515 | if ((cptr != 0) && (*cptr != 0)) return SCPE_ARG;\r |
| 516 | sim_throt_type = SIM_THROT_NONE;\r |
| 517 | sim_throt_cancel ();\r |
| 518 | }\r |
| 519 | else if (sim_idle_rate_ms == 0) return SCPE_NOFNC;\r |
| 520 | else {\r |
| 521 | val = strtotv (cptr, &tptr, 10);\r |
| 522 | if (cptr == tptr) return SCPE_ARG;\r |
| 523 | c = toupper (*tptr++);\r |
| 524 | if (*tptr != 0) return SCPE_ARG;\r |
| 525 | if (c == 'M') sim_throt_type = SIM_THROT_MCYC;\r |
| 526 | else if (c == 'K') sim_throt_type = SIM_THROT_KCYC;\r |
| 527 | else if ((c == '%') && (val > 0) && (val < 100))\r |
| 528 | sim_throt_type = SIM_THROT_PCT;\r |
| 529 | else return SCPE_ARG;\r |
| 530 | if (sim_idle_enab) {\r |
| 531 | printf ("Idling disabled\n");\r |
| 532 | if (sim_log) fprintf (sim_log, "Idling disabled\n");\r |
| 533 | sim_clr_idle (NULL, 0, NULL, NULL);\r |
| 534 | }\r |
| 535 | sim_throt_val = (uint32) val;\r |
| 536 | }\r |
| 537 | return SCPE_OK;\r |
| 538 | }\r |
| 539 | \r |
| 540 | t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)\r |
| 541 | {\r |
| 542 | if (sim_idle_rate_ms == 0)\r |
| 543 | fprintf (st, "Throttling not available\n");\r |
| 544 | else {\r |
| 545 | switch (sim_throt_type) {\r |
| 546 | \r |
| 547 | case SIM_THROT_MCYC:\r |
| 548 | fprintf (st, "Throttle = %d megacycles\n", sim_throt_val);\r |
| 549 | break;\r |
| 550 | \r |
| 551 | case SIM_THROT_KCYC:\r |
| 552 | fprintf (st, "Throttle = %d kilocycles\n", sim_throt_val);\r |
| 553 | break;\r |
| 554 | \r |
| 555 | case SIM_THROT_PCT:\r |
| 556 | fprintf (st, "Throttle = %d%%\n", sim_throt_val);\r |
| 557 | break;\r |
| 558 | \r |
| 559 | default:\r |
| 560 | fprintf (st, "Throttling disabled\n");\r |
| 561 | break;\r |
| 562 | }\r |
| 563 | \r |
| 564 | if (sim_switches & SWMASK ('D')) {\r |
| 565 | fprintf (st, "Wait rate = %d ms\n", sim_idle_rate_ms);\r |
| 566 | if (sim_throt_type != 0)\r |
| 567 | fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait);\r |
| 568 | }\r |
| 569 | }\r |
| 570 | return SCPE_OK;\r |
| 571 | }\r |
| 572 | \r |
| 573 | void sim_throt_sched (void)\r |
| 574 | {\r |
| 575 | sim_throt_state = 0;\r |
| 576 | if (sim_throt_type)\r |
| 577 | sim_activate (&sim_throt_unit, SIM_THROT_WINIT);\r |
| 578 | return;\r |
| 579 | }\r |
| 580 | \r |
| 581 | void sim_throt_cancel (void)\r |
| 582 | {\r |
| 583 | sim_cancel (&sim_throt_unit);\r |
| 584 | }\r |
| 585 | \r |
| 586 | /* Throttle service\r |
| 587 | \r |
| 588 | Throttle service has three distinct states\r |
| 589 | \r |
| 590 | 0 take initial measurement\r |
| 591 | 1 take final measurement, calculate wait values\r |
| 592 | 2 periodic waits to slow down the CPU\r |
| 593 | */\r |
| 594 | \r |
| 595 | t_stat sim_throt_svc (UNIT *uptr)\r |
| 596 | {\r |
| 597 | uint32 delta_ms;\r |
| 598 | double a_cps, d_cps;\r |
| 599 | \r |
| 600 | switch (sim_throt_state) {\r |
| 601 | \r |
| 602 | case 0: /* take initial reading */\r |
| 603 | sim_throt_ms_start = sim_os_msec ();\r |
| 604 | sim_throt_wait = SIM_THROT_WST;\r |
| 605 | sim_throt_state++; /* next state */\r |
| 606 | break; /* reschedule */\r |
| 607 | \r |
| 608 | case 1: /* take final reading */\r |
| 609 | sim_throt_ms_stop = sim_os_msec ();\r |
| 610 | delta_ms = sim_throt_ms_stop - sim_throt_ms_start;\r |
| 611 | if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */\r |
| 612 | if (sim_throt_wait >= 100000000) { /* too many inst? */\r |
| 613 | sim_throt_state = 0; /* fails in 32b! */\r |
| 614 | return SCPE_OK;\r |
| 615 | }\r |
| 616 | sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL;\r |
| 617 | sim_throt_ms_start = sim_throt_ms_stop;\r |
| 618 | }\r |
| 619 | else { /* long enough */\r |
| 620 | a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms;\r |
| 621 | if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */\r |
| 622 | d_cps = (double) sim_throt_val * 1000000.0;\r |
| 623 | else if (sim_throt_type == SIM_THROT_KCYC)\r |
| 624 | d_cps = (double) sim_throt_val * 1000.0;\r |
| 625 | else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0;\r |
| 626 | if (d_cps >= a_cps) {\r |
| 627 | sim_throt_state = 0;\r |
| 628 | return SCPE_OK;\r |
| 629 | }\r |
| 630 | sim_throt_wait = (int32) /* time between waits */\r |
| 631 | ((a_cps * d_cps * ((double) sim_idle_rate_ms)) /\r |
| 632 | (1000.0 * (a_cps - d_cps)));\r |
| 633 | if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */\r |
| 634 | sim_throt_state = 0;\r |
| 635 | return SCPE_OK;\r |
| 636 | }\r |
| 637 | sim_throt_state++;\r |
| 638 | // fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n",\r |
| 639 | // a_cps, d_cps, sim_throt_wait);\r |
| 640 | }\r |
| 641 | break;\r |
| 642 | \r |
| 643 | case 2: /* throttling */\r |
| 644 | sim_os_ms_sleep (1);\r |
| 645 | break;\r |
| 646 | }\r |
| 647 | \r |
| 648 | sim_activate (uptr, sim_throt_wait); /* reschedule */\r |
| 649 | return SCPE_OK;\r |
| 650 | }\r |