open-simh / simh

The Open SIMH simulators package
https://opensimh.org/
Other
485 stars 93 forks source link

SCP: ^E does not stop simulator when using THROTTLE x/t #318

Closed deltecent closed 8 months ago

deltecent commented 1 year ago

When executing SET THROTTLE x/y, pressing ^E character on the console does not stop the simulator:

% altairz80

Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75
sim> show version
Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current
    Simulator Framework Capabilities:
        32b data
        32b addresses
        no Ethernet
        Idle/Throttling support is available
        Virtual Hard Disk (VHD) support
        RAW disk and CD/DVD ROM support
        Asynchronous I/O support (Lock free asynchronous event queue)
        Asynchronous Clock support
        FrontPanel API Version 12
    Host Platform:
        Compiler: GCC Apple LLVM 15.0.0 (clang-1500.0.40.1)
        Simulator Compiled as C arch: x64 (Release Build) on Oct 10 2023 at 13:21:41
        Build Tool: simh-makefile
        Memory Access: Little Endian
        Memory Pointer Size: 64 bits
        Large File (>2GB) support
        SDL Video support: SDL Version 2.26.3, PNG Version 1.6.39, zlib: (Compiled: 1.2.13, Runtime: 1.2.11)
        PCRE RegEx (Version 8.45 2021-06-15) support for EXPECT commands
        OS clock resolution: 1ms
        Time taken by msleep(1): 1ms
        OS: Darwin Patricks-iMac-2.local 22.6.0 Darwin Kernel Version 22.6.0: Fri Sep 15 13:39:52 PDT 2023; root:xnu-8796.141.3.700.8~1/RELEASE_X86_64 x86_64
        Processor Name: Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz
        tar tool: bsdtar 3.5.3 - libarchive 3.5.3 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.8
        curl tool: curl 8.1.2 (x86_64-apple-darwin22.0) libcurl/8.1.2 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.11 nghttp2/1.51.0
        git commit id: 5fba1e75
        git commit time: 2023-10-05T23:25:11-0400
SET SIO NOSLEEP
#SET THROTTLE 250K
SET THROTTLE 250/1

DEP 00 C3
DEP 01 00
DEP 02 00

G 0

When running the following commands, pressing ^E character on the console does stop the simulator:

SET SIO NOSLEEP
SET THROTTLE 250K
#SET THROTTLE 250/1

DEP 00 C3
DEP 01 00
DEP 02 00

G 0

Expected:

% altairz80 test.ini

Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75

Simulation stopped, PC: 00000 (JMP 0000h)

Simulation stopped C0Z0M0E0I0 A=00 B=0000 D=0000 H=0000 S=0000 P=0000 JMP 0000h
sim>

Actual:

% altairz80 test.ini

Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75
(pressing ^E - nothing happens... must send TERM signal)
SIGTERM received, PC: 00000 (JMP 0000h)
sim> 
markpizz commented 1 year ago

It works just fine for me on my favorite simulators.

It all depends on IF sim_poll_kbd() is called periodically.

For a simulator without some sort of a console configured and no calibrated timers, the internal timer gratuitously calls sim_poll_kbd() if nothing else has called it.

deltecent commented 1 year ago

I'm wondering why the behavior difference between "SET THROTTLE 250K and SET THROTTLE 250/1?

