From 7455d2812696e329b4d6870db75a7b2e4a493bf8 Mon Sep 17 00:00:00 2001 From: Seth Morabito Date: Sat, 13 Apr 2019 11:36:02 -0700 Subject: [PATCH] 3b2: Soft-power shutdown via TIMER command This change enables the simulator to be shut down cleanly via a soft-power shutdown command. This is implemented in the real 3B2/400 through the sanity timer, which, if it reaches zero, sets a bus timeout flag in the CSR and issues an interrupt at IPL 15. The operating system (System V UNIX) treats this as a shutdown request and enters runlevel 0. To use this change in a SIMH startup script, for example to implement a 3B2 simulator as a service, one could add these commands: # [... simulator setup ...] BOOT SET TIMER SHUTDOWN CONTINUE EXIT On catching a SIGTERM, SIGINT, or SIGHUP, the simulator would return to SCP control, set the soft power shutdown flag, and then continue simulator execution. After the system is cleanly shut down, the simulator would then exit back to the operating system. --- 3B2/3b2_cpu.c | 2 +- 3B2/3b2_sysdev.c | 31 ++++++++++++++++++++++++++++++- 3B2/3b2_sysdev.h | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index c41e06eb..b4de1283 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -3455,7 +3455,7 @@ static void cpu_calc_ints() cpu_int_ipl = cpu_int_vec = CPU_ID_IF_IPL; } else if ((csr_data & CSRUART) || (csr_data & CSRDMA)) { cpu_int_ipl = cpu_int_vec = CPU_IU_DMA_IPL; - } else if (csr_data & CSRCLK) { + } else if ((csr_data & CSRCLK) || (csr_data & CSRTIMO)) { cpu_int_ipl = cpu_int_vec = CPU_TMR_IPL; } else { cpu_int_ipl = cpu_int_vec = 0; diff --git a/3B2/3b2_sysdev.c b/3B2/3b2_sysdev.c index 1414992c..58c046a7 100644 --- a/3B2/3b2_sysdev.c +++ b/3B2/3b2_sysdev.c @@ -433,8 +433,14 @@ REG timer_reg[] = { { NULL } }; +MTAB timer_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, NULL, "SHUTDOWN", + &timer_set_shutdown, NULL, NULL, "Soft Power Shutdown" }, + { 0 } +}; + DEVICE timer_dev = { - "TIMER", timer_unit, timer_reg, NULL, + "TIMER", timer_unit, timer_reg, timer_mod, 1, 16, 8, 4, 16, 32, NULL, NULL, &timer_reset, NULL, NULL, NULL, NULL, @@ -468,6 +474,23 @@ t_stat timer_reset(DEVICE *dptr) { return SCPE_OK; } +t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char* cptr, void* desc) +{ + struct timer_ctr *sanity = timer_unit[0].tmr; + + sim_debug(EXECUTE_MSG, &timer_dev, + "[%08x] Setting sanity timer to 0 for shutdown.\n", R[NUM_PC]); + + sanity->val = 0; + csr_data &= ~CSRCLK; + csr_data |= CSRTIMO; + + return SCPE_OK; +} + +/* + * Sanity Timer + */ t_stat timer0_svc(UNIT *uptr) { struct timer_ctr *ctr; @@ -486,6 +509,9 @@ t_stat timer0_svc(UNIT *uptr) return SCPE_OK; } +/* + * Interval Timer + */ t_stat timer1_svc(UNIT *uptr) { struct timer_ctr *ctr; @@ -505,6 +531,9 @@ t_stat timer1_svc(UNIT *uptr) return SCPE_OK; } +/* + * Bus Timeout Timer + */ t_stat timer2_svc(UNIT *uptr) { struct timer_ctr *ctr; diff --git a/3B2/3b2_sysdev.h b/3B2/3b2_sysdev.h index 63394a8f..8f749150 100644 --- a/3B2/3b2_sysdev.h +++ b/3B2/3b2_sysdev.h @@ -69,6 +69,7 @@ void timer_write(uint32 pa, uint32 val, size_t size); t_stat timer0_svc(UNIT *uptr); t_stat timer1_svc(UNIT *uptr); t_stat timer2_svc(UNIT *uptr); +t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char *cptr, void *desc); /* CSR */ t_stat csr_svc(UNIT *uptr);