Commit | Line | Data |
---|---|---|
196ba1fc PH |
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 |