SET THROTTLE 256K

Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75
/Users/patrick/Library/Mobile Documents/com~apple~CloudDocs/open-simh/null.ini-12> SET DEBUG STDOUT
%SIM-INFO: Debug output to "STDOUT"
Debug output to "STDOUT" at Sat Nov  4 14:14:14 2023
Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75
DBG(0)> INT-CLOCK CALIB: sim_start_timer_services(tmr=8) - adjusting calibration real time by 0 ms
DBG(0)> INT-CLOCK CALIB: sim_start_timer_services() - restarting internal timer after 0 instructions
DBG(47596131)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after -1685393983 msecs
DBG(47596131)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=567, delta_vtime=433, base=847917, nxintv=1433, catchups=0, idle=0%, result: 1215065)
DBG(169102631)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 808 msecs
DBG(169102631)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=808, delta_vtime=500, base=1503793, nxintv=1500, catchups=0, idle=0%, result: 2255689)
DBG(394671531)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 1508 msecs
DBG(394671531)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=1508, delta_vtime=117, base=1495815, nxintv=1117, catchups=0, idle=0%, result: 1670825)
DBG(395155800)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=1001, delta_vtime=500, base=2497, nxintv=1500, catchups=0, idle=0%, result: 3745)
DBG(395530300)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 1891 msecs
DBG(395530300)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=1877, delta_vtime=226, base=1995, nxintv=1226, catchups=0, idle=0%, result: 2445)
DBG(395774800)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 1238 msecs
DBG(395774800)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=1239, delta_vtime=-13, base=1974, nxintv=987, catchups=0, idle=0%, result: 1948)
DBG(395969600)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 986 msecs
DBG(395969600)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=986, delta_vtime=1, base=1976, nxintv=1001, catchups=0, idle=0%, result: 1977)
DBG(396167200)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 990 msecs
DBG(396167200)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=990, delta_vtime=11, base=1997, nxintv=1011, catchups=0, idle=0%, result: 2018)
DBG(396369000)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 1023 msecs
DBG(396369000)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8, tickper=100) (delta_rtime=1023, delta_vtime=-12, base=1973, nxintv=988, catchups=0, idle=0%, result: 1949)

Simulation stopped, PC: 00000 (JMP 0000h)

Simulation stopped C0Z0M0E0I0 A=00 B=0000 D=0000 H=0000 S=0000 P=0000 JMP 0000h
sim>
SET THROTTLE 250/1

Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75
/Users/patrick/Library/Mobile Documents/com~apple~CloudDocs/open-simh/null.ini-12> SET DEBUG STDOUT
%SIM-INFO: Debug output to "STDOUT"
Debug output to "STDOUT" at Sat Nov  4 14:15:07 2023
Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 5fba1e75
DBG(0)> INT-CLOCK CALIB: sim_start_timer_services(tmr=8) - adjusting calibration real time by 0 ms
DBG(0)> INT-CLOCK CALIB: sim_start_timer_services() - restarting internal timer after 0 instructions
/Users/patrick/Library/Mobile Documents/com~apple~CloudDocs/open-simh/null.ini-15> G 0

SIGTERM received, PC: 00000 (JMP 0000h)
sim> 
psco commented 1 year ago

Actually it appears that ^E "works" even with SET THROTTLE 250/1. After pressing ^E you just have to wait, on my machine about 3.5 minutes (not seconds). It seems that

if (rtc->ticks < ticksper)                          /* 1 sec yet? */
    return rtc->currd;

in sim_rtcn_calb is still getting called but it takes ages for rtc->ticks to reach ticksper. Only then the call to sim_poll_kbd() happens and ^E is detected. With set INT-CLOCK debug=CALIB I get after pressing ^E and a long wait

DBG(89639551)> INT-CLOCK CALIB: sim_start_timer_services() - restarting internal timer after 450451 instructions
DBG(134684552)> INT-CLOCK CALIB: sim_rtcn_calb(tmr=8) gratuitious keyboard poll after 1181573 msecs
DBG(134684552)> INT-CLOCK CALIB: gap too big: delta = 237670 - result: 450450

Simulation stopped, PC: 00000 (JMP 0000h)
deltecent commented 1 year ago

Actually it appears that ^E "works" even with SET THROTTLE 250/1. After pressing ^E you just have to wait, on my machine about 3.5 minutes (not seconds).

That aligns with my observation that although instructions are executed at the correct rate, service routines are called several times more slowly than they should be when using SET THROTTLE x/t.

psco commented 1 year ago

@markpizz writes

For a simulator without some sort of a console configured and no calibrated timers, the internal timer gratuitously calls sim_poll_kbd() if nothing else has called it.

