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