As shown, the gratuitous call happens but it is useless as it does not come within a reasonable period after ^E was pressed (e.g. 5 seconds) but only after minutes. A fix for this defect would be appreciated.

deltecent commented 1 year ago

The problem, I believe, is with the internal timer. When SET THROTTLE x/t is used, it does not appear to get recalibrated to account for the t ms sleep every x instructions. I imagine if I could comprehend the code, it would be a simple fix, similar to what happens when THROTTLE is calculated using SET THROTTLE xK.

Maybe the solution to all this is to learn how to use my own calibrated timer and not rely on the internal timer. I also feel the "gratuitous" keyboard check should be in the sim, not in SCP. The less SCP does that impacts the internal workings of the simulator the better.

deltecent commented 1 year ago

The issue appears to be that as soon as the throttle logic starts sleeping the host cpu, all of the timers need to be adjusted to account for the reduced instructions per second due to the injection of a sleep interval. This adjustment happens when SIM THROTTLE 250K throttle calibration is complete, but doesn't happen if the throttle is forced through SET THROTTLE x/t. Without the timers being adjusted, they still assume maximum instructions per second rather than the throttled value which causes extreme service dispatch times (including gratuitous keyboard checks).

The following appears to fix it for me.

% git diff sim_timer.c
diff --git a/sim_timer.c b/sim_timer.c
index 4028a687..e909bf7a 100644
--- a/sim_timer.c
+++ b/sim_timer.c
@@ -680,6 +680,7 @@ static void _rtcn_configure_calibrated_clock (int32 newtmr);
 static t_bool _sim_coschedule_cancel (UNIT *uptr);
 static t_bool _sim_wallclock_cancel (UNIT *uptr);
 static t_bool _sim_wallclock_is_active (UNIT *uptr);
+static void _sim_timer_adjust_cal(void);
 t_stat sim_timer_show_idle_mode (FILE* st, UNIT* uptr, int32 val, CONST void *  desc);

@@ -1753,9 +1754,10 @@ else {
         sim_throt_wait = sim_throt_val;
         }
     }
-if (sim_throt_type == SIM_THROT_SPC)    /* Set initial value while correct one is determined */
+if (sim_throt_type == SIM_THROT_SPC) {    /* Set initial value while correct one is determined */
     sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)sim_throt_sleep_time);
-else
+    _sim_timer_adjust_cal();
+} else
     sim_throt_cps = sim_precalibrate_ips;
 return SCPE_OK;
 }
@@ -1960,21 +1962,7 @@ switch (sim_throt_state) {
             sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Throttle values a_cps = %f, d_cps = %f, wait = %d, sleep = %d ms\n", 
                                                 a_cps, d_cps, sim_throt_wait, sim_throt_sleep_time);
             sim_throt_cps = d_cps;                  /* save the desired rate */
-            /* Run through all timers and adjust the calibration for each */
-            /* one that is running to reflect the throttle rate */
-            for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
-                rtc = &rtcs[tmr];
-                if (rtc->hz) {                                      /* running? */
-                    rtc->currd = (int32)(sim_throt_cps / rtc->hz);/* use throttle calibration */
-                    rtc->ticks = rtc->hz - 1;                     /* force clock calibration on next tick */
-                    rtc->rtime = sim_throt_ms_start - 1000 + 1000/rtc->hz;/* adjust calibration parameters to reflect throttled rate */
-                    rtc->gtime = sim_throt_inst_start - sim_throt_cps + sim_throt_cps/rtc->hz;
-                    rtc->nxintv = 1000;
-                    rtc->based = rtc->currd;
-                    if (rtc->clock_unit)
-                        sim_activate_abs (rtc->clock_unit, rtc->currd);/* reschedule next tick */
-                    }
-                }
+           _sim_timer_adjust_cal();
             }
         break;

@@ -2038,6 +2026,30 @@ sim_activate (uptr, sim_throt_wait);                    /* reschedule */
 return SCPE_OK;
 }

+static void _sim_timer_adjust_cal(void)
+{
+    int32 tmr;
+    RTC *rtc = NULL;
+
+    /* Run through all timers and adjust the calibration for each */
+    /* one that is running to reflect the throttle rate */
+    for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
+        rtc = &rtcs[tmr];
+
+        if (rtc->hz) {                                      /* running? */
+            rtc->currd = (int32)(sim_throt_cps / rtc->hz);/* use throttle calibration */
+            rtc->ticks = rtc->hz - 1;                     /* force clock calibration on next tick */
+            rtc->rtime = sim_throt_ms_start - 1000 + 1000/rtc->hz;/* adjust calibration parameters to reflect throttled rate */
+            rtc->gtime = sim_throt_inst_start - sim_throt_cps + sim_throt_cps/rtc->hz;
+            rtc->nxintv = 1000;
+            rtc->based = rtc->currd;
+
+            if (rtc->clock_unit)
+                sim_activate_abs (rtc->clock_unit, rtc->currd);/* reschedule next tick */
+        }
+    }
+}
+
 /* Clock assist activites */
 t_stat sim_timer_tick_svc (UNIT *uptr)
 {

With this change, all of the JAIR's timing loops work during boot:

Altair 8800 (Z80) simulator Open SIMH V4.1-0 Current        git commit id: 7539b112+uncommitted-changes

ALTAIR/IMSAI 8080 CPU BOARD BOOT LOADER - Josh Bensadon v2.5 Sep 3, 2018
<D> -SD Card Directory
<R> -RAM Test
<V> -View Load
> .....
BOOT BIOS.HEX, INIT_FAT 
Init SD Type#1 ACMD41+MBR S Type06 PBR S VOL=JAIR        SYS=FAT16   
LOADING FILE BIOS.HEX -EXISTS
FILE SIZE=0x00004AA7

Execute at:DB00

ALTAIR/IMSAI 8080 CPU BOARD - Josh Bensadon v2.6 (Oct 2016)

Init SD Type#1 ACMD41+MBR S Type06 PBR S VOL=JAIR        SYS=FAT16   
CPMDISKS.TXT -EXISTS

M - Monitor
C - Boot CP/M
  1 - Disk A = DISK-A.BIN -EXISTS = 8" SSSD 250K
  2 - Disk B = DISK-B.BIN -EXISTS = 8" SSSD 250K
  3 - Disk C = DISK-C.BIN -EXISTS = 8" SSSD 250K
  4 - Disk D = DISK-D.BIN -EXISTS = 8" SSSD 250K
>c
ALTAIR/IMSAI CPU CARD. 61K CP/M 2.2

WBOOT

a>ls
Name    Ext Bytes   Name    Ext Bytes   Name    Ext Bytes   Name    Ext Bytes
ASM     COM    8K | ED      COM    8K | MOVCPM2 COM   11K | VIEW    COM    1K
BIOS    ASM   12K | GENMOD  COM    2K | NSWP    COM   12K | WM      COM   11K
CPM48   COM    9K | LOAD    COM    2K | PIP     COM    8K | WM      HLP    3K
CPMM48  COM    9K | LS      COM    3K | RLOCBIOSCOM    3K | XMODEM  CFG    1K
DDT     COM    5K | MBASIC  COM   24K | STAT    COM    6K | XMODEM  COM    8K
DEBLOCK ASM   10K | MON-32K COM    4K | STDBIOS ASM   16K | XSUB    COM    1K
DISKDEF LIB    7K | MONV18  COM    5K | SUBMIT  COM    2K
DUMP    ASM    5K | MONV18  HEX   13K | SURVEY  COM    2K
DUMP    COM    1K | MOVCPM  COM   10K | SYSGEN  COM    1K
33 File(s), occupying 223K of 241K total capacity
30 directory entries and 18K bytes remain on A:
a>mbasic
BASIC-80 Rev. 5.21
[CP/M Version]
Copyright 1977-1981 (C) by Microsoft
Created: 28-Jul-81
31800 Bytes free
Ok

Hopefully @markpizz will chime in.

deltecent commented 10 months ago

Possibly fixed with #336