From e370b9e78e25e8d765845050867468b9b915475d Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Tue, 20 Sep 2016 20:34:22 -0700 Subject: [PATCH] HP3000: Third HP 3000 release from Dave Bryan --- HP3000/hp3000_atc.c | 70 ++-- HP3000/hp3000_clk.c | 80 ++-- HP3000/hp3000_cpu.c | 841 ++++++++++++++++++++++++++++++++------ HP3000/hp3000_cpu.h | 118 +++++- HP3000/hp3000_cpu_base.c | 89 +++- HP3000/hp3000_cpu_ims.h | 20 +- HP3000/hp3000_defs.h | 5 +- HP3000/hp3000_diag.txt | 42 +- HP3000/hp3000_ds.c | 6 +- HP3000/hp3000_io.h | 24 +- HP3000/hp3000_iop.c | 76 +++- HP3000/hp3000_lp.c | 297 +++++++------- HP3000/hp3000_mpx.c | 6 +- HP3000/hp3000_ms.c | 6 +- HP3000/hp3000_release.txt | 185 ++++++++- HP3000/hp3000_scmb.c | 5 +- HP3000/hp3000_sel.c | 33 +- HP3000/hp3000_sys.c | 242 ++++++----- HP3000/hp_disclib.c | 6 +- doc/hp3000_doc.doc | Bin 354304 -> 365056 bytes 20 files changed, 1632 insertions(+), 519 deletions(-) diff --git a/HP3000/hp3000_atc.c b/HP3000/hp3000_atc.c index d864405d..3d99590e 100644 --- a/HP3000/hp3000_atc.c +++ b/HP3000/hp3000_atc.c @@ -26,6 +26,9 @@ ATCD,ATCC HP 30032B Asynchronous Terminal Controller + 16-Sep-16 JDB Fixed atcd_detach to skip channel cancel if SIM_SW_REST + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG + 20-Jul-16 JDB Corrected poll_unit "wait" field initializer. 26-Jun-16 JDB Removed tmxr_set_modem_control_passthru call in atcc_reset 09-Jun-16 JDB Added casts for ptrdiff_t to int32 values 16-May-16 JDB Fixed interrupt mask setting @@ -879,23 +882,23 @@ static DIB atcc_dib = { */ static UNIT atcd_unit [UNIT_COUNT] = { - { UDATA (&line_service, TT_MODE_7P | UNIT_LOCALACK | UNIT_CAPSLOCK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, - { UDATA (&poll_service, UNIT_ATTABLE | UNIT_DIS | UNIT_IDLE, POLL_TIME) } /* multiplexer poll unit */ + { UDATA (&line_service, TT_MODE_7P | UNIT_LOCALACK | UNIT_CAPSLOCK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, + { UDATA (&poll_service, UNIT_ATTABLE | UNIT_DIS | UNIT_IDLE, 0), POLL_TIME } /* multiplexer poll unit */ }; static UNIT atcc_unit [] = { /* a dummy unit to satisfy SCP requirements */ @@ -935,7 +938,8 @@ static REG atcd_reg [] = { { BRDATA (SPARM, send_param, 8, 16, SEND_CHAN_COUNT) }, { BRDATA (SBUFR, send_buffer, 8, 16, SEND_CHAN_COUNT), REG_A }, { FLDATA (POLL, atc_is_polling, 0), REG_HRO }, - { SRDATA (DIB, atcd_dib, REG_HRO) }, + + DIB_REGS (atcd_dib), { NULL } }; @@ -959,7 +963,7 @@ static REG atcc_reg [] = { { FBDATA (MS2, cntl_param, 1, TERM_COUNT, PV_RZRO) }, { FBDATA (MS1, cntl_param, 0, TERM_COUNT, PV_RZRO) }, - { SRDATA (DIB, atcc_dib, REG_HRO) }, + DIB_REGS (atcc_dib), { NULL } }; @@ -1810,14 +1814,17 @@ return status; Normally, this routine is called by the DETACH ATCD command, which is equivalent to DETACH ATCD0. However, it may be called with other units in - two cases. + three cases. A DETACH ALL command will call us for unit 16 (the poll unit) if it is - attached. Also, during simulator shutdown, we will be called for units 0-15 - (detach_all in scp.c calls the detach routines of all units that do NOT have - UNIT_ATTABLE), as well as for unit 16 if it is attached. In both cases, it - is imperative that we return SCPE_OK; otherwise any remaining device detaches - will not be performed. + attached. A RESTORE command also will call us for unit 16 if it is attached. + In the latter case, the terminal channels will have already been rescheduled + as appropriate, so canceling them is skipped. Also, during simulator + shutdown, we will be called for units 0-15 (detach_all in scp.c calls the + detach routines of all units that do NOT have UNIT_ATTABLE), as well as for + unit 16 if it is attached. In all cases, it is imperative that we not reject + the request for unit 16; otherwise any remaining device detaches will not be + performed. */ static t_stat atcd_detach (UNIT *uptr) @@ -1825,13 +1832,14 @@ static t_stat atcd_detach (UNIT *uptr) uint32 channel; t_stat status = SCPE_OK; -if (uptr == line_unit || uptr == &poll_unit) { /* if we're detaching the base unit or poll unit */ - status = tmxr_detach (&atcd_mdsc, &poll_unit); /* then detach the listening port */ +if (uptr == line_unit || uptr == &poll_unit) { /* if we're detaching the base unit or poll unit */ + status = tmxr_detach (&atcd_mdsc, &poll_unit); /* then detach the listening port */ - for (channel = 0; channel < TERM_COUNT; channel++) { /* for each terminal channel */ - atcd_ldsc [channel].rcve = FALSE; /* disable reception */ - sim_cancel (&line_unit [channel]); /* and cancel any transfer in progress */ - } + if ((sim_switches & SIM_SW_REST) == 0) /* if this is not a RESTORE call */ + for (channel = 0; channel < TERM_COUNT; channel++) { /* then for each terminal channel */ + atcd_ldsc [channel].rcve = FALSE; /* disable reception */ + sim_cancel (&line_unit [channel]); /* and cancel any transfer in progress */ + } } return status; diff --git a/HP3000/hp3000_clk.c b/HP3000/hp3000_clk.c index 68b9ad9f..35673d0a 100644 --- a/HP3000/hp3000_clk.c +++ b/HP3000/hp3000_clk.c @@ -25,7 +25,9 @@ CLK HP 30135A System Clock/Fault Logging Interface - 08-Jul-16 JDB Preset the unit wait field + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG + 11-Jul-16 JDB Change "clk_unit" from a UNIT to an array of one UNIT + 08-Jul-16 JDB Added REG entry to save the unit wait field 09-Jun-16 JDB Clarified the IRQ FF set code in DRESETINT 08-Jun-16 JDB Corrected %d format to %u for unsigned values 21-Mar-16 JDB Changed inbound_value and outbound_value types to HP_WORD @@ -392,28 +394,32 @@ static DIB clk_dib = { /* Unit list */ -static UNIT clk_unit = { - UDATA (&clk_service, UNIT_IDLE | UNIT_CALTIME, 0), mS (1) +static UNIT clk_unit [] = { + { UDATA (&clk_service, UNIT_IDLE | UNIT_CALTIME, 0) } }; /* Register list */ static REG clk_reg [] = { -/* Macro Name Location Width Offset Flags */ -/* ------ ------ --------------- ----- ------ ------- */ - { ORDATA (CNTL, control_word, 16) }, - { ORDATA (STAT, status_word, 16) }, - { ORDATA (COUNT, count_register, 16) }, - { ORDATA (LIMIT, limit_register, 16) }, - { ORDATA (RATE, rate, 3) }, - { FLDATA (SYSIRQ, system_irq, 0) }, - { FLDATA (LIMIRQ, limit_irq, 0) }, - { FLDATA (OVFIRQ, lost_tick_irq, 0) }, - { DRDATA (SCALE, prescaler, 16), REG_HRO }, - { DRDATA (INCR, increment, 16), REG_HRO }, - { FLDATA (COSOK, coschedulable, 0), REG_HRO }, - { FLDATA (COSCH, coscheduled, 0), REG_HRO }, - { SRDATA (DIB, clk_dib, REG_HRO) }, +/* Macro Name Location Width Offset Flags */ +/* ------ ------ ----------------- ----- ------ ------------------ */ + { ORDATA (CNTL, control_word, 16) }, + { ORDATA (STAT, status_word, 16) }, + { ORDATA (COUNT, count_register, 16) }, + { ORDATA (LIMIT, limit_register, 16) }, + { ORDATA (RATE, rate, 3) }, + { FLDATA (SYSIRQ, system_irq, 0) }, + { FLDATA (LIMIRQ, limit_irq, 0) }, + { FLDATA (OVFIRQ, lost_tick_irq, 0) }, + + { DRDATA (SCALE, prescaler, 16), REG_HRO }, + { DRDATA (INCR, increment, 16), REG_HRO }, + { FLDATA (COSOK, coschedulable, 0), REG_HRO }, + { FLDATA (COSCH, coscheduled, 0), REG_HRO }, + { DRDATA (UWAIT, clk_unit [0].wait, 32), PV_LEFT | REG_HRO }, + + DIB_REGS (clk_dib), + { NULL } }; @@ -445,7 +451,7 @@ static DEBTAB clk_deb [] = { DEVICE clk_dev = { "CLK", /* device name */ - &clk_unit, /* unit array */ + clk_unit, /* unit array */ clk_reg, /* register array */ clk_mod, /* modifier array */ 1, /* number of units */ @@ -494,11 +500,11 @@ void clk_update_counter (void) int32 elapsed, ticks; if (coscheduled) { /* if the clock is coscheduled, then adjust the count */ - elapsed = clk_unit.wait /* the elapsed time is the original wait time */ - - sim_activate_time (&clk_unit); /* less the time remaining before the next service */ + elapsed = clk_unit [0].wait /* the elapsed time is the original wait time */ + - sim_activate_time (&clk_unit [0]); /* less the time remaining before the next service */ - ticks = (elapsed * CLK_MULTIPLIER) / clk_unit.wait /* the adjustment is the elapsed fraction of the multiplier */ - - (CLK_MULTIPLIER - increment); /* less the amount of any adjustment already made */ + ticks = (elapsed * CLK_MULTIPLIER) / clk_unit [0].wait /* the adjustment is the elapsed fraction of the multiplier */ + - (CLK_MULTIPLIER - increment); /* less the amount of any adjustment already made */ count_register = count_register + ticks & R_MASK; /* update the clock counter with rollover */ increment = increment - ticks; /* and reduce the amount remaining to add at service */ @@ -568,17 +574,17 @@ while (working_set) { if (control_word & CN_RESET_LOAD_SEL) { /* if the reset/load selector is set */ rate = CN_RATE (control_word); /* then load the clock rate */ - if (clk_unit.flags & UNIT_CALTIME) /* if in calibrated timing mode */ + if (clk_unit [0].flags & UNIT_CALTIME) /* if in calibrated timing mode */ prescaler = scale [rate]; /* then set the prescaler */ else /* otherwise */ prescaler = 1; /* the prescaler isn't used */ - sim_cancel (&clk_unit); /* changing the rate restarts the timing divider */ + sim_cancel (&clk_unit [0]); /* changing the rate restarts the timing divider */ - if (rate > 0) { /* if the rate is valid */ - clk_unit.wait = delay [rate]; /* then set the initial service delay */ - sim_rtcn_init (clk_unit.wait, TMR_CLK); /* initialize the clock */ - resync_clock (); /* and reschedule the service */ + if (rate > 0) { /* if the rate is valid */ + clk_unit [0].wait = delay [rate]; /* then set the initial service delay */ + sim_rtcn_init (clk_unit [0].wait, TMR_CLK); /* initialize the clock */ + resync_clock (); /* and reschedule the service */ } } @@ -915,22 +921,22 @@ static void resync_clock (void) coschedulable = (ticks [rate] == 1000 /* the clock can be coscheduled if the rate */ && limit_register == 100); /* is 1 msec and the limit is 100 ticks */ -if (clk_unit.flags & UNIT_CALTIME /* if the clock is in calibrated timing mode */ - && coschedulable /* and may be coscheduled with the process clock */ - && cpu_is_calibrated) { /* and the process clock is calibrated */ - clk_unit.wait = sim_activate_time (cpu_pclk_uptr); /* then synchronize with it */ - coscheduled = TRUE; /* the clock is coscheduled with the process clock */ +if (clk_unit [0].flags & UNIT_CALTIME /* if the clock is in calibrated timing mode */ + && coschedulable /* and may be coscheduled with the process clock */ + && cpu_is_calibrated) { /* and the process clock is calibrated */ + clk_unit [0].wait = sim_activate_time (cpu_pclk_uptr); /* then synchronize with it */ + coscheduled = TRUE; /* the clock is coscheduled with the process clock */ } else { /* otherwise */ - clk_unit.wait = delay [rate]; /* set up an independent clock */ + clk_unit [0].wait = delay [rate]; /* set up an independent clock */ coscheduled = FALSE; /* the clock is not coscheduled with the process clock */ } dprintf (clk_dev, DEB_PSERV, "Rate %s delay %d service rescheduled\n", - rate_name [rate], clk_unit.wait); + rate_name [rate], clk_unit [0].wait); -sim_activate_abs (&clk_unit, clk_unit.wait); /* restart the clock */ +sim_activate_abs (&clk_unit [0], clk_unit [0].wait); /* restart the clock */ return; } diff --git a/HP3000/hp3000_cpu.c b/HP3000/hp3000_cpu.c index 6bfe21d5..9b4450cb 100644 --- a/HP3000/hp3000_cpu.c +++ b/HP3000/hp3000_cpu.c @@ -25,6 +25,11 @@ CPU HP 3000 Series III Central Processing Unit + 01-Sep-16 JDB Add power fail/power restore support + 23-Aug-16 JDB Add module interrupt support + 14-Jul-16 JDB Implemented the cold dump process + Changed "loading" EXEC_STATE to "waiting" + 11-Jul-16 JDB Change "cpu_unit" from a UNIT to an array of one UNIT 08-Jun-16 JDB Corrected %d format to %u for unsigned values 16-May-16 JDB ACCESS_PROPERTIES.name is now a pointer-to-constant 13-May-16 JDB Modified for revised SCP API function parameter types @@ -345,16 +350,16 @@ When the simulator examines the bit patterns of instructions to execute, each will fall into one of four categories: - 1. Defined (canonical) instruction encodings. + 1. Defined (canonical) instruction encodings. - 2. Undefined (non-canonical) instruction encodings, where reserved fields - are "don't care" bits (e.g., MOVE). + 2. Undefined (non-canonical) instruction encodings, where reserved fields + are "don't care" bits (e.g., MOVE). - 3. Undefined (non-canonical) instruction encodings, where reserved fields - are decoded (e.g., IXIT). + 3. Undefined (non-canonical) instruction encodings, where reserved fields + are decoded (e.g., IXIT). - 4. Unimplemented instruction encodings (e.g., stack opcode 072 or EADD with - no EIS installed). + 4. Unimplemented instruction encodings (e.g., stack opcode 072 or EADD with + no EIS installed). When examining memory or register values in instruction-mnemonic form, the names of the canonical instructions in category 1 are displayed in uppercase, @@ -690,6 +695,30 @@ extern DEVICE sel_dev; /* Selector Channel */ #define UNIT_OPTS (UNIT_EIS) /* the standard equipment feature set */ +#define CPU_IO_RESET 0 /* reset CPU and all I/O devices */ +#define IO_RESET 1 /* reset just the I/O devices */ + +#define CNTL_BASE 8 /* the radix for the cold dump control byte */ +#define CNTL_MAX 0377 /* the maximum cold dump control byte value */ + +#define SIO_JUMP 0000000u /* Jump unconditionally */ +#define SIO_SBANK 0014000u /* Set bank */ +#define SIO_ENDIN 0034000u /* End with interrupt */ +#define SIO_CNTL 0040000u /* Control */ +#define SIO_WRITE 0060000u /* Write 4096 words */ +#define SIO_READ 0077760u /* Read 16 words */ + +#define MS_CN_GAP 0000005u /* Write Gap */ +#define MS_CN_WRR 0000004u /* Write Record */ +#define MS_CN_RST 0000011u /* Rewind and Reset */ +#define MS_CN_BSR 0000012u /* Backspace Record */ +#define MS_CN_WFM 0000015u /* Write File Mark */ + +#define MS_ST_PROTECTED 0001000u /* write protected */ +#define MS_ST_READY 0000400u /* unit ready */ + +#define MS_ST_MASK (MS_ST_PROTECTED | MS_ST_READY) + /* CPU global SCP data definitions */ @@ -723,6 +752,7 @@ HP_WORD STA = 0; /* status register */ HP_WORD SWCH = 0; /* switch register */ HP_WORD CPX1 = 0; /* run-mode interrupt flags register */ HP_WORD CPX2 = 0; /* halt-mode interrupt flags register */ +HP_WORD MOD = 0; /* module control register */ HP_WORD PCLK = 0; /* process clock register */ HP_WORD CNTR = 0; /* microcode counter */ @@ -779,13 +809,14 @@ const HP_WORD cpu_ccb_table [256] = { /* CPU global state */ -jmp_buf cpu_save_env; /* the saved environment for microcode aborts */ -uint32 cpu_stop_flags; /* the simulation stop flag set */ +jmp_buf cpu_save_env; /* the saved environment for microcode aborts */ +uint32 cpu_stop_flags; /* the simulation stop flag set */ -EXEC_STATE cpu_micro_state = halted; /* the microcode execution state */ -t_bool cpu_base_changed = FALSE; /* TRUE if any base register is changed */ -t_bool cpu_is_calibrated = TRUE; /* TRUE if the process clock is calibrated */ -UNIT *cpu_pclk_uptr = &cpu_unit; /* a (constant) pointer to the process clock unit */ +POWER_STATE cpu_power_state = power_on; /* the power supply state */ +EXEC_STATE cpu_micro_state = halted; /* the microcode execution state */ +t_bool cpu_base_changed = FALSE; /* TRUE if any base register is changed */ +t_bool cpu_is_calibrated = TRUE; /* TRUE if the process clock is calibrated */ +UNIT *cpu_pclk_uptr = &cpu_unit [0]; /* a (constant) pointer to the process clock unit */ /* CPU local state */ @@ -793,6 +824,7 @@ UNIT *cpu_pclk_uptr = &cpu_unit; /* a (constant) pointer to the p static uint32 sim_stops = 0; /* the current simulation stop flag settings */ static uint32 cpu_speed = 1; /* the CPU speed, expressed as a multiplier of a real machine */ static uint32 pclk_increment = 1; /* the process clock increment per event service */ +static uint32 dump_control = 0002006u; /* the cold dump control word (default CNTL = 4, DEVNO = 6 */ /* CPU local data structures */ @@ -800,9 +832,7 @@ static uint32 pclk_increment = 1; /* the process clock increment p /* Main memory */ -#define MEMSIZE (cpu_unit.capac) /* the current memory size in 16-bit words */ - -static MEMORY_WORD *M = NULL; /* the pointer to the main memory allocation */ +static MEMORY_WORD *M = NULL; /* the pointer to the main memory allocation */ /* Interrupt classification names */ @@ -939,13 +969,16 @@ static t_stat cpu_reset (DEVICE *dptr); static t_stat cpu_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches); static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches); -static t_stat set_stops (UNIT *uptr, int32 option, CONST char *cptr, void *desc); -static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc); -static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc); -static t_stat set_option (UNIT *uptr, int32 new_option, CONST char *cptr, void *desc); +static t_stat set_stops (UNIT *uptr, int32 option, CONST char *cptr, void *desc); +static t_stat set_dump (UNIT *uptr, int32 option, CONST char *cptr, void *desc); +static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc); +static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc); +static t_stat set_option (UNIT *uptr, int32 new_option, CONST char *cptr, void *desc); +static t_stat set_pfars (UNIT *uptr, int32 setting, CONST char *cptr, void *desc); -static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc); -static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat show_dump (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc); /* CPU local utility routine declarations */ @@ -969,8 +1002,10 @@ static t_stat machine_instruction (void); size via the MEMSIZE macro, which references the "capac" field. */ -UNIT cpu_unit = { - UDATA (&cpu_service, UNIT_FIX | UNIT_BINK | UNIT_IDLE | UNIT_CALTIME, 0), PCLK_PERIOD * PCLK_MULTIPLIER +#define UNIT_FLAGS (UNIT_PFARS | UNIT_CALTIME) + +UNIT cpu_unit [] = { + { UDATA (&cpu_service, UNIT_FLAGS | UNIT_FIX | UNIT_BINK | UNIT_IDLE, 0), PCLK_PERIOD * PCLK_MULTIPLIER } }; @@ -998,37 +1033,39 @@ UNIT cpu_unit = { */ static REG cpu_reg [] = { -/* Macro Name Location Width Flags */ -/* ------ ------ ------------ ----- ------------------------ */ - { ORDATA (CIR, CIR, 16), REG_M | REG_RO | REG_FIT }, /* current instruction register */ - { ORDATA (NIR, NIR, 16), REG_M | REG_RO | REG_FIT }, /* next instruction register */ - { ORDATA (PB, PB, 16), REG_FIT }, /* program base register */ - { ORDATA (P, P, 16), REG_FIT }, /* program counter register */ - { ORDATA (PL, PL, 16), REG_FIT }, /* program limit register */ - { ORDATA (PBANK, PBANK, 4), REG_FIT }, /* program segment bank register */ - { ORDATA (DL, DL, 16), REG_FIT }, /* data limit register */ - { ORDATA (DB, DB, 16), REG_FIT }, /* data base register */ - { ORDATA (DBANK, DBANK, 4), REG_FIT }, /* data segment bank register */ - { ORDATA (Q, Q, 16), REG_FIT }, /* stack marker register */ - { ORDATA (SM, SM, 16), REG_FIT }, /* stack memory register */ - { ORDATA (SR, SR, 3), REG_FIT }, /* stack register counter */ - { ORDATA (Z, Z, 16), REG_FIT }, /* stack limit register */ - { ORDATA (SBANK, SBANK, 4), REG_FIT }, /* stack segment bank register */ - { ORDATA (RA, TR [0], 16), REG_A | REG_FIT }, /* top of stack register */ - { ORDATA (RB, TR [1], 16), REG_A | REG_FIT }, /* top of stack - 1 register */ - { ORDATA (RC, TR [2], 16), REG_A | REG_FIT }, /* top of stack - 2 register */ - { ORDATA (RD, TR [3], 16), REG_A | REG_FIT }, /* top of stack - 3 register */ - { ORDATA (X, X, 16), REG_A | REG_FIT }, /* index register */ - { ORDATA (STA, STA, 16), REG_S | REG_B | REG_FIT }, /* status register */ - { ORDATA (SWCH, SWCH, 16), REG_A | REG_FIT }, /* switch register */ - { ORDATA (CPX1, CPX1, 16), REG_B | REG_FIT }, /* run-mode interrupt flags */ - { ORDATA (CPX2, CPX2, 16), REG_B | REG_FIT }, /* halt-mode interrupt flags */ - { ORDATA (PCLK, PCLK, 16), REG_FIT }, /* process clock register */ - { ORDATA (CNTR, CNTR, 6), REG_HRO | REG_FIT }, /* microcode counter */ +/* Macro Name Location Radix Width Offset Flags */ +/* ------ ------- ------------ ----- ----- ------ ------------------------ */ + { ORDATA (CIR, CIR, 16), REG_M | REG_RO | REG_FIT }, /* current instruction register */ + { ORDATA (NIR, NIR, 16), REG_M | REG_RO | REG_FIT }, /* next instruction register */ + { ORDATA (PB, PB, 16), REG_FIT }, /* program base register */ + { ORDATA (P, P, 16), REG_FIT }, /* program counter register */ + { ORDATA (PL, PL, 16), REG_FIT }, /* program limit register */ + { ORDATA (PBANK, PBANK, 4), REG_FIT }, /* program segment bank register */ + { ORDATA (DL, DL, 16), REG_FIT }, /* data limit register */ + { ORDATA (DB, DB, 16), REG_FIT }, /* data base register */ + { ORDATA (DBANK, DBANK, 4), REG_FIT }, /* data segment bank register */ + { ORDATA (Q, Q, 16), REG_FIT }, /* stack marker register */ + { ORDATA (SM, SM, 16), REG_FIT }, /* stack memory register */ + { ORDATA (SR, SR, 3), REG_FIT }, /* stack register counter */ + { ORDATA (Z, Z, 16), REG_FIT }, /* stack limit register */ + { ORDATA (SBANK, SBANK, 4), REG_FIT }, /* stack segment bank register */ + { ORDATA (RA, TR [0], 16), REG_A | REG_FIT }, /* top of stack register */ + { ORDATA (RB, TR [1], 16), REG_A | REG_FIT }, /* top of stack - 1 register */ + { ORDATA (RC, TR [2], 16), REG_A | REG_FIT }, /* top of stack - 2 register */ + { ORDATA (RD, TR [3], 16), REG_A | REG_FIT }, /* top of stack - 3 register */ + { ORDATA (X, X, 16), REG_A | REG_FIT }, /* index register */ + { ORDATA (STA, STA, 16), REG_S | REG_B | REG_FIT }, /* status register */ + { ORDATA (SWCH, SWCH, 16), REG_A | REG_FIT }, /* switch register */ + { ORDATA (CPX1, CPX1, 16), REG_B | REG_FIT }, /* run-mode interrupt flags */ + { ORDATA (CPX2, CPX2, 16), REG_B | REG_FIT }, /* halt-mode interrupt flags */ + { ORDATA (PCLK, PCLK, 16), REG_FIT }, /* process clock register */ + { ORDATA (CNTR, CNTR, 6), REG_HRO | REG_FIT }, /* microcode counter */ + { ORDATA (MOD, MOD, 16), REG_HRO | REG_FIT }, /* module control register */ - { ORDATA (WRU, sim_int_char, 8), REG_HRO }, /* SCP interrupt character */ - { ORDATA (BRK, sim_brk_char, 8), REG_HRO }, /* SCP break character */ - { ORDATA (DEL, sim_del_char, 8), REG_HRO }, /* SCP delete character */ + { DRDATA (POWER, cpu_power_state, 2), REG_HRO }, /* system power supply state */ + { ORDATA (WRU, sim_int_char, 8), REG_HRO }, /* SCP interrupt character */ + { ORDATA (BRK, sim_brk_char, 8), REG_HRO }, /* SCP break character */ + { ORDATA (DEL, sim_del_char, 8), REG_HRO }, /* SCP delete character */ { NULL } }; @@ -1045,6 +1082,9 @@ static MTAB cpu_mod [] = { { UNIT_EIS, UNIT_EIS, "EIS", NULL, &set_option, NULL, NULL }, { UNIT_EIS, 0, "no EIS", "NOEIS", NULL, NULL, NULL }, + { UNIT_PFARS, UNIT_PFARS, "auto-restart", "ARS", &set_pfars, NULL, NULL }, + { UNIT_PFARS, 0, "no auto-restart", "NOARS", &set_pfars, NULL, NULL }, + { UNIT_CALTIME, UNIT_CALTIME, "calibrated timing", "CALTIME", NULL, NULL, NULL }, { UNIT_CALTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, @@ -1059,8 +1099,13 @@ static MTAB cpu_mod [] = { { MTAB_XDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle, NULL }, { MTAB_XDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL, NULL }, + + { MTAB_XDV | MTAB_NMO, 0, "DUMP", "DUMPDEV", &set_dump, &show_dump, NULL }, + { MTAB_XDV, 1, NULL, "DUMPCTL", &set_dump, NULL, NULL }, + { MTAB_XDV | MTAB_NMO, 1, "STOPS", "STOP", &set_stops, &show_stops, NULL }, { MTAB_XDV, 0, NULL, "NOSTOP", &set_stops, NULL, NULL }, + { MTAB_XDV | MTAB_NMO, 0, "SPEED", NULL, NULL, &show_speed, NULL }, { 0 } @@ -1093,7 +1138,7 @@ static DEBTAB cpu_stop [] = { DEVICE cpu_dev = { "CPU", /* device name */ - &cpu_unit, /* unit array */ + cpu_unit, /* unit array */ cpu_reg, /* register array */ cpu_mod, /* modifier array */ 1, /* number of units */ @@ -1137,15 +1182,15 @@ DEVICE cpu_dev = { Execution is divided into four phases. First, the instruction prelude configures the simulation state to resume - execution. This involves verifying that there are no device conflicts (e.g., - two devices with the same device number), initializing the I/O processor and - channels, and setting the RUN switch if no other front panel switches are - pressed. These actions accommodate reconfiguration of the I/O device - settings and program counter while the simulator was stopped. The prelude - also responds to one command-line switch: if "-B" is specified, the current - set of simulation stop conditions is bypassed for the first instruction - executed. This allows, e.g., a PAUS instruction to be bypassed or an - unimplemented instruction trap to be taken. + execution. This involves verifying that system power is on and there are no + device conflicts (e.g., two devices with the same device number), + initializing the I/O processor and channels, and setting the RUN switch if no + other front panel switches are pressed. These actions accommodate + reconfiguration of the I/O device settings and program counter while the + simulator was stopped. The prelude also responds to one command-line switch: + if "-B" is specified, the current set of simulation stop conditions is + bypassed for the first instruction executed. This allows, e.g., a PAUS + instruction to be bypassed or an unimplemented instruction trap to be taken. Second, the microcode abort mechanism is set up. Microcode aborts utilize the "setjmp/longjmp" mechanism to transfer control out of the instruction @@ -1185,8 +1230,8 @@ DEVICE cpu_dev = { The HP 3000 is a microcoded machine. In hardware, the micromachine is always executing microinstructions, even when the CPU is "halted." The halt/run state is simply a flip-flop setting, reflected in bit 15 of the CPX2 - register, that determines whether the "halt-mode" or "run-mode" microprogram - is currently executing. + register and the RUN light on the front panel, that determines whether the + "halt-mode" or "run-mode" microprogram is currently executing. In simulation, the "cpu_micro_state" variable indicates the state of the micromachine, i.e., which section of the microcode it is executing, while @@ -1195,7 +1240,7 @@ DEVICE cpu_dev = { - running : the run-mode fetch-and-execute microcode is executing - paused : the run-mode PAUS instruction microcode is executing - - loading : the halt-mode COLD LOAD microcode is executing + - waiting : the halt-mode cold load or dump microcode is executing - halted : the halt-mode front panel microcode is executing Simulation provides a variety of stop conditions that break instruction @@ -1334,7 +1379,10 @@ if (sim_switches & SWMASK ('B')) /* if a simulation stop else /* otherwise */ cpu_stop_flags = sim_stops; /* set the stops as indicated */ -if (hp_device_conflict ()) /* if the check for device assignment consistency fails */ +if (cpu_power_state == power_off) /* if system power is off */ + status = STOP_POWER; /* then execution is not possible until restoration */ + +else if (hp_device_conflict ()) /* otherwise if device assignment is inconsistent */ status = SCPE_STOP; /* then inhibit execution */ else { /* otherwise */ @@ -1343,7 +1391,7 @@ else { /* otherwise */ sel_initialize (); /* and the selector channel */ if ((CPX2 & CPX2_IRQ_SET) == 0) /* if no halt-mode interrupt is present */ - CPX2 |= cpx2_RUNSWCH; /* then assume a RUN command */ + CPX2 |= cpx2_RUNSWCH; /* then assume a RUN command via STEP/CONT */ } @@ -1451,8 +1499,11 @@ if (abortval) { /* if a microcode abort case trap_Power_On: /* this trap executes on the ICS */ - status = SCPE_INCOMP; /* but is not implemented yet */ - label = 0; /* the trap handler is not called */ + cpu_setup_ics_irq (irq_Trap, trap); /* so set it up */ + cpu_power_state = power_on; /* and return to the power-on state */ + + if (CPX2 & cpx2_INHPFARS) /* if auto-restart is inhibited */ + status = STOP_ARSINH; /* then exit and wait for a manual restart */ break; } /* all cases are handled */ @@ -1497,8 +1548,9 @@ while (status == SCPE_OK) { /* execute until simulat device = iop_poll (); /* then poll to acknowledge the request */ if (cpu_micro_state == running) /* if the micromachine is running */ - if (CPX1 & CPX1_IRQ_SET) /* then if a run-mode interrupt is pending */ - cpu_run_mode_interrupt (device); /* then service it */ + if (CPX1 & CPX1_IRQ_SET /* then if a run-mode interrupt is pending */ + && cpu_power_state != power_failing) /* and power is not currently failing */ + cpu_run_mode_interrupt (device); /* then service it */ else if (sim_brk_summ /* otherwise if a breakpoint exists */ && sim_brk_test (TO_PA (PBANK, P - 1 & LA_MASK), /* at the next location */ @@ -1533,7 +1585,7 @@ while (status == SCPE_OK) { /* execute until simulat P = P + 1 & R_MASK; /* point to the following instruction */ - if (DEBUG_PRI (cpu_dev, DEB_INSTR)) { /* if instruction tracing is enabled */ + if (DPRINTING (cpu_dev, DEB_INSTR)) { /* if instruction tracing is enabled */ sim_eval [0] = CIR; /* then save the instruction that will be executed */ sim_eval [1] = NIR; /* and the following word for evaluation */ @@ -1584,6 +1636,10 @@ else if (cpu_micro_state == running) /* otherwise if it is ru cpu_micro_state = halted; /* halt the micromachine */ +if (cpu_power_state == power_failing /* if power is failing */ + && status == STOP_HALT) /* and we executed a HALT instruction */ + cpu_power_state = power_off; /* then power will be off when we return */ + dprintf (cpu_dev, cpu_dev.dctrl, BOV_FORMAT "simulation stop: %s\n", PBANK, P, STA, sim_error_text (status)); @@ -1591,6 +1647,228 @@ return status; /* return the reason for } +/* Execute the LOAD and DUMP commands. + + This command processing routine implements the cold load and cold dump + commands. The syntax is: + + LOAD { } + DUMP { } + + The is a number that is deposited into the SWCH register + before invoking the CPU cold load or cold dump facility. The CPU radix is + used to interpret the number; it defaults to octal. If the number is + omitted, the SWCH register value is not altered before loading or dumping. + + On entry, the "arg" parameter is "Cold_Load" for a LOAD command and + "Cold_Dump" for a DUMP command, and "buf" points at the remainder of the + command line. If characters exist on the command line, they are parsed, + converted to a numeric value, and stored in the SWCH register. Then the + CPU's cold load/dump routine is called to set up the CPU state. Finally, the + CPU is started to begin the requested action. + + + Implementation notes: + + 1. The RUN command uses the RU_CONT argument instead of RU_RUN so that the + run_cmd SCP routine will not reset all devices before entering the + instruction executor. The halt mode interrupt handlers for cold load and + cold dump reset the simulator as appropriate for their commands (i.e., + the CPU and all I/O devices, or just the I/O devices, respectively). +*/ + +t_stat cpu_cold_cmd (int32 arg, CONST char *buf) +{ +const char *cptr; +char gbuf [CBUFSIZE]; +t_stat status; +HP_WORD value; + +if (*buf != '\0') { /* if more characters exist on the command line */ + cptr = get_glyph (buf, gbuf, 0); /* then get the next glyph */ + + if (*cptr != '\0') /* if that does not exhaust the input */ + return SCPE_2MARG; /* then report that there are too many arguments */ + + value = (HP_WORD) get_uint (gbuf, cpu_dev.dradix, /* get the parameter value */ + D16_UMAX, &status); + + if (status == SCPE_OK) /* if a valid number was present */ + SWCH = value; /* then set it into the switch register */ + else /* otherwise */ + return status; /* return the error status */ + } + +else if (arg == Cold_Dump) /* otherwise if no dump value was given */ + SWCH = dump_control; /* then use the system control panel presets */ + +cpu_front_panel (SWCH, (PANEL_TYPE) arg); /* set up the cold load or dump microcode */ + +return run_cmd (RU_CONT, buf); /* execute the halt-mode routine */ +} + + +/* Execute the POWER commands. + + This command processing routine is called to initiate a power failure or + power restoration. The "cptr" parameter points to the power option keyword; + the "arg" parameter is not used. + + The routine processes commands of the form: + + POWER { FAIL | OFF | DOWN } + POWER { RESTORE | ON | UP } + + In simulation, the "cpu_power_state" global variable indicates the current + state of system power. The simulator starts in the power_on state. The + POWER FAIL command moves from the power_on to the power_failing state if the + CPU is running, or to the power_off state if it is not. Execution of a HALT + in the power_failing state moves to the power_off state. The POWER RESTORE + command moves from the power_off to the power_returning state if the CPU is + running, or to the power_on state if it is not. Execution of the power-on + trap moves from the power_returning to the power_on state. + + The POWER FAIL and POWER RESTORE commands are only valid in the power_on and + power_off states, respectively; otherwise, they print "Command not allowed." + + The four enumeration values model the states of the PON (power on) and PFW + (power-fail warning) hardware signals, as follows: + + PON PFW State Simulator Action + --- --- --------------- ---------------------------- + 1 0 power on executing normally + 1 1 power failing executing with cpx1_PFINTR + 0 1 power off will not execute + 0 0 power returning executing with trap_Power_On + + In microcode, the power-fail routine writes the current value of the CPX2 + register to the word following the last word of the ICS. This value is used + by the power-on routine to decide if the CPU was running (cpx2_RUN bit is + set) or halted at the time of the power failure. A power failure is + indicated by setting the cpx1_PFINTR bit in the CPX1 register; this causes an + interrupt to the power-failure routine in the operating system, which + performs an orderly shutdown followed by a programmed HALT to wait for power + to die. When power is restored, the power-on trap (trap_Power_On) is set up, + and then, if the PF/ARS switch is in the "enable" position, the trap is + taken, which restarts the operating system and any I/O that was in progress + when power failed. If the switch is in the "disable" position, the CPU + remains halted, and the trap is taken when the RUN button is pressed. + + The POWER commands are entered at the SCP prompt. If the machine was running + at the time of power failure, execution is resumed automatically to execute + the power-fail or power-restore OS routines. If the machine was halted when + the POWER commands were entered, the machine remains halted -- just the power + state changes. + + + Implementation notes: + + 1. In hardware, when the power fail interrupt is serviced, the microcode + sets the PWR INH flip-flop, which locks out the RUN switch and inhibits + all other halt-mode (CPX2) and run-mode (CPX1) interrupts until the CPU + is reset. This ensures that the software power-fail interrupt handler + executes unimpeded. In simulation, a POWER FAIL command with the CPU + running sets the power-fail interrupt bit in the CPX1 register and + resumes execution in the power_on state. When the interrupt is detected + by the "cpu_run_mode_interrupt" routine, the state is changed to + power_failing. In this state, run-mode interrupts are not recognized. + Halt-mode interrupts need no special handling, as the power state is + changed to power_off when the CPU halts. Therefore, the CPU is never in + a "halted-and-waiting-for-power-to-fade-away" state. + + 2. The RUN command uses the RU_CONT argument instead of RU_RUN so that the + run_cmd SCP routine will not reset all devices before entering the + instruction executor. The halt-mode interrupt handlers for cold load and + cold dump reset the simulator as appropriate for their commands (i.e., + the CPU and all I/O devices, or just the I/O devices, respectively). + + 3. In order to set up the power-on trap, this routine presses the RUN button + and continues the simulation. After the trap is set up in the + "sim_instr" routine, the halt-mode interrupt handler checks the PF/ARS + switch and stops simulation if auto-restart is disabled. +*/ + +t_stat cpu_power_cmd (int32 arg, CONST char *cptr) +{ +static CTAB options [] = { + { "FAIL", NULL, power_failing }, + { "RESTORE", NULL, power_returning }, + { "OFF", NULL, power_failing }, + { "ON", NULL, power_returning }, + { "DOWN", NULL, power_failing }, + { "UP", NULL, power_returning }, + { NULL } + }; + +char gbuf [CBUFSIZE]; +CTAB *ctptr; +HP_WORD zi, failure_cpx2; +t_stat status; + +if (cptr == NULL || *cptr == '\0') /* if there is no option word */ + return SCPE_2FARG; /* then report a missing argument */ + +cptr = get_glyph (cptr, gbuf, 0); /* parse (and upshift) the option specified */ +ctptr = find_ctab (options, gbuf); /* and look it up in the option table */ + +if (ctptr == NULL) /* if the option is not valid */ + status = SCPE_ARG; /* then report a bad argument */ + +else if (*cptr != '\0') /* otherwise if something follows the option */ + return SCPE_2MARG; /* then report too many arguments */ + +else if (ctptr->arg == power_failing) /* otherwise if a power-fail option was given */ + if (cpu_power_state != power_on) /* but the CPU power is not on */ + status = SCPE_NOFNC; /* then the command is not allowed */ + + else { /* otherwise the failure is valid */ + iop_assert_PFWARN (); /* so send a power-fail warning to all devices */ + + cpu_read_memory (absolute, ICS_Z, &zi); /* get the ICS stack limit */ + cpu_write_memory (absolute, zi + 1, CPX2); /* and save the CPX2 value in the following word */ + + if (CPX2 & cpx2_RUN) { /* if the CPU is currently running */ + CPX1 |= cpx1_PFINTR; /* then set the power-fail interrupt */ + CPX2 |= cpx2_RUNSWCH; /* and assume a RUN command */ + status = run_cmd (RU_CONT, cptr); /* and continue execution */ + } + + else { /* otherwise the CPU is currently halted */ + cpu_power_state = power_off; /* so remain halted in the "power is off" state */ + status = SCPE_OK; /* and return command success */ + } + } + +else if (ctptr->arg == power_returning) /* otherwise if a power-restoration option was given */ + if (cpu_power_state != power_off) /* but the CPU power is not off */ + status = SCPE_NOFNC; /* then the command is not allowed */ + + else { /* otherwise the restoration is valid */ + reset_all_p (0); /* so reset all devices to their power on states */ + + cpu_read_memory (absolute, ICS_Z, &zi); /* get the ICS stack limit */ + cpu_read_memory (absolute, zi + 1, &failure_cpx2); /* and get the value of CPX2 at the time of failure */ + cpu_write_memory (absolute, zi + 1, CPX2); /* and replace it with the current CPX2 value */ + + if (failure_cpx2 & cpx2_RUN) { /* if the CPU was running at the time of power failure */ + cpu_power_state = power_returning; /* then move to the "power is returning" state */ + CPX2 |= cpx2_RUNSWCH; /* and assume a RUN command */ + status = run_cmd (RU_CONT, cptr); /* and continue execution */ + } + + else { /* otherwise the CPU was halted when power failed */ + cpu_power_state = power_on; /* so remain halted in the "power is on" state */ + status = SCPE_OK; /* and return command success */ + } + } + +else /* otherwise a valid option has no handler */ + status = SCPE_IERR; /* so report an internal error */ + +return status; /* return the operation status */ +} + + /* CPU global utility routines */ @@ -1675,7 +1953,7 @@ else { /* otherwise the access case program: case data: case stack: - *value = (HP_WORD) M [address]; /* unchecked access values comes from memory */ + *value = (HP_WORD) M [address]; /* unchecked access values come from memory */ break; @@ -1895,7 +2173,7 @@ else /* otherwise the microma request_set = CPX1 & CPX1_IRQ_SET; /* get the set of active interrupt requests */ -if (request_set == cpx1_EXTINTR) { /* if only an external request present */ +if (request_set == cpx1_EXTINTR) { /* if only an external request is present */ class = irq_External; /* (the most common case) then set the class */ parameter = device_number; /* and set the parameter to the device number */ } @@ -1919,8 +2197,15 @@ else { /* otherwise scan for th else if (class == irq_External) /* otherwise if an external interrupt occurred */ parameter = device_number; /* then set the parameter to the device number */ - else if (class == irq_Module) /* otherwise if the class is a module interrupt */ - parameter = 0; /* then the parameter is the module number */ + else if (class == irq_Module) { /* otherwise if the class is a module interrupt */ + parameter = UPPER_BYTE (MOD); /* then the parameter is the module number */ + MOD = 0; /* clear the register to prevent a second interrupt */ + } + + else if (class == irq_Power_Fail) { /* otherwise if a power fail interrupt occurred */ + parameter = TO_LABEL (LABEL_IRQ, class); /* then the parameter is the label */ + cpu_power_state = power_failing; /* and system power is now failing */ + } else /* otherwise the parameter */ parameter = TO_LABEL (LABEL_IRQ, class); /* is the label */ @@ -1991,12 +2276,12 @@ void cpu_update_pclk (void) int32 elapsed, ticks; if (cpu_is_calibrated) { /* if the process clock is calibrated */ - elapsed = /* then the elapsed time is the original wait time */ - cpu_unit.wait - sim_activate_time (&cpu_unit); /* less the time remaining before the next service */ + elapsed = cpu_unit [0].wait /* then the elapsed time is the original wait time */ + - sim_activate_time (&cpu_unit [0]); /* less the time remaining before the next service */ ticks = /* the adjustment is */ - (elapsed * PCLK_MULTIPLIER) / cpu_unit.wait /* the elapsed fraction of the multiplier */ - - (PCLK_MULTIPLIER - pclk_increment); /* less the amount of any adjustment already made */ + (elapsed * PCLK_MULTIPLIER) / cpu_unit [0].wait /* the elapsed fraction of the multiplier */ + - (PCLK_MULTIPLIER - pclk_increment); /* less the amount of any adjustment already made */ PCLK = PCLK + ticks & R_MASK; /* update the process clock counter with rollover */ pclk_increment = pclk_increment - ticks; /* and reduce the amount remaining to add at service */ @@ -2528,9 +2813,6 @@ return; an external interrupt is pending. This is handled as an external interrupt but is classified differently so that the teardown and rebuild of the stack may be avoided to improve performance. - - 3. ICS interrupts other than external interrupts (e.g., parity errors) are - not currently generated or handled by the simulation. */ void cpu_setup_irq_handler (IRQ_CLASS class, HP_WORD parameter) @@ -2552,7 +2834,11 @@ if (class == irq_External || class == irq_IXIT) { /* if entry is for an ex else if (class >= irq_System_Parity /* otherwise if entry is for */ && class <= irq_Power_Fail) { /* another ICS interrupt */ - return; /* then.... [not handled yet] */ + cpu_setup_ics_irq (class, 0); /* then set it up on the ICS */ + + label = TO_LABEL (LABEL_IRQ, class); /* form the label for the specified classification */ + + STA = STATUS_M; /* clear status and enter privileged mode */ } else { /* otherwise entry is for a non-ICS interrupt */ @@ -2642,9 +2928,10 @@ void cpu_setup_ics_irq (IRQ_CLASS class, TRAP_CLASS trap) { HP_WORD delta_q, stack_db; -if (class != irq_Trap || trap != trap_Cold_Load) { /* if this is not a cold load trap entry */ - cpu_flush (); /* then flush the TOS registers to memory */ - cpu_mark_stack (); /* and write a four-word stack marker */ +if (class != irq_Trap /* if this is not */ + || trap != trap_Cold_Load && trap != trap_Power_On) { /* a cold load or power on trap entry */ + cpu_flush (); /* then flush the TOS registers to memory */ + cpu_mark_stack (); /* and write a four-word stack marker */ cpu_write_memory (stack, SM + 1 & LA_MASK, DBANK); /* add DBANK and DB to the stack */ cpu_write_memory (stack, SM + 2 & LA_MASK, DB); /* to form a six-word ICS marker */ @@ -2671,7 +2958,7 @@ else { /* otherwise execution i &stack_db); cpu_write_memory (stack, Q - 6 & LA_MASK, /* write the stack-DB-relative S value */ - SM + 2 - stack_db & DV_MASK); /* which is meaningless if this is a cold load */ + SM + 2 - stack_db & DV_MASK); /* which is meaningless for a cold load or power on */ SR = 0; /* invalidate the stack registers for a cold load */ DL = D16_UMAX; /* and set the data limit */ @@ -3156,9 +3443,8 @@ return SCPE_OK; /* return the success of If this is a power-on reset ("RESET -P"), the process clock calibrated timer is initialized, and any LOAD or DUMP request in progress is cleared. - The micromachine is halted, the process clock is scheduled, and, if a DUMP is - not in progress, several registers are cleared. The register values are - preserved for a DUMP to record the state of the machine accurately. + The micromachine is halted, the process clock is scheduled, and several + registers are cleared. Implementation notes: @@ -3178,7 +3464,7 @@ if (M == NULL) { /* if this is the first return SCPE_MEM; /* then report the error and abort the simulation */ else /* otherwise the memory was allocated */ - set_model (&cpu_unit, UNIT_SERIES_III, /* so establish the initial CPU model */ + set_model (&cpu_unit [0], UNIT_SERIES_III, /* so establish the initial CPU model */ NULL, NULL); for (sim_PC = dptr->registers; /* find the P register entry */ @@ -3190,21 +3476,24 @@ if (M == NULL) { /* if this is the first } if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */ - sim_rtcn_init (cpu_unit.wait, TMR_PCLK); /* then initialize the process clock timer */ + sim_rtcn_init (cpu_unit [0].wait, TMR_PCLK); /* then initialize the process clock timer */ CPX2 &= ~(cpx2_LOADSWCH | cpx2_DUMPSWCH); /* and clear any cold load or dump request */ } cpu_micro_state = halted; /* halt the micromachine */ -sim_activate_abs (&cpu_unit, cpu_unit.wait); /* and schedule the process clock */ +sim_activate_abs (&cpu_unit [0], cpu_unit [0].wait); /* and schedule the process clock */ -if (!(CPX2 & cpx2_DUMPSWCH)) { /* if the DUMP switch is inactive */ - PCLK = 0; /* then clear the process clock counter */ - CPX1 = 0; /* and all run-mode signals */ - CPX2 &= ~(cpx2_RUN | cpx2_SYSHALT); /* and the run and system halt flip-flops */ +PCLK = 0; /* clear the process clock counter */ +CPX1 = 0; /* and all run-mode signals */ +CPX2 &= ~(cpx2_RUN | cpx2_SYSHALT); /* and the run and system halt flip-flops */ - CNTR = SR; /* copy the stack register to the counter */ - cpu_flush (); /* and flush the TOS registers to memory */ - } +if (cpu_unit [0].flags & UNIT_PFARS) /* if the PF/ARS switch position is ENBL */ + CPX2 &= ~cpx2_INHPFARS; /* then clear the auto-restart inhibit flag */ +else /* otherwise the position is DSBL */ + CPX2 |= cpx2_INHPFARS; /* so set the auto-restart inhibit flag */ + +CNTR = SR; /* copy the stack register to the counter */ +cpu_flush (); /* and flush the TOS registers to memory */ return SCPE_OK; /* indicate that the reset succeeded */ } @@ -3330,6 +3619,58 @@ return SCPE_OK; /* the stops were succes } +/* Set the CPU cold dump configuration jumpers. + + This validation routine is called to configure the set of jumpers on the + system control panel that preset the device number and control value for the + cold dump process. The "option" parameter is 0 to set the device number + and 1 to set the control value. The "cptr" parameter points to the first + character of the value to be set. The unit and description pointers are not + used. + + The routine processes commands of the form: + + SET CPU DUMPDEV= + SET CPU DUMPCTL= + + The device number is a decimal value between 3 and 127, and the control value + is an octal number between 0 and 377. Values outside of these ranges are + rejected. +*/ + +static t_stat set_dump (UNIT *uptr, int32 option, CONST char *cptr, void *desc) +{ +t_value value; +t_stat status = SCPE_OK; + +if (cptr == NULL || *cptr == '\0') /* if the expected value is missing */ + status = SCPE_MISVAL; /* then report the error */ + +else if (option == 0) { /* otherwise if a device number is present */ + value = get_uint (cptr, DEVNO_BASE, /* then parse the supplied value */ + DEVNO_MAX, &status); + + if (status == SCPE_OK) /* if it is valid */ + if (value >= 3) /* and in the proper range */ + dump_control = REPLACE_LOWER (dump_control, /* then set the new device number */ + (uint32) value); /* into the dump control word */ + else /* otherwise the device number */ + status = SCPE_ARG; /* is invalid */ + } + +else { /* otherwise a control byte is present */ + value = get_uint (cptr, CNTL_BASE, /* so parse the supplied value */ + CNTL_MAX, &status); + + if (status == SCPE_OK) /* if it is valid */ + dump_control = REPLACE_UPPER (dump_control, /* then set the new control value */ + (uint32) value); /* into the dump control word */ + } + +return status; /* return the operation status */ +} + + /* Change the CPU memory size. This validation routine is called to configure the CPU memory size. The @@ -3453,6 +3794,32 @@ else /* otherwise */ } +/* Change the power-fail auto-restart switch setting. + + This validation routine is called to configure the PF/ARS switch that is + located behind the system control panel. If set to the ENBL (enable) + position, the CPU will perform an auto-restart when power is restored after a + failure. In the DSBL (disable) position, the CPU will remain halted after + power restoration; execution may be continued by pressing the RUN button. + + In simulation, a SET CPU ARS command enables auto-restart, and SET CPU NOARS + disables auto-restart. The "setting" parameter is set to the UNIT_ARS flag + in the former cast and to zero in the latter case. The other parameters are + not used. The routine reflects the ARS setting in "inhibit auto-restart" bit + of the CPX2 register. +*/ + +static t_stat set_pfars (UNIT *uptr, int32 setting, CONST char *cptr, void *desc) +{ +if (setting == UNIT_PFARS) /* if the option is ARS */ + CPX2 &= ~cpx2_INHPFARS; /* then clear the auto-restart inhibit flag */ +else /* otherwise the option is NOARS */ + CPX2 |= cpx2_INHPFARS; /* so set the auto-restart inhibit flag */ + +return SCPE_OK; /* confirm the change */ +} + + /* Show the CPU simulation stop conditions. This display routine is called to show the set of CPU stop conditions. The @@ -3494,6 +3861,23 @@ return SCPE_OK; /* report the success of } +/* Show the CPU cold dump configuration jumpers. + + This display routine is called to show the device number and control byte + that are preset on the rear of the system control panel for the cold dump + process. The "st" parameter is the open output stream. The other parameters + are not used. +*/ + +static t_stat show_dump (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +fprintf (st, "Dump device = %u, dump control = %03o\n", + LOWER_BYTE (dump_control), UPPER_BYTE (dump_control)); + +return SCPE_OK; +} + + /* Show the current CPU simulation speed. This display routine is called to show the current simulation speed. The @@ -3536,6 +3920,41 @@ return SCPE_OK; /* and report success to the accumulator to ensure that a single step won't complete without executing an instruction. + If the DUMP switch is pressed, an I/O Reset is performed on all devices + except the CPU, which is skipped to preserve the register state. The dump + device number is obtained from the SWCH register and tested to ensure that a + tape is mounted with a write ring and the unit is online. Then the contents + of the CPU registers are written to the reserved memory area starting at + address 1400 octal in bank 0. This is followed by two SIO programs. The + main program at addresses 1430-1437 write 4K-word blocks of memory to the + dump device. The error recovery program at addresses 1422-1427 is invoked + when a write fails. It does a Backspace Record followed by a Write Gap to + skip the bad spot on the tape, and then the write is retried. + + The DUMP switch remains set, so after each pass through the main execution + loop to run a channel cycle, this routine is reentered. The "waiting" state + causes the second part of the process to check for device completion. When + it occurs, the expected external interrupt is cleared, and if the dump is + complete, the DUMP switch is reset, the original SIO pointer in the DRT is + restored, and the micromachine is halted. Otherwise, the SIO pointer is + read. If it points at the end of the program, then the operation completed + normally. In this case, the dump address is advanced, and, if all of memory + has been dumped, the SIO pointer is reset to the recovery program, and that + program is changed to finish up with Write File Mark and Rewind/Offline + commands. Otherwise, the pointer is reset to the main program in preparation + for the next 4K write. The SIO program is then restarted. + + If SIO pointer failed to complete, the pointer is reset to point at the error + recovery program, and the memory address is unchanged; the same 4K write will + be performed once the recovery program runs. If the recovery program fails, + the dump is terminated at that point with a failure indication. + + During the dump operation, the CIR register is continually updated with the + current memory bank number. If the dump runs to completion, CIR will contain + the number of 64K memory banks installed in the machine. A value less than + the installed memory value indicates a dump failure. Except for the CIR, the + machine state is restored, so that another dump may be attempted. + If the LOAD switch is pressed, the cold load process begins by filling memory with HALT 10 instructions if SWCH register bit 8 is clear. The cold load device number is obtained from the lower byte of the SWCH register. @@ -3543,12 +3962,12 @@ return SCPE_OK; /* and report success The first part of the cold load process clears the TOS and STA registers, stores the initial channel program in memory, and executes an SIO instruction to start the channel. Once the device starts, interrupts are enabled, and - the micromachine state is set to "loading" in preparation for executing the + the micromachine state is set to "waiting" in preparation for executing the second part of the cold load process once the channel program ends. The routine then exits to begin channel execution. The LOAD switch remains set, so after each pass through the main execution - loop to run a channel cycle, this routine is reentered. The "loading" state + loop to run a channel cycle, this routine is reentered. The "waiting" state causes the second part of the process to check for device completion. The expected external interrupt is cleared, the LOAD switch is cleared, the micromachine state is set to "running", and the Cold Load trap is taken to @@ -3566,25 +3985,26 @@ return SCPE_OK; /* and report success instruction in the NIR will become the first instruction executed after the interrupt handler completes. - 2. The cold load microcode is shared with the cold dump process. The dump - process saves memory locations DRT + 0 through DRT + 3 in the TOS + 2. The cold load microcode is shared with the cold dump process. The Series + II dump process saves memory locations DRT + 0 through DRT + 3 in the TOS registers. The load process uses the same microcode but does not perform the memory read, so the TOS registers are loaded with the previous contents of the OPND register, which is effectively a random value. In simulation, the TOS registers are cleared. - 3. The cold load microcode waits forever for an interrupt from the cold - load device. If it doesn't occur, the microcode hangs until a system - reset is performed (it tests CPX1 bit 8 and does a JMP *-1 if the bit is - not set). The simulation follows the microcode behavior. + 3. The cold load and dump microcode waits forever for an interrupt from the + cold load device. If it doesn't occur, the microcode hangs until a + system reset is performed (it tests CPX1 bit 8 and does a JMP *-1 if the + bit is not set). The simulation follows the microcode behavior. 4. Front panel diagnostics and direct I/O cold loading is not implemented. */ static t_stat halt_mode_interrupt (HP_WORD device_number) { -static HP_WORD cold_load_device; -uint32 address; +static HP_WORD cold_device, sio_pointer, status, offset, pointer; +static uint32 address; +static t_bool error_recovery; if (CPX2 & cpx2_RUNSWCH) { /* if the RUN switch is pressed */ if (CPX2 & cpx2_SYSHALT) { /* then if the System Halt flip-flop is set */ @@ -3608,18 +4028,181 @@ if (CPX2 & cpx2_RUNSWCH) { /* if the RUN switch is cpu_micro_state = running; /* start the micromachine */ sim_interval = sim_interval + 1; /* don't count this cycle against a STEP count */ + + if (cpu_power_state == power_returning) { /* if power is returning after a failure */ + if (CPX2 & cpx2_INHPFARS) /* then if auto-restart is inhibited */ + CPX2 &= ~cpx2_RUN; /* then clear the Run flip-flop */ + + MICRO_ABORT (trap_Power_On); /* set up the trap to the power-on routine */ + } } else if (CPX2 & cpx2_DUMPSWCH) { /* otherwise if the DUMP switch is pressed */ - CPX2 &= ~CPX2_IRQ_SET; /* then clear all switches */ - return SCPE_INCOMP; /* and report that DUMP is not implemented yet */ + if (cpu_micro_state != waiting) { /* then if the dump is not in progress */ + reset_all (IO_RESET); /* then reset all I/O devices */ + + cold_device = LOWER_BYTE (SWCH) & DEVNO_MASK; /* get the device number from the lower SWCH byte */ + + status = iop_direct_io (cold_device, ioTIO, 0); /* get the device status */ + + if ((status & MS_ST_MASK) != MS_ST_READY) { /* if the tape is not ready and unprotected */ + CPX2 &= ~cpx2_DUMPSWCH; /* then clear the dump switch */ + + CIR = 0; /* clear CIR to indicate a failure */ + return STOP_CDUMP; /* and terminate the dump */ + } + + cpu_read_memory (absolute, cold_device * 4, /* get the original DRT pointer */ + &sio_pointer); + + cpu_write_memory (absolute, 01400, 1); /* set the machine ID to 1 for the Series III */ + cpu_write_memory (absolute, 01401, sio_pointer); /* store the original DRT pointer */ + cpu_write_memory (absolute, 01402, SM); /* store the stack pointer */ + cpu_write_memory (absolute, 01403, 0); /* store zeros for the scratch pad 1 */ + cpu_write_memory (absolute, 01404, 0); /* and scratch pad 2 register values */ + cpu_write_memory (absolute, 01405, DB); /* store the data base */ + cpu_write_memory (absolute, 01406, DBANK << 12 /* store DBANK in 0:4 */ + | PBANK << 8 /* and PBANK in 4:4 */ + | SBANK); /* and SBANK in 12:4 */ + cpu_write_memory (absolute, 01407, Z); /* store the stack limit */ + cpu_write_memory (absolute, 01410, DL); /* and the data limit */ + cpu_write_memory (absolute, 01411, X); /* and the index register */ + cpu_write_memory (absolute, 01412, Q); /* and the frame pointer */ + cpu_write_memory (absolute, 01413, CIR); /* and the current instruction */ + cpu_write_memory (absolute, 01414, PB); /* and the program base */ + cpu_write_memory (absolute, 01415, PL); /* and the program limit */ + cpu_write_memory (absolute, 01416, P); /* and the program counter */ + cpu_write_memory (absolute, 01417, CPX1); /* store the CPX1 register */ + cpu_write_memory (absolute, 01420, STA); /* and the status register */ + cpu_write_memory (absolute, 01421, /* store the lower byte of the CPX2 register */ + LOWER_WORD (CPX2 << 8 /* in the upper byte of memory */ + | MEMSIZE / 65536)); /* and the memory bank count in the lower byte */ + + cpu_write_memory (absolute, 01422, SIO_CNTL); /* CONTRL 0,BSR */ + cpu_write_memory (absolute, 01423, MS_CN_BSR); + cpu_write_memory (absolute, 01424, SIO_CNTL); /* CONTRL 0,GAP */ + cpu_write_memory (absolute, 01425, MS_CN_GAP); + cpu_write_memory (absolute, 01426, SIO_JUMP); /* JUMP 001436 */ + cpu_write_memory (absolute, 01427, 001436); + + cpu_write_memory (absolute, 01430, SIO_SBANK); /* SETBNK 0 */ + cpu_write_memory (absolute, 01431, 000000); + cpu_write_memory (absolute, 01432, SIO_CNTL); /* CONTRL 0, */ + cpu_write_memory (absolute, 01433, UPPER_BYTE (SWCH)); + cpu_write_memory (absolute, 01434, SIO_WRITE); /* WRITE #4096,000000 */ + cpu_write_memory (absolute, 01435, 000000); + cpu_write_memory (absolute, 01436, SIO_ENDIN); /* ENDINT */ + cpu_write_memory (absolute, 01437, 000000); + + address = 0; /* clear the address */ + offset = 0; /* and memory offset counters */ + + CIR = 0; /* clear the memory bank counter */ + + cpu_write_memory (absolute, cold_device * 4, 01430); /* point the DRT at the cold dump program */ + error_recovery = FALSE; + + iop_direct_io (cold_device, ioSIO, 0); /* start the device */ + + if (CPX1 & cpx1_IOTIMER) /* if the device did not respond */ + MICRO_ABORT (trap_SysHalt_IO_Timeout); /* then a System Halt occurs */ + + else { /* otherwise the device has started */ + status = STA; /* so save the original status register value */ + + STA = STATUS_I | STATUS_O; /* enable interrupts and set overflow */ + cpu_micro_state = waiting; /* and set the load-in-progress state */ + } + } + + else if (CPX1 & cpx1_EXTINTR) { /* otherwise if an external interrupt is pending */ + CPX1 &= ~cpx1_EXTINTR; /* then clear it */ + + iop_direct_io (device_number, ioRIN, 0); /* reset the device interrupt */ + + if (device_number == cold_device) /* if the expected device interrupted */ + if (address >= MEMSIZE) { /* then if all of memory has been dumped */ + CPX2 &= ~cpx2_DUMPSWCH; /* then reset the DUMP switch */ + + STA = status; /* restore the original status register value */ + + cpu_write_memory (absolute, /* restore the */ + cold_device * 4, /* original SIO pointer */ + sio_pointer); /* to the DRT */ + + cpu_micro_state = halted; /* clear the dump-in-progress state */ + return STOP_CDUMP; /* and report dump completion */ + } + + else { /* otherwise the dump continues */ + cpu_read_memory (absolute, /* read the */ + cold_device * 4, /* current SIO pointer address */ + &pointer); /* from the DRT */ + + if (pointer == 01440) { /* if the SIO program completed normally */ + cpu_write_memory (absolute, /* then reset the pointer */ + cold_device * 4, /* to the start */ + 001430); /* of the program */ + + if (error_recovery) /* if this was a successful error recovery */ + error_recovery = FALSE; /* then clear the flag and keep the current address */ + + else { /* otherwise this was a successful write */ + address = address + 4096; /* so bump the memory address */ + offset = offset + 4096 & LA_MASK; /* and offset to the next 4K block */ + + cpu_write_memory (absolute, /* store the new write buffer address */ + 001435, offset); + + if (offset == 0) { /* if the offset wrapped around */ + CIR = CIR + 1; /* then increment the bank number */ + cpu_write_memory (absolute, /* and store it as the SET BANK target */ + 001431, CIR); + + if (address >= MEMSIZE) { /* if all of memory has been dumped */ + cpu_write_memory (absolute, 001423, /* then change the error recovery program */ + MS_CN_WFM); /* to write a file mark */ + cpu_write_memory (absolute, 001425, /* followed by */ + MS_CN_RST); /* a rewind/offline request */ + + cpu_write_memory (absolute, /* point at the recovery program */ + cold_device * 4, + 001422); + } + } + } + } + + else if (error_recovery) { /* otherwise if the recover program failed */ + CPX2 &= ~cpx2_DUMPSWCH; /* then reset the DUMP switch */ + + STA = status; /* restore the original status register value */ + + cpu_write_memory (absolute, /* restore the */ + cold_device * 4, /* original SIO pointer */ + sio_pointer); /* to the DRT */ + + cpu_micro_state = halted; /* clear the dump-in-progress state */ + return STOP_CDUMP; /* and report dump failure */ + } + + else { /* otherwise attempt error recovery */ + cpu_write_memory (absolute, /* by setting the SIO pointer */ + cold_device * 4, /* to the backspace/write gap */ + 001422); /* program */ + + error_recovery = TRUE; /* indicate that recovery is in progress */ + } + + iop_direct_io (cold_device, ioSIO, 0); /* start the device */ + } + } /* otherwise wait for the cold dump device to interrupt */ } - else if (CPX2 & cpx2_LOADSWCH) /* otherwise if the LOAD switch is pressed */ - if (cpu_micro_state != loading) { /* then if the load is not in progress */ - reset_all (0); /* then reset the CPU and all I/O devices */ + if (cpu_micro_state != waiting) { /* then if the load is not in progress */ + reset_all (CPU_IO_RESET); /* then reset the CPU and all I/O devices */ if ((SWCH & 000200) == 0) /* if switch register bit 8 is clear */ for (address = 0; address < MEMSIZE; address++) /* then fill memory */ @@ -3627,14 +4210,14 @@ else if (CPX2 & cpx2_LOADSWCH) /* otherwise if the LOAD SBANK = 0; /* set the stack bank to bank 0 */ - cold_load_device = LOWER_BYTE (SWCH) & DEVNO_MASK; /* get the device number from the lower SWCH byte */ + cold_device = LOWER_BYTE (SWCH) & DEVNO_MASK; /* get the device number from the lower SWCH byte */ - if (cold_load_device < 3) { /* if the device number is between 0 and 2 */ + if (cold_device < 3) { /* if the device number is between 0 and 2 */ CPX2 &= ~cpx2_LOADSWCH; /* then reset the LOAD switch */ return SCPE_INCOMP; /* and execute a front panel diagnostic */ } - else if (cold_load_device > 63) { /* otherwise if the device number is > 63 */ + else if (cold_device > 63) { /* otherwise if the device number is > 63 */ CPX2 &= ~cpx2_LOADSWCH; /* then reset the LOAD switch */ return SCPE_INCOMP; /* and execute a direct I/O cold load */ } @@ -3648,25 +4231,25 @@ else if (CPX2 & cpx2_LOADSWCH) /* otherwise if the LOAD SR = 4; /* mark the TOS registers as valid */ STA = 0; /* and clear the status register */ - cpu_write_memory (absolute, 01430, 014000); /* SETBNK 0 */ + cpu_write_memory (absolute, 01430, SIO_SBANK); /* SETBNK 0 */ cpu_write_memory (absolute, 01431, 000000); - cpu_write_memory (absolute, 01432, 040000); /* CONTRL 0, */ + cpu_write_memory (absolute, 01432, SIO_CNTL); /* CONTRL 0, */ cpu_write_memory (absolute, 01433, UPPER_BYTE (SWCH)); - cpu_write_memory (absolute, 01434, 077760); /* READ #16,001400 */ + cpu_write_memory (absolute, 01434, SIO_READ); /* READ #16,001400 */ cpu_write_memory (absolute, 01435, 001400); - cpu_write_memory (absolute, 01436, 000000); /* JUMP 001400 */ + cpu_write_memory (absolute, 01436, SIO_JUMP); /* JUMP 001400 */ cpu_write_memory (absolute, 01437, 001400); - cpu_write_memory (absolute, cold_load_device * 4, 01430); /* point the DRT to the cold load program */ + cpu_write_memory (absolute, cold_device * 4, 01430); /* point the DRT to the cold load program */ - iop_direct_io (cold_load_device, ioSIO, 0); /* start the device */ + iop_direct_io (cold_device, ioSIO, 0); /* start the device */ if (CPX1 & cpx1_IOTIMER) /* if the device did not respond */ MICRO_ABORT (trap_SysHalt_IO_Timeout); /* then a System Halt occurs */ else { /* otherwise the device has started */ STA = STATUS_I | STATUS_O; /* so enable interrupts and set overflow */ - cpu_micro_state = loading; /* and set the load-in-progress state */ + cpu_micro_state = waiting; /* and set the load-in-progress state */ } } } @@ -3677,7 +4260,7 @@ else if (CPX2 & cpx2_LOADSWCH) /* otherwise if the LOAD iop_direct_io (device_number, ioRIN, 0); /* reset the device interrupt */ - if (device_number == cold_load_device) { /* if the expected device interrupted */ + if (device_number == cold_device) { /* if the expected device interrupted */ CPX2 &= ~cpx2_LOADSWCH; /* then reset the LOAD switch */ cpu_micro_state = running; /* clear the load-in-progress state */ diff --git a/HP3000/hp3000_cpu.h b/HP3000/hp3000_cpu.h index 6a8e3729..9fb4e547 100644 --- a/HP3000/hp3000_cpu.h +++ b/HP3000/hp3000_cpu.h @@ -23,6 +23,12 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 12-Sep-16 JDB Added the PCN_SERIES_II and PCN_SERIES_III constants + 02-Sep-16 JDB Added the POWER_STATE enumeration type, the UNIT_PFARS + flag, and the "cpu_power_state" external declaration + 24-Aug-16 JDB Fixed the UNIT_CPU_MODEL test macro + 23-Aug-16 JDB Added the MOD (module control) register + 12-Jul-16 JDB Renamed "loading" EXEC_STATE to "waiting" 21-Mar-16 JDB Changed cpu_ccb_table type from uint16 to HP_WORD 14-Feb-16 JDB First release version 11-Dec-12 JDB Created @@ -73,6 +79,7 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe #define UNIT_MODEL_SHIFT (UNIT_V_UF + 0) /* the CPU model (1 bit) */ #define UNIT_EIS_SHIFT (UNIT_V_UF + 1) /* the Extended Instruction Set firmware option */ #define UNIT_CALTIME_SHIFT (UNIT_V_UF + 2) /* the process clock timing mode */ +#define UNIT_PFARS_SHIFT (UNIT_V_UF + 3) /* the power-fail auto-restart mode */ #define UNIT_MODEL_MASK 0000001u /* model ID mask */ @@ -82,11 +89,14 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe #define UNIT_SERIES_II (1u << UNIT_MODEL_SHIFT) /* the CPU is a Series II */ #define UNIT_EIS (1u << UNIT_EIS_SHIFT) /* the Extended Instruction Set is installed */ #define UNIT_CALTIME (1u << UNIT_CALTIME_SHIFT) /* the process clock is calibrated to wall time */ +#define UNIT_PFARS (1u << UNIT_PFARS_SHIFT) /* the system will auto-restart after a power failure */ -#define UNIT_CPU_MODEL (cpu_unit.flags & UNIT_MODEL_MASK) +#define UNIT_CPU_MODEL (cpu_unit [0].flags & UNIT_MODEL) #define CPU_MODEL(f) ((f) >> UNIT_MODEL_SHIFT & UNIT_MODEL_MASK) +#define MEMSIZE (cpu_unit [0].capac) /* the current memory size in 16-bit words */ + /* CPU debug flags */ @@ -108,12 +118,41 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe #define SS_BYPASSED (1u << 31) /* stops are bypassed for this instruction */ +/* System power state. + + The HP 3000 power supply uses two signals to indicate its state: PON (power + on) and PFW (power-fail warning). PON is asserted when the DC power levels + are within their operating ranges. PFW is asserted when AC power is lost. + When a power failure occurs, PFW will be asserted at least three milliseconds + before PON is denied. When power is restored, PFW denies immediately, but + PON does not assert until the DC output voltages have stabilized, and the + machine is ready to resume execution. + + In simulation, the four states of these two signals are modeled with + enumeration constants, as follows: + + PON PFW State Simulator Action + --- --- --------------- ---------------------------- + 1 0 power on executing normally + 1 1 power failing executing with cpx1_PFINTR + 0 1 power off will not execute + 0 0 power returning executing with trap_Power_On +*/ + +typedef enum { + power_on, + power_failing, + power_off, + power_returning + } POWER_STATE; + + /* Micromachine execution state */ typedef enum { running, /* the micromachine is running */ paused, /* a PAUS instruction has been executed */ - loading, /* a COLD LOAD is in progress */ + waiting, /* a cold load or dump is in progress */ halted /* a programmed or front panel HALT has been executed */ } EXEC_STATE; @@ -201,7 +240,7 @@ typedef enum { cpx2_DECRADDR = 0000040u, /* decrement address */ /* cpx2_UNUSED = 0000020u, unused, always 0 */ /* cpx2_UNUSED = 0000010u, unused, always 0 */ - cpx2_INHPFARS = 0000004u, /* inhibit power fail autorestart */ + cpx2_INHPFARS = 0000004u, /* inhibit power-fail auto-restart */ cpx2_SYSHALT = 0000002u, /* system halt */ cpx2_RUN = 0000001u /* run flip-flop */ } CPX2FLAG; @@ -297,7 +336,7 @@ typedef enum { trap_DS_Absent = 042, /* ucode Absent Data Segment */ trap_Power_On = 043, /* hdwe Power On */ trap_Cold_Load = 044, /* ucode Cold Load */ - trap_System_Halt = 045, /* ucode System Halt */ + trap_System_Halt = 045 /* ucode System Halt */ } TRAP_CLASS; #define trap_Integer_Overflow TO_DWORD (001, trap_User) @@ -332,6 +371,50 @@ typedef enum { #define MICRO_ABORTP(t,p) longjmp (cpu_save_env, TO_DWORD ((p),(t))) +/* Central Data Bus module definitions */ + +#define MODULE_MEMORY_LOWER 0 /* lower memory MCL address */ +#define MODULE_MEMORY_UPPER 2 /* upper memory MCL address */ +#define MODULE_MEMORY 3 /* upper bound of MCL addresses */ +#define MODULE_PORT_CNTLR 4 /* selector channel port controller address */ +#define MODULE_CPU 5 /* CPU MCU address */ +#define MODULE_UNDEFINED 6 /* addresses 6-7 are unused */ + +#define MOP_NOP 0 /* module operation 00 = no operation */ +#define MOP_WRITE 1 /* module operation 01 = write */ +#define MOP_READ 2 /* module operation 10 = read */ +#define MOP_READ_WRITE_ONES 3 /* module operation 11 = read/write ones */ + + +/* Module control register accessors. + + The module control register, MOD, has this format: + + 0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 | MOP | 0 | FROM | 0 0 0 0 | B | A | 0 0 | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + MOP = module operation + FROM = source module address + B = this CPU is CPU #2 + A = this CPU is CPU #1 +*/ + +#define MOD_MOP_MASK 0030000u /* MOD register MOP field mask */ +#define MOD_FROM_MASK 0003400u /* MOD register FROM field mask */ +#define MOD_CPU_2 0000010u /* CPU number 2 MCU */ +#define MOD_CPU_1 0000004u /* CPU number 1 MCU */ + +#define MOD_MOP_SHIFT 12 /* MOD register MOP field alignment shift */ +#define MOD_FROM_SHIFT 8 /* MOD register FROM field alignment shift */ + +#define TO_MOD_MOP(v) ((v) << MOD_MOP_SHIFT & MOD_MOP_MASK) +#define TO_MOD_FROM(v) ((v) << MOD_FROM_SHIFT & MOD_FROM_MASK) + + /* Status register accessors. The CPU status register, STA, has this format: @@ -840,6 +923,15 @@ typedef enum { #define TBX 0054000u /* test and branch, limit in X */ #define MTBX 0056000u /* modify, test and branch, limit in X */ +#define CMD_TO_MASK 0000007u /* CMD command word TO field mask */ +#define CMD_MOP_MASK 0000060u /* CMD command word MOP field mask */ + +#define CMD_TO_SHIFT 0 /* CMD command word TO field alignment shift */ +#define CMD_MOP_SHIFT 4 /* CMD command word MOP field alignment shift */ + +#define CMD_TO(v) (((v) & CMD_TO_MASK) >> CMD_TO_SHIFT) +#define CMD_MOP(v) (((v) & CMD_MOP_MASK) >> CMD_MOP_SHIFT) + /* PSHR/SETR instruction accessors */ @@ -858,6 +950,12 @@ typedef enum { #define PSR_PRIV (PSR_SBANK | PSR_DB_DBANK | PSR_DL | PSR_Z) +/* PCN instruction result values */ + +#define PCN_SERIES_II 1 /* CPU number for the Series II */ +#define PCN_SERIES_III 2 /* CPU number for the Series III */ + + /* Reserved memory addresses */ #define CSTB_POINTER 0000000u /* code segment table base pointer */ @@ -952,6 +1050,7 @@ extern HP_WORD STA; /* Status Register */ extern HP_WORD SWCH; /* Switch Register */ extern HP_WORD CPX1; /* Run-Mode Interrupt Flags Register */ extern HP_WORD CPX2; /* Halt-Mode Interrupt Flags Register */ +extern HP_WORD MOD; /* Module Control Register */ extern HP_WORD PCLK; /* Process Clock Register */ extern HP_WORD CNTR; /* Microcode Counter */ @@ -966,11 +1065,12 @@ extern HP_WORD CNTR; /* Microcode Counter */ /* CPU state */ -extern jmp_buf cpu_save_env; /* saved environment for microcode aborts */ -extern EXEC_STATE cpu_micro_state; /* micromachine execution state */ -extern uint32 cpu_stop_flags; /* set of simulation stop flags */ -extern t_bool cpu_base_changed; /* TRUE if any base register has been changed */ -extern UNIT cpu_unit; /* CPU unit structure (needed for memory size) */ +extern jmp_buf cpu_save_env; /* saved environment for microcode aborts */ +extern POWER_STATE cpu_power_state; /* power supply state */ +extern EXEC_STATE cpu_micro_state; /* micromachine execution state */ +extern uint32 cpu_stop_flags; /* set of simulation stop flags */ +extern t_bool cpu_base_changed; /* TRUE if any base register has been changed */ +extern UNIT cpu_unit []; /* CPU unit array (needed for memory size) */ /* Condition Code B mapping table */ diff --git a/HP3000/hp3000_cpu_base.c b/HP3000/hp3000_cpu_base.c index e81c2e1a..f2b71d19 100644 --- a/HP3000/hp3000_cpu_base.c +++ b/HP3000/hp3000_cpu_base.c @@ -23,6 +23,8 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 12-Sep-16 JDB Use the PCN_SERIES_II and PCN_SERIES_III constants + 23-Aug-16 JDB Implement the CMD instruction and module interrupts 11-Jun-16 JDB Bit mask constants are now unsigned 13-Jan-16 JDB First release version 11-Dec-12 JDB Created @@ -2991,7 +2993,7 @@ switch (operation) { /* dispatch the move or break; - case 020: /* MVBW (CCB; STUN, STOV, BNDV() */ + case 020: /* MVBW (CCB; STUN, STOV, BNDV) */ case 021: case 022: case 023: @@ -3417,10 +3419,10 @@ switch (operation) { /* dispatch the move or cpu_push (); /* push the stack down */ if (UNIT_CPU_MODEL == UNIT_SERIES_II) /* if the CPU is a Series II */ - RA = 1; /* then the CPU number is 1 */ + RA = PCN_SERIES_II; /* then the CPU number is 1 */ else if (UNIT_CPU_MODEL == UNIT_SERIES_III) /* if the CPU is a Series III */ - RA = 2; /* then the CPU number is 2 */ + RA = PCN_SERIES_III; /* then the CPU number is 2 */ else /* if it's anything else */ status = SCPE_IERR; /* then there's a problem! */ @@ -3631,6 +3633,21 @@ return status; /* return the execution simulation, and the UNDEF stop is active, a simulation stop will occur. If the stop is bypassed or not set, then the instruction will execute as though the reserved bits were zero. + + 3. The CMD instruction is simulated by assuming that the addressed module + will send a return message to the CPU, causing a module interrupt. If + the module is the CPU, then the "return message" is the originating + message, including whatever MOP was specified. Memory modules return a + no-operation MOP in response to a read or read/write ones MOP. Sending a + read/write ones MOP to a Series II memory module sets the addressed + location to 177777 before the read value is returned. + + 4. The module interrupt signal is qualified by the I-bit of the status + register. This is simulated by setting the cpx1_MODINTR bit in the CMD + executor if the I-bit is set, by clearing the cpx1_MODINTR bit in the SED + 0 executor, and by setting the bit in the SED 1 executor if the MOD + register is non-zero (indicating a pending module interrupt that has not + been serviced). */ static t_stat io_control (void) @@ -3640,9 +3657,8 @@ static const uint8 preadjustment [16] = { /* stack preadjustment, indexed 0, 1, 0, 1, 1, 2, 0, 0 /* RIO WIO TIO CIO CMD SST SIN HALT */ }; -uint32 operation; -HP_WORD operand, address, offset; -HP_WORD ics_q, delta_qi, disp_counter; +uint32 operation, address, offset, module; +HP_WORD operand, command, ics_q, delta_qi, disp_counter; t_stat status = SCPE_OK; operation = IOCSUBOP (CIR); /* get the suboperation from the instruction */ @@ -3663,7 +3679,7 @@ switch (operation) { /* dispatch the I/O or c else /* otherwise */ cpu_read_memory (absolute, /* use the specified offset */ - offset + SGT_POINTER & LA_MASK, + offset + SGT_POINTER, /* which cannot overflow */ &operand); if (NPRV) /* if the mode is not privileged */ @@ -3701,11 +3717,17 @@ switch (operation) { /* dispatch the I/O or c && cpu_stop_flags & SS_UNDEF) /* and the undefined instruction stop is active */ status = STOP_UNIMPL; /* then stop the simulator */ - else if (CIR & 1) /* otherwise if bit 15 of the instruction is 1 */ + else if (CIR & 1) { /* otherwise if bit 15 of the instruction is 1 */ STA |= STATUS_I; /* then enable interrupts */ - else /* otherwise */ - STA &= ~STATUS_I; /* disable them */ + if (MOD != 0) /* if a module interrupt is pending */ + CPX1 |= cpx1_MODINTR; /* then request it now */ + } + + else { /* otherwise */ + STA &= ~STATUS_I; /* disable interrupts */ + CPX1 &= ~cpx1_MODINTR; /* and clear any indicated module interrupt */ + } break; @@ -3953,7 +3975,50 @@ switch (operation) { /* dispatch the I/O or c if (NPRV) /* if the mode is not privileged */ MICRO_ABORT (trap_Privilege_Violation); /* then abort with a privilege violation */ - status = STOP_UNIMPL; /* THIS INSTRUCTION IS NOT IMPLEMENTED YET! */ + address = SM + SR - IO_K (CIR) & LA_MASK; /* get the location of the command word */ + cpu_read_memory (stack_checked, address, &command); /* and read it from the stack */ + + module = CMD_TO (command); /* get the addressed (TO) module number */ + + if (module == MODULE_PORT_CNTLR /* if the selector channel port controller */ + || module >= MODULE_UNDEFINED) /* or an undefined module is addressed */ + CPX1 |= cpx1_CPUTIMER; /* then a module timeout occurs */ + + else if (module == MODULE_CPU) /* otherwise if the CPU is addressing itself */ + MOD = MOD_CPU_1 /* then set the MOD register */ + | TO_MOD_FROM (module) /* FROM field to the TO address */ + | TO_MOD_MOP (CMD_MOP (command)); /* and include the MOP field value */ + + else if (UNIT_CPU_MODEL == UNIT_SERIES_II) /* otherwise if a Series II memory module is addressed */ + if (module >= MODULE_MEMORY_UPPER /* then if the upper module is addressed */ + && MEMSIZE < 128 * 1024) /* but it's not present */ + CPX1 |= cpx1_CPUTIMER; /* then it will not respond */ + + else { /* otherwise the module address is valid */ + if (CMD_MOP (command) == MOP_READ_WRITE_ONES) { /* if the operation is read/write ones */ + address = TO_PA (module, RA); /* then get the bank and address */ + cpu_write_memory (absolute, address, D16_UMAX); /* and set the addressed word to all one bits */ + } + + MOD = MOD_CPU_1 /* set the MOD register */ + | TO_MOD_FROM (module) /* FROM field to the TO address */ + | TO_MOD_MOP (MOP_NOP); /* and the module operation to NOP */ + } + + else if (UNIT_CPU_MODEL == UNIT_SERIES_III) /* otherwise if a Series III memory module is addressed */ + if (module >= MODULE_MEMORY_UPPER /* then if the upper module is addressed */ + && MEMSIZE < 512 * 1024) /* but it's not present */ + CPX1 |= cpx1_CPUTIMER; /* then it will not respond */ + + else /* otherwise the module address is valid */ + MOD = MOD_CPU_1 /* so set the MOD register */ + | TO_MOD_FROM (module) /* FROM field to the TO address */ + | TO_MOD_MOP (MOP_NOP); /* and the module operation to NOP */ + + if (MOD != 0 && STA & STATUS_I) /* if a module interrupt is indicated and enabled */ + CPX1 |= cpx1_MODINTR; /* then request it */ + + cpu_pop (); /* delete the TOS */ break; @@ -3969,7 +4034,7 @@ switch (operation) { /* dispatch the I/O or c else /* otherwise */ cpu_read_memory (absolute, /* use the specified offset */ - offset + SGT_POINTER & LA_MASK, + offset + SGT_POINTER, /* which cannot overflow */ &operand); if (NPRV) /* if the mode is not privileged */ diff --git a/HP3000/hp3000_cpu_ims.h b/HP3000/hp3000_cpu_ims.h index c1b1ff46..f870217e 100644 --- a/HP3000/hp3000_cpu_ims.h +++ b/HP3000/hp3000_cpu_ims.h @@ -23,6 +23,9 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 01-Sep-16 JDB Added the cpu_cold_cmd and cpu_power_cmd routines + 15-Aug-16 JDB Removed obsolete comment mentioning iop_read/write_memory + 15-Jul-16 JDB Corrected the IOCW_COUNT macro to return the correct value 11-Jun-16 JDB Bit mask constants are now unsigned 05-Sep-15 JDB First release version 11-Dec-12 JDB Created @@ -92,7 +95,7 @@ typedef enum { Implementation notes: 1. The IOCW_COUNT(w) macro sign-extends the 12-bit two's-complement word - count in the IOCW for the Read and Write orders. + count to a 16-bit value for the Return Residue order. 2. The sioWRITE, sioWRITEC, sioREAD, and sioREADC enumeration constants must be contiguous and the final four values, so that a ">= sioWRITE" test @@ -117,7 +120,7 @@ typedef enum { #define IOCW_CNTL(w) (((w) & IOCW_CNTL_MASK) >> IOCW_CNTL_SHIFT) #define IOCW_WCNT(w) (((w) & IOCW_WCNT_MASK) >> IOCW_WCNT_SHIFT) -#define IOCW_COUNT(w) (- (int32) (~(w) + 1 & IOCW_WCNT_MASK)) +#define IOCW_COUNT(w) ((w) | ~IOCW_WCNT_MASK & D16_MASK) #define IOAW_BANK(w) (((w) & IOAW_BANK_MASK) >> IOAW_BANK_SHIFT) @@ -139,7 +142,16 @@ typedef enum { } SIO_ORDER; -/* Global CPU routine declarations */ +/* Global CPU routine declarations. + + cpu_cold_cmd : process the LOAD and DUMP commands + cpu_power_cmd : process the POWER commands + cpu_read_memory : read a word from main memory + cpu_write_memory : write a word to main memory +*/ + +extern t_stat cpu_cold_cmd (int32 arg, CONST char *buf); +extern t_stat cpu_power_cmd (int32 arg, CONST char *buf); extern t_bool cpu_read_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD *value); extern t_bool cpu_write_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD value); @@ -161,8 +173,6 @@ extern const char *const sio_order_name []; iop_initialize : initialize the I/O processor iop_poll : poll the interfaces for an active interrupt request - iop_read_memory : read memory via the module control unit - iop_write_memory : write memory via the module control unit iop_direct_io : dispatch an I/O command to an interface */ diff --git a/HP3000/hp3000_defs.h b/HP3000/hp3000_defs.h index c151a54c..3ed39d80 100644 --- a/HP3000/hp3000_defs.h +++ b/HP3000/hp3000_defs.h @@ -23,6 +23,7 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 03-Sep-16 JDB Added the STOP_POWER and STOP_ARSINH codes 13-May-16 JDB Modified for revised SCP API function parameter types 21-Mar-16 JDB Changed uint16 types to HP_WORD 19-Mar-16 JDB Added UNDEFs for the additional register macros @@ -306,6 +307,8 @@ #define STOP_INFLOOP 7 /* infinite loop stop */ #define STOP_CLOAD 8 /* cold load complete */ #define STOP_CDUMP 9 /* cold dump complete */ +#define STOP_ARSINH 10 /* auto-restart inhibited */ +#define STOP_POWER 11 /* power is off */ /* Modifier validation identifiers */ @@ -585,7 +588,7 @@ typedef enum { /* trailing separator */ append_bar /* append a trailing separator */ } BITSET_BAR; -typedef const char *const BITSET_NAME; /* a bit name string pointer */ +typedef const char *const BITSET_NAME; /* a bit name string pointer */ typedef struct { /* bit set format descriptor */ uint32 name_count; /* count of bit names */ diff --git a/HP3000/hp3000_diag.txt b/HP3000/hp3000_diag.txt index ebcf0c9f..02d30c87 100644 --- a/HP3000/hp3000_diag.txt +++ b/HP3000/hp3000_diag.txt @@ -1,13 +1,13 @@ SIMH/HP 3000 DIAGNOSTICS PERFORMANCE ==================================== - Last update: 2016-05-31 + Last update: 2016-09-07 -The HP 32230 offline diagnostic suite has been run against the SIMH HP 3000 -simulation. Diagnostic programs were obtained from two magnetic tapes: HP -30000-11016 Rev. 1244 (CPU) and 30000-11017 Rev. 2640 (non-CPU). For each -diagnostic, the recommended standard tests were selected, plus any available -optional tests that broadened the test coverage. +The HP 32230 diagnostic suite has been run against the SIMH HP 3000 simulation. +Diagnostic programs were obtained from two magnetic tapes: HP 30000-11016 Rev. +1244 (CPU) and 30000-11017 Rev. 2640 (non-CPU). For each diagnostic, the +recommended standard tests were selected, plus any available optional tests that +broadened the test coverage. Except where noted in the individual diagnostic reports, the test system configuration is the default SIMH configuration with these changes: @@ -27,7 +27,7 @@ The results of the diagnostic runs are summarized below: PD420A CPU Diagnostic Section 1 01.00 Passed PD420A1 CPU Diagnostic Section 2 01.00 Passed PD420A2 CPU Diagnostic Section 3 01.01 Passed - PD420A3 CPU Diagnostic Section 4 01.24 Partial + PD420A3 CPU Diagnostic Section 4 01.24 Passed PD420A4 CPU Diagnostic Section 5 01.00 Passed PD420A5 CPU Diagnostic Section 6 01.00 Passed PD420A6 CPU Diagnostic Section 7 01.00 Passed @@ -188,7 +188,6 @@ TEST REPORT: Programmed halt, CIR: 030375 (HALT 15), P: 010163 (ZERO,NOP) TEST RESULT: Passed. - TEST NOTES: The Internal Switch Register (2000) and Section Select Register (2001) settings are preconfigured to simplify execution. @@ -210,27 +209,29 @@ D420A3 - CPU Section 4 TESTED DEVICE: CPU (hp3000_cpu.c) CONFIGURATION: sim> deposit 2000 000001 - sim> deposit 2001 034160 - sim> deposit 010550 120005 + sim> deposit 2001 034170 sim> deposit 015761 051007 sim> set scmb enable sim> set scmb mx + sim> set cpu stop=PAUSE sim> go -TEST REPORT: Programmed halt, CIR: 030375 (HALT 15), P: 010630 (ZERO,NOP) +TEST REPORT: CPU paused, P: 021376 (PAUS 12) -TEST RESULT: Partially passed. + sim> set cpu nostop=PAUSE + sim> power fail + + Programmed halt, CIR: 030374 (HALT 14), P: 011130 (EXIT 0) + + sim> power restore + + Programmed halt, CIR: 030375 (HALT 15), P: 010630 (ZERO,NOP) + +TEST RESULT: Passed. TEST NOTES: The Internal Switch Register (2000) and Section Select Register (2001) settings are preconfigured to simplify execution. - Step 17 (module interrupt tests) fails, as this feature is not - currently simulated. The step is bypassed by changing the PCAL - instruction at location 010550 to an INCM DB+5 instruction. - - Step 20 (power fail tests) is optional and is not selected, as - this feature is not currently simulated. - The "Stand-Alone HP 30003A/B CPU Diagnostic" manual (30003-90001, April 1979) says that step 31 has been "eliminated from diagnostic" (the February 1976 manual lists this step as @@ -1901,3 +1902,6 @@ TEST REPORT: D1 ONLINE LINE PRINTER VERIFIER (HP D466A.01.06) END OF PROGRAM TEST RESULT: Passed. + +TEST NOTES: The test was run from the OPERATOR.SYS account on MPE-V/R + release E.01.00. diff --git a/HP3000/hp3000_ds.c b/HP3000/hp3000_ds.c index e7b0b405..be04f78c 100644 --- a/HP3000/hp3000_ds.c +++ b/HP3000/hp3000_ds.c @@ -25,6 +25,7 @@ DS HP 30229B Cartridge Disc Interface + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG 09-Jun-16 JDB Added casts for ptrdiff_t to int32 values 08-Jun-16 JDB Corrected %d format to %u for unsigned values 16-May-16 JDB Fixed interrupt mask setting @@ -439,9 +440,10 @@ static REG ds_reg [] = { { DRDATA (RETRY, retry_counter, 4), REG_FIT | PV_LEFT }, { SRDATA (DIAG, overrides, REG_HRO) }, - { SRDATA (DIB, ds_dib, REG_HRO) }, - DL_REGS (mac_cntlr, ds_unit, UNIT_COUNT, buffer, fast_times), + DIB_REGS (ds_dib), + + DL_REGS (mac_cntlr, ds_unit, UNIT_COUNT, buffer, fast_times), { NULL } }; diff --git a/HP3000/hp3000_io.h b/HP3000/hp3000_io.h index 015234da..35332635 100644 --- a/HP3000/hp3000_io.h +++ b/HP3000/hp3000_io.h @@ -23,6 +23,8 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 12-Sep-16 JDB Added the DIB_REGS macro + 02-Sep-16 JDB Added the iop_assert_PFWARN routine 11-Jun-16 JDB Bit mask constants are now unsigned 21-Mar-16 JDB Changed type of inbound_value of CNTLR_INTRF to HP_WORD 20-Jan-16 JDB First release version @@ -135,7 +137,7 @@ typedef enum { /* --- source of signal --- */ INTPOLLIN = 000040000000, /* IOP interrupt poll */ XFERERROR = 000100000000, /* Multiplexer channel abort */ CHANSO = 000200000000, /* Channel service call to interface */ - PFWARN = 000400000000 /* SET CPU POWERFAIL */ + PFWARN = 000400000000 /* POWER FAIL command */ /* = 001000000000 (available) */ /* = 002000000000 (available) */ /* = 004000000000 (available) */ @@ -269,6 +271,12 @@ typedef uint32 SIGNALS_DATA; /* a combined outbound signal se could be smaller than the defined 32-bit sizes, but IA-32 processors execute instructions with 32-bit operands much faster than those with 16- or 8-bit operands. + + 3. The DIB_REGS macro provides hidden register entries needed to save and + restore the state of a DIB. Only the potentially variable fields are + referenced. In particular, the "io_interface" field must not be saved, + as the address of the device's interface routine may change from version + to version of the simulator. */ #define DEVNO_MAX 127 /* the maximum device number */ @@ -312,6 +320,17 @@ struct dib { /* the Device Information Block t_bool service_request; /* channel service has been requested */ }; +#define DIB_REGS(dib) \ +/* Macro Name Location Width Flags */ \ +/* ------ ------- -------------------------- ----- ------- */ \ + { DRDATA (DIBDN, dib.device_number, 32), REG_HRO }, \ + { DRDATA (DIBSRN, dib.service_request_number, 32), REG_HRO }, \ + { DRDATA (DIBPRI, dib.interrupt_priority, 32), REG_HRO }, \ + { ORDATA (DIBMASK, dib.interrupt_mask, 32), REG_HRO }, \ + { ORDATA (DIBIRQ, dib.interrupt_request, 32), REG_HRO }, \ + { ORDATA (DIBACT, dib.interrupt_active, 32), REG_HRO }, \ + { ORDATA (DIBSR, dib.service_request, 32), REG_HRO } + /* Calibrated timer numbers */ @@ -334,13 +353,14 @@ typedef enum { extern UNIT *cpu_pclk_uptr; /* pointer to the process clock unit */ extern t_bool cpu_is_calibrated; /* TRUE if the process clock is calibrated */ -extern void cpu_front_panel (HP_WORD switch_reg, /* set the front panel switches as directed */ +extern void cpu_front_panel (HP_WORD switch_reg, /* set the CPU front panel switches as directed */ PANEL_TYPE request); /* Global asynchronous signal assertion functions */ extern void iop_assert_INTREQ (DIB *dib_pointer); /* assert the interrupt request signal */ +extern void iop_assert_PFWARN (void); /* assert the power failure warning signal */ extern void mpx_assert_REQ (DIB *dib_pointer); /* assert the multiplexer channel request signal */ extern void mpx_assert_SRn (DIB *dib_pointer); /* assert the multiplexer channel service request signal */ diff --git a/HP3000/hp3000_iop.c b/HP3000/hp3000_iop.c index fb01c932..779ac54b 100644 --- a/HP3000/hp3000_iop.c +++ b/HP3000/hp3000_iop.c @@ -25,6 +25,8 @@ IOP HP 3000 Series III I/O Processor + 03-Sep-16 JDB Added "iop_assert_PFWARN" to warn devices of power loss + 01-Aug-16 JDB Added "iop_reset" to initialize the IOP 30-Jun-16 JDB Changed REG type of filter array to BRDATA 08-Jun-16 JDB Corrected %d format to %u for unsigned values 13-May-16 JDB Modified for revised SCP API function parameter types @@ -120,7 +122,7 @@ memory. Direct I/O instructions are sent via the IOP Bus to all device interfaces. - When executing I/O instruc tions, the CPU microcode writes a 16-bit command + When executing I/O instructions, the CPU microcode writes a 16-bit command word to the IOP, which then places bits 5-7 of that word onto the IOP Bus as IOCMD0-2 as follows: @@ -277,6 +279,7 @@ static uint32 filter [4] = { /* filter bitmap for device numb /* IOP local SCP support routines */ +static t_stat iop_reset (DEVICE *dptr); static t_stat iop_set_filter (UNIT *uptr, int32 value, CONST char *cptr, void *desc); static t_stat iop_show_filter (FILE *st, UNIT *uptr, int32 value, CONST void *desc); @@ -342,7 +345,7 @@ DEVICE iop_dev = { DV_WIDTH, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ - NULL, /* reset routine */ + &iop_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ @@ -372,6 +375,10 @@ DEVICE iop_dev = { flip-flop values in the device DIBs and clears the external interrupt flag if there are no devices with active interrupts (as the user may have set the flag or reset the interrupting device during a simulation stop). + + The value of the IOA register is returned. This is zero unless a device + requesting an interrupt has been acknowledged but not yet serviced, in which + case the value is the device number. */ uint32 iop_initialize (void) @@ -698,11 +705,76 @@ return; } +/* Warn devices of an impending power failure. + + This routine is called by the POWER FAIL command to send a warning + to all devices that power is about to fail. It corresponds in hardware to + asserting the PFWARN signal. Devices may process or ignore the signal as + appropriate. If the device returns the INTREQ signal, an interrupt is + requested. +*/ + +void iop_assert_PFWARN (void) +{ +uint32 devno; +DIB *dibptr; +SIGNALS_DATA outbound; + +for (devno = 0; devno <= DEVNO_MAX; devno++) { /* loop through the device number list */ + dibptr = devs [devno]; /* and get the next device information block pointer */ + + if (dibptr != NULL) { /* if this device is defined */ + outbound = /* then send the PFWARN signal to the device interface */ + dibptr->io_interface (dibptr, PFWARN, 0); + + if (outbound & INTREQ) /* if the device requested an interrupt */ + iop_assert_INTREQ (dibptr); /* then set it up */ + } + } + +return; +} + + /* IOP local SCP support routines */ +/* Device reset routine. + + This routine is called for a RESET or RESET IOP command. It is the + simulation equivalent of the IORESET signal, which is asserted by the front + panel LOAD and DUMP switches. + + + Implementation notes: + + 1. In hardware, IORESET clears flip-flops associated with the state machines + that implement the interrupt poll, SO/SI handshake, and multiplexer + channel access. In simulation, these are all represented by function + calls and, as such, are atomic. Therefore, the only state variable that + IORESET clears is the external interrupt flip-flop, which is implemented + as its respective bit in the CPX1 register rather than as a separate + variable. Setting IOA to 0 and calling iop_initialize clears this bit; + it also sets up the devs array, which is used by the POWER FAIL command. + + 2. In hardware, IORESET also clears the IOP address parity error, system + parity error, and illegal address flip-flops. However, these exist only + to assert XFERERROR to devices. In simulation, XFERERROR is sent to a + device interface when the initiating condition is detected by the + multiplexer channel, so these are not represented by state variables. +*/ + +static t_stat iop_reset (DEVICE *dptr) +{ +IOA = 0; /* clear the I/O Address register and initialize */ +iop_initialize (); /* which clears the external interrupt flip-flop */ + +return SCPE_OK; +} + + /* Set the trace omission filter. If the "value" parameter is 1, the filter array bits corresponding to the diff --git a/HP3000/hp3000_lp.c b/HP3000/hp3000_lp.c index 9d6db35d..06bda815 100644 --- a/HP3000/hp3000_lp.c +++ b/HP3000/hp3000_lp.c @@ -25,6 +25,10 @@ LP HP 30209A Line Printer Interface + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG + 03-Sep-16 JDB Added power-fail detection + 08-Jul-16 JDB Added REG entry to save the transfer unit wait field + Extended "lp_show_vfu" to show the VFU channel definitions 01-Jul-16 JDB First release version 27-Apr-16 JDB Passes the On-Line HP Line Printers Verification (D466A) 19-Apr-16 JDB Passes the universal interface diagnostic (D435A) @@ -391,6 +395,12 @@ condition causes the printer to go offline at the completion of the current line. In simulation, a DETACH is handled as a torn-paper condition. + + 5. Slewing in expanded mode is performed by appending CR LF pairs to the + character buffer and then writing the combined buffer to the printer + output file. The size of the buffer must accommodate the largest print + line (136 characters) plus the largest possible slew (144 lines * 2 + characters per line). */ @@ -1019,6 +1029,7 @@ static t_bool device_end_in = FALSE; /* external DEV END signal state /* Diagnostic Hardware Assembly state */ static HP_WORD dha_control_word = 0; /* Diagnostic Hardware Assembly control word */ +static t_bool power_warning = FALSE; /* PFWARN is not asserted to the DHA */ /* Printer state */ @@ -1034,7 +1045,9 @@ static uint32 form_length; /* form length in lines */ static uint8 buffer [BUFFER_SIZE]; /* character and paper advance buffer */ static uint16 VFU [VFU_SIZE]; /* vertical format unit tape */ static char vfu_title [LINE_SIZE]; /* descriptive title of the tape currently in the VFU */ -static REG *vfu_reg; /* pointer to the VFU register entry */ + +static int32 punched_char = 'O'; /* character to display if VFU channel is punched */ +static int32 unpunched_char = '.'; /* character to display if VFU channel is not punched */ static const DELAY_PROPS *dlyptr = &fast_times; /* pointer to the event delay times to use */ @@ -1126,21 +1139,7 @@ static UNIT lp_unit [] = { Implementation notes: - 1. The VFU register displays the data currently held in the printer's - Vertical Format Unit. For the convenience of the user, the register's - width and offset fields are changed with the printer model to reflect the - number of VFU channels available, and the depth field is changed by the - VFU load routine to reflect the number of lines defined for the current - form. This allows an "EXAMINE LP VFU[ALL]" command to display the - current number of channels and lines. - - 2. The VFUREG register must immediately precede the VFU register. The - location field is changed at power-on reset to point at the VFU register - entry. This ensures that the full REG structure for the VFU register is - SAVEd and RESTOREd properly, which is necessary for the dynamic VFU - display to work (SAVE normally saves only a register's depth and value). - - 3. The DHA hardware buffers control word bits 6-10 to LEDs. Inspection and + 1. The DHA hardware buffers control word bits 6-10 to LEDs. Inspection and user confirmation of the control word state is required by the interface diagnostic. In simulation, bits 6-10 of the control word are presented as the CNLED register to allow an ASSERT command to test this subrange of @@ -1148,61 +1147,62 @@ static UNIT lp_unit [] = { */ static REG lp_reg [] = { -/* Macro Name Location Radix Width Offset Depth Flags */ -/* ------ ------ ------------------------ ----- ------------ ------ ------------- ----------------- */ - { FLDATA (SIOBSY, sio_busy, 0) }, - { FLDATA (CHANSR, channel_sr, 0) }, - { FLDATA (DEVSR, device_sr, 0) }, - { FLDATA (INXFR, input_xfer, 0) }, - { FLDATA (OUTXFR, output_xfer, 0) }, - { FLDATA (RDXFR, read_xfer, 0) }, - { FLDATA (WRXFR, write_xfer, 0) }, - { FLDATA (INTMSK, interrupt_mask, 0) }, +/* Macro Name Location Radix Width Offset Depth Flags */ +/* ------ ------ ------------------------ ----- ------------ ------ ------------- ------------------ */ + { FLDATA (SIOBSY, sio_busy, 0) }, + { FLDATA (CHANSR, channel_sr, 0) }, + { FLDATA (DEVSR, device_sr, 0) }, + { FLDATA (INXFR, input_xfer, 0) }, + { FLDATA (OUTXFR, output_xfer, 0) }, + { FLDATA (RDXFR, read_xfer, 0) }, + { FLDATA (WRXFR, write_xfer, 0) }, + { FLDATA (INTMSK, interrupt_mask, 0) }, - { FLDATA (DEVCMD, device_command, 0) }, - { FLDATA (DEVFLG, device_flag, 0) }, - { FLDATA (DEVEND, device_end, 0) }, + { FLDATA (DEVCMD, device_command, 0) }, + { FLDATA (DEVFLG, device_flag, 0) }, + { FLDATA (DEVEND, device_end, 0) }, - { DRDATA (SEQSTA, sequencer, 8), PV_LEFT }, - { ORDATA (CNTL, control_word, 16), PV_RZRO }, - { ORDATA (ISTAT, int_status_word, 16), PV_RZRO }, - { ORDATA (DSTAT, dev_status_word, 16), PV_RZRO }, - { ORDATA (READ, read_word, 16), PV_RZRO | REG_A }, - { ORDATA (WRITE, write_word, 16), PV_RZRO | REG_A }, - { YRDATA (J2WX, jumper_set, 10, PV_RZRO) }, + { DRDATA (SEQSTA, sequencer, 8), PV_LEFT }, + { ORDATA (CNTL, control_word, 16), PV_RZRO }, + { ORDATA (ISTAT, int_status_word, 16), PV_RZRO }, + { ORDATA (DSTAT, dev_status_word, 16), PV_RZRO }, + { ORDATA (READ, read_word, 16), PV_RZRO | REG_A }, + { ORDATA (WRITE, write_word, 16), PV_RZRO | REG_A }, + { YRDATA (J2WX, jumper_set, 10, PV_RZRO) }, - { ORDATA (DATOUT, data_out, 16), PV_RZRO | REG_A }, - { ORDATA (DATIN, data_in, 16), PV_RZRO | REG_A }, + { ORDATA (DATOUT, data_out, 16), PV_RZRO | REG_A }, + { ORDATA (DATIN, data_in, 16), PV_RZRO | REG_A }, - { FLDATA (DCOUT, device_command_out, 0) }, - { FLDATA (DFIN, device_flag_in, 0) }, - { FLDATA (DENDIN, device_end_in, 0) }, + { FLDATA (DCOUT, device_command_out, 0) }, + { FLDATA (DFIN, device_flag_in, 0) }, + { FLDATA (DENDIN, device_end_in, 0) }, - { SRDATA (DIB, lp_dib, REG_HRO) }, + DIB_REGS (lp_dib), + { ORDATA (DIAGCN, dha_control_word, 16), PV_RZRO }, + { GRDATA (CNLED, control_word, 2, 5, 5), PV_RZRO }, + { FLDATA (PFWARN, power_warning, 0) }, - { ORDATA (DIAGCN, dha_control_word, 16), PV_RZRO }, - { GRDATA (CNLED, control_word, 2, 5, 5), PV_RZRO }, + { FLDATA (PFAULT, paper_fault, 0) }, + { FLDATA (TFAULT, tape_fault, 0) }, + { FLDATA (OLPEND, offline_pending, 0) }, + { DRDATA (PRLINE, current_line, 8), PV_LEFT }, + { DRDATA (BUFIDX, buffer_index, 8), PV_LEFT }, + { BRDATA (PRTBUF, buffer, 8, 8, BUFFER_SIZE), PV_RZRO | REG_A }, + { ORDATA (OVPCHR, overprint_char, 8), PV_RZRO | REG_A }, - { FLDATA (PFAULT, paper_fault, 0) }, - { FLDATA (TFAULT, tape_fault, 0) }, - { FLDATA (OLPEND, offline_pending, 0) }, + { DRDATA (FORMLN, form_length, 8), PV_LEFT | REG_RO }, + { BRDATA (TITLE, vfu_title, 8, 8, LINE_SIZE), REG_HRO }, + { BRDATA (VFU, VFU, 2, VFU_WIDTH, VFU_SIZE), PV_RZRO | REG_RO }, + { ORDATA (PCHR, punched_char, 8), PV_RZRO | REG_A }, + { ORDATA (UPCHR, unpunched_char, 8), PV_RZRO | REG_A }, - { DRDATA (PRLINE, current_line, 8), PV_LEFT }, - { DRDATA (BUFIDX, buffer_index, 8), PV_LEFT }, - { BRDATA (PRTBUF, buffer, 8, 8, BUFFER_SIZE), PV_RZRO | REG_A }, - { ORDATA (OVPCHR, overprint_char, 8), PV_RZRO | REG_A }, - - { DRDATA (FORMLN, form_length, 8), PV_LEFT | REG_RO }, - { BRDATA (TITLE, vfu_title, 8, 8, LINE_SIZE), REG_HRO }, - { SRDATA (VFUREG, lp_reg [0], REG_HRO) }, - { BRDATA (VFU, VFU, 2, VFU_WIDTH, VFU_SIZE), PV_RZRO | REG_RO }, - - { DRDATA (BTIME, fast_times.buffer_load, 24), PV_LEFT | REG_NZ }, - { DRDATA (PTIME, fast_times.print, 24), PV_LEFT | REG_NZ }, - { DRDATA (STIME, fast_times.advance, 24), PV_LEFT | REG_NZ }, - { DRDATA (POS, lp_unit [0].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (BTIME, fast_times.buffer_load, 24), PV_LEFT | REG_NZ }, + { DRDATA (PTIME, fast_times.print, 24), PV_LEFT | REG_NZ }, + { DRDATA (STIME, fast_times.advance, 24), PV_LEFT | REG_NZ }, + { DRDATA (POS, lp_unit [0].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (UWAIT, lp_unit [0].wait, 32), PV_LEFT | REG_HRO }, { NULL } }; @@ -1231,20 +1231,21 @@ static MTAB lp_mod [] = { { UNIT_EXPAND, UNIT_EXPAND, "expanded output", "EXPAND", NULL, NULL, NULL }, { UNIT_EXPAND, 0, "compact output", "COMPACT", NULL, NULL, NULL, }, -/* Entry Flags Value Print String Match String Validation Display Descriptor */ -/* ------------------ ----------- ------------ ------------ ------------ ------------- ---------------- */ - { MTAB_XDV, Fast_Time, NULL, "FASTTIME", &lp_set_mode, NULL, NULL }, - { MTAB_XDV, Real_Time, NULL, "REALTIME", &lp_set_mode, NULL, NULL }, - { MTAB_XDV, Printer, NULL, "PRINTER", &lp_set_mode, NULL, NULL }, - { MTAB_XDV, Diagnostic, NULL, "DIAGNOSTIC", &lp_set_mode, NULL, NULL }, - { MTAB_XDV, 0, "MODES", NULL, NULL, &lp_show_mode, NULL }, +/* Entry Flags Value Print String Match String Validation Display Descriptor */ +/* ------------------- ----------- ------------ ------------ ------------ ------------- ---------------- */ + { MTAB_XDV, Fast_Time, NULL, "FASTTIME", &lp_set_mode, NULL, NULL }, + { MTAB_XDV, Real_Time, NULL, "REALTIME", &lp_set_mode, NULL, NULL }, + { MTAB_XDV, Printer, NULL, "PRINTER", &lp_set_mode, NULL, NULL }, + { MTAB_XDV, Diagnostic, NULL, "DIAGNOSTIC", &lp_set_mode, NULL, NULL }, + { MTAB_XDV, 0, "MODES", NULL, NULL, &lp_show_mode, NULL }, - { MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, - { MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, - { MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, - { MTAB_XDV, VAL_SRNO, "SRNO", "SRNO", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, + { MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, + { MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, + { MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, + { MTAB_XDV, VAL_SRNO, "SRNO", "SRNO", &hp_set_dib, &hp_show_dib, (void *) &lp_dib }, - { MTAB_XDV | MTAB_NC, 0, "VFU", "VFU", &lp_set_vfu, &lp_show_vfu, NULL }, + { MTAB_XDV | MTAB_NMO, 1, "VFU", NULL, NULL, &lp_show_vfu, NULL }, + { MTAB_XDV | MTAB_NC, 0, "VFU", "VFU", &lp_set_vfu, &lp_show_vfu, NULL }, { 0 } }; @@ -1343,7 +1344,10 @@ DEVICE lp_dev = { 2. In hardware, the SETJMP signal is ignored, and the JMPMET signal is asserted continuously when enabled by CHANSO. - 3. Sending a power fail warning to the device is not currently simulated. + 3. In hardware, a power fail warning (PFWARN) is asserted continuously from + detection until power is lost. In simulation, the "power_warning" flag + is set by a PFWARN assertion and is cleared by a power-on reset. PFWARN + is used only by the DHA. */ static SIGNALS_DATA ui_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) @@ -1622,7 +1626,8 @@ while (working_set) { break; - case PFWARN: /* not currently simulated */ + case PFWARN: + power_warning = TRUE; /* system power is in the process of failing */ break; @@ -2382,6 +2387,13 @@ return SCPE_OK; 2. The DHA transfer service is called with a null pointer to update the potential change in the flag state. + + 3. Setting bit 2 of the DHA control word reflects the current state of the + PON and ~PFWARN signals in status bits 9 and 10, respectively. Status 9 + is always set, as PON is always active while the machine is operating. + Status 10 is normally set to indicate that PFWARN is denied. However, if + the system power is failing, PFWARN is asserted from detection until + power is lost. */ static OUTBOUND_SET diag_control (uint32 control_word) @@ -2426,7 +2438,10 @@ if (control_word & CN_DHA_FN_ENABLE) /* if the decoder is ena if (dha_control_word & DHA_STAT_SEL) { /* if status follows master clear/power on/power fail */ - new_status = ST_DHA_PON | ST_DHA_NOT_PF; /* then indicate that power is on and has not failed */ + new_status = ST_DHA_PON; /* then indicate that power is on */ + + if (power_warning == FALSE) /* if we have seen a PFWARN signal */ + new_status |= ST_DHA_NOT_PF; /* then indicate that power has not failed */ if (dha_control_word & DHA_MR) /* if a master reset is requested */ new_status |= ST_DHA_MR; /* then indicate a master clear */ @@ -2535,20 +2550,14 @@ return outbound_signals; /* return INTREQ if any Implementation notes: - 1. Slewing in expanded mode is performed by appending CR LF pairs to the - character buffer and then writing the combined buffer to the file. The - size of the buffer must accommodate the largest print line (136 - characters) plus the largest possible slew (144 lines * 2 characters per - line) plus a trailing NUL. - - 2. Because attached files are opened in binary mode, newline translation + 1. Because attached files are opened in binary mode, newline translation (i.e., from LF to CR LF) is not performed by the host system. Therefore, we write explicit CR LF pairs to end lines, even in compact mode, as required for fidelity to HP peripherals. If bare LFs are used by the host system, the printer output file must be postprocessed to remove the CRs. - 3. Overprinting in expanded mode is simulated by merging the lines in the + 2. Overprinting in expanded mode is simulated by merging the lines in the buffer. A format command to suppress spacing resets the buffer index but saves the previous buffer length as a "high water mark" that will be extended if the overlaying line is longer. This process may be repeated @@ -2561,20 +2570,20 @@ return outbound_signals; /* return INTREQ if any "overprint character" (which defaults to DEL, but can be changed by the user) replaces the character in the buffer. - 4. Printers that support 12-channel VFUs treat the VFU format command as + 3. Printers that support 12-channel VFUs treat the VFU format command as modulo 16. Printers that support 8-channel VFUs treat the command as modulo 8. - 5. As a convenience to the user, the printer output file is flushed when a + 4. As a convenience to the user, the printer output file is flushed when a TOF operation is performed. - 6. The user may examine the TFAULT and PFAULT registers to determine why the + 5. The user may examine the TFAULT and PFAULT registers to determine why the printer went offline. - 7. The transfer service may be called with a null pointer to update the + 6. The transfer service may be called with a null pointer to update the potential change in the flag state. - 8. If printing is attempted with the printer offline, this routine will be + 7. If printing is attempted with the printer offline, this routine will be called with STROBE asserted (device_command_in TRUE) and DEMAND denied (device_flag_in TRUE). The printer ignores STROBE if DEMAND is not asserted, so we simply return in this case. This will hang the handshake @@ -2648,9 +2657,6 @@ else if (device_flag_in == FALSE) { /* otherwise if STROBE h buffer_index++; /* increment the buffer index */ uptr->wait = dlyptr->buffer_load; /* schedule the buffer load delay */ - - dprintf (lp_dev, DEB_XFER, "Character %s sent to printer\n", - fmt_char (data_byte)); } else { /* otherwise the buffer is full */ @@ -2678,13 +2684,13 @@ else if (device_flag_in == FALSE) { /* otherwise if STROBE h buffer [0] = data_byte; /* store the character */ buffer_index = 1; /* in the empty buffer */ - dprintf (lp_dev, DEB_XFER, "Character %s sent to printer\n", - fmt_char (data_byte)); - uptr->wait = dlyptr->print /* schedule the print delay */ + dlyptr->advance /* plus the paper advance delay */ + dlyptr->buffer_load; /* plus the buffer load delay */ } + + dprintf (lp_dev, DEB_XFER, "Character %s sent to printer\n", + fmt_char (data_byte)); } else { /* otherwise this is a print format command */ @@ -3010,16 +3016,11 @@ return SCPE_OK; /* the mode change succe This validation routine is called to set the model of the printer. The "value" parameter is one of the UNIT_26nn constants that indicates the new model. Validation isn't necessary, except to detect a model change and alter - the real-time delays and the VFU display width and offset fields accordingly. + the real-time delays accordingly. */ static t_stat lp_set_model (UNIT *uptr, int32 value, CONST char *cptr, void *desc) { -const PRINTER_TYPE model = GET_MODEL (value); /* get the model associated with the value */ - -vfu_reg->width = print_props [model].vfu_channels; /* set the number of VFU channels to display */ -vfu_reg->offset = VFU_WIDTH - vfu_reg->width; /* and the offset to the last channel */ - if (lp_dev.flags & DEV_REALTIME) /* if the printer is in real-time mode */ dlyptr = &real_times [GET_MODEL (uptr->flags)]; /* then use the times for the new model */ @@ -3145,17 +3146,61 @@ return SCPE_OK; /* Show the VFU tape. - This display routine is called to show the title of the tape currently loaded - in the printer's VFU. The title is taken from the tape image file if a - custom tape is loaded. Otherwise, the standard tape title is displayed. + This display routine is called to show the content of the tape currently + loaded in the printer's VFU. The "value" parameter indicates how the routine + was called. It is 0 if a SHOW LP command was given and 1 if a SHOW LP VFU + command was issued. For the former, only the VFU title is displayed. The + latter displays the VFU title, followed by a header labelling each of the + channel columns and then one line for each line of the form consisting of + punch and no-punch characters, according to the VFU definition. - The output stream is passed in the "st" parameter, and the other parameters - are ignored. + The output stream is passed in the "st" parameter, and the "uptr" and "desc" + parameters are ignored. + + + Implementation notes: + + 1. Setting the string precision for the header lines trims them to the + appropriate number of channels. */ static t_stat lp_show_vfu (FILE *st, UNIT *uptr, int32 value, CONST void *desc) { -fputs (vfu_title, st); /* print the VFU title */ +static const char header_1 [] = " Ch 1 Ch 2 Ch 3 Ch 4 Ch 5 Ch 6 Ch 7 Ch 8 Ch 9 Ch10 Ch11 Ch12"; +static const char header_2 [] = " ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----"; + +const PRINTER_TYPE model = GET_MODEL (uptr->flags); +const uint32 channel_count = print_props [model].vfu_channels; +uint32 chan, line, current_channel; + +if (value == 0) /* if we're called for a summary display */ + fputs (vfu_title, st); /* then output only the VFU title */ + +else { /* otherwise the full VFU definition is requested */ + fprintf (st, "\n%s tape is loaded.\n\n", vfu_title); /* so start by displaying the VFU title */ + + fprintf (st, "Line %.*s\n", channel_count * 5, header_1); /* display the */ + fprintf (st, "---- %.*s\n", channel_count * 5, header_2); /* channel headers */ + + for (line = 1; line <= form_length; line++) { /* loop through the VFU array */ + fprintf (st, "%3d ", line); /* display the current form line number */ + + current_channel = VFU_CHANNEL_1; /* start with channel 1 */ + + for (chan = 1; chan <= channel_count; chan++) { /* loop through the defined channels */ + fputs (" ", st); /* add some space */ + + if (VFU [line] & current_channel) /* if the current channel is punched for this line */ + fputc (punched_char, st); /* then display a punched location */ + else /* otherwise */ + fputc (unpunched_char, st); /* display an unpunched location */ + + current_channel = current_channel >> 1; /* move to the next channel */ + } + + fputc ('\n', st); /* end the line */ + } + } return SCPE_OK; } @@ -3191,19 +3236,8 @@ return SCPE_OK; online). In addition, if a power-on reset (RESET -P) is done, the original FASTTIME - settings are restored, the pointer to the VFU register is determined, and the - standard VFU tape is loaded. - - - Implementation notes: - - 1. Setting the "vfu_reg" pointer at run time accommodates changes in the - register order automatically. A fixed setting runs the risk of it not - being updated if a change in the register order is made. - - 2. The location field of the register entry preceding the VFU register is - changed to point at the VFU register. This is required to preserve the - dynamic VFU register settings across a SAVE and RESTORE operation. + settings are restored, the standard VFU tape is loaded, and the power failure + warning is cleared. */ static t_stat lp_reset (t_bool programmed_clear) @@ -3217,18 +3251,9 @@ if (! programmed_clear && (sim_switches & SWMASK ('P'))) { /* if this is a comm fast_times.print = LP_PRINT; /* the print and advance-one-line time, */ fast_times.advance = LP_ADVANCE; /* and the slew additional lines time */ - for (vfu_reg = lp_reg; /* find the VFU register entry */ - vfu_reg->loc != VFU && vfu_reg->loc != NULL; /* in the register array */ - vfu_reg++); - - if (vfu_reg == NULL) /* if the VFU register entry is not present */ - return SCPE_NXREG; /* then there is a serious problem! */ - else /* otherwise */ - (vfu_reg - 1)->loc = (void *) vfu_reg; /* point the prior entry's location at the VFU register */ - result = lp_load_vfu (xfer_uptr, NULL); /* load the standard VFU tape */ - lp_set_model (NULL, xfer_unit.flags, NULL, NULL); /* set the VFU register width and offset */ + power_warning = FALSE; /* clear the power failure warning */ } buffer_index = 0; /* clear the buffer without printing */ @@ -3436,7 +3461,7 @@ return TRUE; no-punch character a single character representing a non-punched location title an optional descriptive string printed by the SHOW LP - VFU command ("custom VFU" is used by default) + VFU command ("Custom VFU" is used by default) If the "VFU" line is missing or not of the correct form, then "Format error" status is returned, and the VFU tape is not changed. @@ -3488,10 +3513,6 @@ return TRUE; logical OR of all of the other entries and is used during VFU format command processing to determine if a punch is present somewhere in a given channel. - - 2. The depth field of the VFU register is set to the form length so that an - EXAMINE LP VFU[ALL] command will display only the defined VFU. - */ static t_stat lp_load_vfu (UNIT *uptr, FILE *vf) @@ -3504,7 +3525,7 @@ char *bptr, *tptr; uint16 tape [VFU_SIZE] = { 0 }; if (vf == NULL) { /* if the standard VFU is requested */ - strcpy (vfu_title, "standard VFU"); /* then set the title */ + strcpy (vfu_title, "Standard VFU"); /* then set the title */ tape [ 1] = VFU_CHANNEL_1; /* punch channel 1 for the top of form */ tape [60] = VFU_CHANNEL_2 | VFU_CHANNEL_9; /* punch channels 2 and 9 for the bottom of form */ @@ -3557,7 +3578,7 @@ else { /* otherwise load a cust if (tptr != NULL) /* if it's present */ strcpy (vfu_title, tptr); /* then save the user's title */ else /* otherwise */ - strcpy (vfu_title, "custom VFU"); /* use a generic title */ + strcpy (vfu_title, "Custom VFU"); /* use a generic title */ for (line = 1; line <= VFU_MAX; line++) { /* load up to the maximum VFU tape length */ @@ -3610,8 +3631,6 @@ if (print_props [model].vfu_channels > 8) { /* if the printer VFU ha set_device_status (ST_VFU_9 | ST_VFU_12, vfu_status); /* set the VFU status */ -vfu_reg->depth = form_length + 1; /* set the VFU display depth */ - return SCPE_OK; /* the VFU was successfully loaded */ } diff --git a/HP3000/hp3000_mpx.c b/HP3000/hp3000_mpx.c index 188f7948..b4046140 100644 --- a/HP3000/hp3000_mpx.c +++ b/HP3000/hp3000_mpx.c @@ -25,6 +25,8 @@ MPX HP 3000 Series III Multiplexer Channel + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG + 15-Jul-16 JDB Fixed the word count display for DREADSTB trace 08-Jun-16 JDB Corrected %d format to %u for unsigned values 07-Jun-16 JDB Corrected ACKSR assertion in State A for chained orders 16-May-16 JDB abort_channel parameter is now a pointer-to-constant @@ -732,7 +734,7 @@ static REG mpx_reg [] = { { ORDATA (CTRREG, cntr_reg, 16), REG_FIT | REG_HRO }, { ORDATA (ADRREG, addr_reg, 16), REG_FIT | REG_HRO }, - { SRDATA (DIB, mpx_dib, REG_HRO) }, + DIB_REGS (mpx_dib), { NULL } }; @@ -1737,7 +1739,7 @@ while (working_set) { dprintf (mpx_dev, DEB_CSRW, "Order register value %02o (%s) " "and counter register value %d returned\n", order_reg & ORDER_MASK, sio_order_name [IOCW_ORDER (outbound_value)], - IOCW_COUNT (outbound_value)); + SEXT (IOCW_COUNT (outbound_value))); } if (control_word & CN_ADDR_RAM) { /* if the address register is selected */ diff --git a/HP3000/hp3000_ms.c b/HP3000/hp3000_ms.c index f058b4f2..3372bdf9 100644 --- a/HP3000/hp3000_ms.c +++ b/HP3000/hp3000_ms.c @@ -25,6 +25,7 @@ MS HP 30215A Magnetic Tape Controller Interface + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG 09-Jun-16 JDB Added casts for ptrdiff_t to int32 values 08-Jun-16 JDB Corrected %d format to %u for unsigned values 16-May-16 JDB Fixed interrupt mask setting @@ -517,9 +518,10 @@ static REG ms_reg [] = { { DRDATA (ATUNIT, attention_unit, 16), REG_FIT | PV_LEFT }, { DRDATA (CLASS, command_class, 4), PV_LEFT }, { YRDATA (FLAGS, flags, 8, PV_RZRO) }, - { SRDATA (DIB, ms_dib, REG_HRO) }, - TL_REGS (ms_cntlr, ms_unit, DRIVE_COUNT, buffer, fast_times), + DIB_REGS (ms_dib), + + TL_REGS (ms_cntlr, ms_unit, DRIVE_COUNT, buffer, fast_times), { NULL } }; diff --git a/HP3000/hp3000_release.txt b/HP3000/hp3000_release.txt index 29b213c7..3fd5131f 100644 --- a/HP3000/hp3000_release.txt +++ b/HP3000/hp3000_release.txt @@ -1,6 +1,6 @@ SIMH/HP 3000 RELEASE NOTES ========================== - Last update: 2016-07-05 + Last update: 2016-09-20 This file documents the release history of the Hewlett-Packard 3000 simulator. @@ -45,7 +45,7 @@ The simulator has been tested with MPE-V/R version E.01.00. Specifically: run properly. - The SYSDUMP program produces a valid tape image, and the system may be - COLDSTARTED from it. + COLDSTARTed from it. - The operator and system manager can be logged in and out, and MPE can be SHUTDOWN through to a HALT 17. @@ -158,6 +158,187 @@ the MPE version used: +===================== +Release 3, 2016-09-20 +===================== + +This release of the HP 3000 simulator adds the following features: + + - Cold dump is now available. Entering the DUMP command simulates pressing + the ENABLE and DUMP front panel buttons. The contents of main memory are + written to an attached magnetic tape in a format suitable for analyzing with + the DPAN4 program. The new SET CPU DUMPDEV and SET CPU DUMPCTL options + specify the default device number and control byte for the dump. + + - The SHOW LP VFU command now displays the VFU channel definitions in + addition to the VFU tape title. + + - The POWER FAIL and POWER RESTORE commands have been added to simulate losing + and regaining system power. + + - The SET CPU ARS and SET CPU NOARS options have been added to simulate the + power-fail/auto-restart switch on the back of the system front panel. + + - The CMD instruction has been implemented and passes section 4 of the CPU + diagnostic. + + +-------------------- +Implementation Notes +-------------------- + + - In hardware, MPE execution cannot continue after a DUMP is performed. This + is because a cold dump performs an I/O reset before writing the contents of + memory to the tape, and this clears the I/O device controllers to their + initial power-on states. However, execution can be continued if a SAVE is + done to record the simulator state before the dump and a RESTORE is done + after the dump completes. This permits taking a "snapshot" of MPE operation + without disturbing MPE. + + +---------- +Bugs Fixed +---------- + + 1. PROBLEM: An SIO READ or WRITE order with a 4K count displays as zero. + + VERSION: Release 2 + + OBSERVATION: SIO READ and WRITE orders define bits 4-15 as the negative + word count of the transfer. If bits 4-15 are zero, the transfer is 4096 + words long. However, an EXAMINE -I command displays the word count as + zero. + + CAUSE: The display value is being calculated incorrectly. + + RESOLUTION: Modify "IOCW_COUNT" (hp3000_cpu_ims.h) to sign-extend the + 12-bit count correctly to 16 bits, and modify "fprint_order" (hp3000_sys.c) + to negate the values to display the counts as positive. Also modify + "mpx_interface" (hp3000_mpx.c) to display the correct count in the debug + trace for the DREADSTB operation. + + STATUS: Fixed in Release 3. + + + 2. PROBLEM: An I/O reset does not clear a pending external interrupt. + + VERSION: Release 2 + + OBSERVATION: A cold load begins with a CPU reset and an I/O reset. A cold + dump begins with an I/O reset only to preserve the CPU state for the dump + operation. The external interrupt flip-flop on the IOP is cleared by an + I/O reset, which should clear the external interrupt bit in the CPX1 + register. However, this does not occur, causing the interrupt generated by + placing the tape drive online to be misinterpreted as the SIO program + completion interrupt. Because the SIO pointer is not set as expected, the + cold dump microcode assumes that a tape error occurred and performs a + retry. This writes an erase gap at the beginning of the tape but otherwise + produces a valid tape. + + CAUSE: Oversight. + + RESOLUTION: Add a new "iop_reset" routine (hp3000_iop.c) that is called + during an I/O reset and that clears the external interrupt bit of the CPX1 + register. + + STATUS: Fixed in Release 3. + + + 3. PROBLEM: RESTORE of a file SAVEd with a different executable may abort the + simulator. + + VERSION: Release 2 + + OBSERVATION: Entering SAVE to save the simulator state on an executable + compiled with one set of compiler options or compiler version and then + entering RESTORE to restore the state on an executable compiled with a + different set of compiler options or compiler version succeeds. However, + attempting to resume execution results in an access exception. + + CAUSE: The simulator's internal Device Information Blocks contain pointers + to the devices' I/O interface handlers, which are saved as part of the DIB + structure in the simulator state file. When restoring the state, the + interface handler pointers are restored. However, the addresses of one or + more routines may have changed, due to differing memory layouts, so the + restored values are no longer correct. If they are not, and I/O is + performed to the affected device(s), control transfers to an invalid code + location. + + RESOLUTION: Modify hp3000_io.h to add a new REG_DIB macro that defines the + register entries needed to save the DIB state, and modify hp3000_atc.c, + hp3000_clk.c, hp3000_ds.c, hp3000_lp.c, hp3000_mpx.c, hp3000_ms.c, and + hp3000_scmb.c to change the REG entries referencing the DIB structures to + use the REG_DIB macro. + + STATUS: Fixed in Release 3. + + + 4. PROBLEM: The LOAD command does not report "Cold load complete". + + VERSION: Release 2 + + OBSERVATION: The LOAD command should report success after completion of a + cold load operation, but it doesn't. Instead, the SCP prompt returns with + no indication of whether the command succeeded or failed. Using the + equivalent BOOT CPU command does print the expected "Cold load complete" + message. + + CAUSE: The "Cold load complete" message is printed by the simulator's + "fprint_stopped" routine that is called via the "sim_vm_fprint_stopped" + pointer from the "run_cmd_message" routine in SCP. The latter is invoked + via the "message" field of the command table. The LOAD, DUMP, and POWER + commands all invoke "sim_instr" via "run_cmd" but do not specify routine + pointers for their message fields, so no completion messages are reported. + + RESOLUTION: Modify "one_time_init" (hp3000_sys.c) to set the "message" + fields of the LOAD, DUMP, and POWER commands to point at the same routine + as is used by the system "CONTINUE" command. + + STATUS: Fixed in Release 3. + + + 5. PROBLEM: RESTOREing with the ATCD attached cancels active line services. + + VERSION: Release 2 + + OBSERVATION: Doing a SAVE while the ATCD has line services scheduled, + e.g., while outputting characters, and then following immediately with a + RESTORE cancels the line services. For example, after a SAVE, a SHOW QUEUE + command prints: + + HP 3000 event queue status, time = 907247803 + CLK at 0 + ATCD unit 0 at 241 + CPU at 27917 + ATCD unit 16 at 27918 + DS unit 8 at 612615 + + Entering RESTORE and then SHOW QUEUE prints: + + HP 3000 event queue status, time = 907247803 + CLK at 0 + CPU at 27917 + ATCD unit 16 at 27918 + DS unit 8 at 612615 + + Note that ATCD unit 0 is no longer scheduled. + + CAUSE: The "atcd_detach" routine is called during RESTORE if the listening + port is currently attached in preparation for reattaching to the port + specified in the SAVE file. The routine detaches the listening port and + then cancels each line to terminate any transfers in progress. This is + appropriate for DETACH ATCD and DETACH ALL, but not for RESTORE, as the + terminal channels have already been rescheduled as indicated in the SAVE + file, and canceling them hangs the channels. + + RESOLUTION: Modify "atcd_detach" (hp3000_atc.c) to skip the channel + termination loop if the SIM_SW_REST flag is set to indicate a RESTORE in + progress. + + STATUS: Fixed in Release 3. + + + ===================== Release 2, 2016-07-05 ===================== diff --git a/HP3000/hp3000_scmb.c b/HP3000/hp3000_scmb.c index 97bee143..a8b7feaa 100644 --- a/HP3000/hp3000_scmb.c +++ b/HP3000/hp3000_scmb.c @@ -25,6 +25,7 @@ SCMB1,SCMB2 HP 30033A Selector Channel Maintenance Board + 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG 11-Jun-16 JDB Bit mask constants are now unsigned 13-May-16 JDB Modified for revised SCP API function parameter types 21-Mar-16 JDB Changed uint16 types to HP_WORD @@ -462,7 +463,7 @@ static REG scmb1_reg [] = { { FLDATA (DEVEND, scmb [card1].device_end, 0) }, { FLDATA (STOP, scmb [card1].stop_transfer, 0) }, - { SRDATA (DIB, scmb_dib [card1], REG_HRO) }, + DIB_REGS (scmb_dib [card1]), { NULL } }; @@ -489,7 +490,7 @@ static REG scmb2_reg [] = { { FLDATA (DEVEND, scmb [card2].device_end, 0) }, { FLDATA (STOP, scmb [card2].stop_transfer, 0) }, - { SRDATA (DIB, scmb_dib [card1], REG_HRO) }, + DIB_REGS (scmb_dib [card2]), { NULL } }; diff --git a/HP3000/hp3000_sel.c b/HP3000/hp3000_sel.c index fd6c3b91..4a9053a6 100644 --- a/HP3000/hp3000_sel.c +++ b/HP3000/hp3000_sel.c @@ -25,6 +25,7 @@ SEL HP 3000 Series III Selector Channel + 11-Jul-16 JDB Change "sel_unit" from a UNIT to an array of one UNIT 30-Jun-16 JDB Reestablish active_dib pointer during sel_initialize 08-Jun-16 JDB Corrected %d format to %u for unsigned values 16-May-16 JDB abort_channel parameter is now a pointer-to-constant @@ -426,8 +427,8 @@ static void load_address (HP_WORD *value); /* Unit list */ -static UNIT sel_unit = { - UDATA (&sel_timer, 0, 0), SR_WAIT_TIMER +static UNIT sel_unit [] = { + { UDATA (&sel_timer, 0, 0), SR_WAIT_TIMER } }; /* Register list */ @@ -476,7 +477,7 @@ static DEBTAB sel_deb [] = { DEVICE sel_dev = { "SEL", /* device name */ - &sel_unit, /* unit array */ + sel_unit, /* unit array */ sel_reg, /* register array */ NULL, /* modifier array */ 1, /* number of units */ @@ -608,7 +609,7 @@ else { /* otherwise abort the t device_number); end_channel (dibptr); /* idle the channel */ - sim_cancel (&sel_unit); /* and cancel the CHANSR timer */ + sim_cancel (&sel_unit [0]); /* and cancel the CHANSR timer */ } return; @@ -817,7 +818,7 @@ while (sel_request && cycles > 0) { /* execute as long as a case Fetch_Sequence: - sim_cancel (&sel_unit); /* cancel the CHANSR timer */ + sim_cancel (&sel_unit [0]); /* cancel the CHANSR timer */ load_control (&control_word); /* load the IOCW */ load_address (&address_word); /* and the IOAW */ @@ -963,8 +964,8 @@ while (sel_request && cycles > 0) { /* execute as long as a prefetch_control = FALSE; /* prefetching is not used */ prefetch_address = FALSE; /* for the Control order */ - sim_activate (&sel_unit, sel_unit.wait); /* start the SR timer */ - sequencer = Wait_Sequence; /* and check for a timeout */ + sim_activate (&sel_unit [0], sel_unit [0].wait); /* start the SR timer */ + sequencer = Wait_Sequence; /* and check for a timeout */ break; case sioWRITE: @@ -972,8 +973,8 @@ while (sel_request && cycles > 0) { /* execute as long as a prefetch_control = (order == sioWRITEC); /* enable prefetching */ prefetch_address = (order == sioWRITEC); /* if the order is chained */ - sim_activate (&sel_unit, sel_unit.wait); /* start the SR timer */ - sequencer = Wait_Sequence; /* and check for a timeout */ + sim_activate (&sel_unit [0], sel_unit [0].wait); /* start the SR timer */ + sequencer = Wait_Sequence; /* and check for a timeout */ break; case sioREAD: @@ -987,8 +988,8 @@ while (sel_request && cycles > 0) { /* execute as long as a prefetch_control = FALSE; /* mark the job done */ } - sim_activate (&sel_unit, sel_unit.wait); /* start the SR timer */ - sequencer = Wait_Sequence; /* and check for a timeout */ + sim_activate (&sel_unit [0], sel_unit [0].wait); /* start the SR timer */ + sequencer = Wait_Sequence; /* and check for a timeout */ break; } /* end switch */ @@ -997,7 +998,7 @@ while (sel_request && cycles > 0) { /* execute as long as a case Wait_Sequence: - sim_cancel (&sel_unit); /* cancel the SR timer */ + sim_cancel (&sel_unit [0]); /* cancel the SR timer */ sequencer = Transfer_Sequence; /* continue with the transfer sequence */ @@ -1071,9 +1072,9 @@ while (sel_request && cycles > 0) { /* execute as long as a prefetch_address = FALSE; /* mark the job done */ } - if (order == sioCNTL) { /* if this is a Control order */ - sim_activate (&sel_unit, sel_unit.wait); /* then start the SR timer */ - sequencer = Fetch_Sequence; /* and the next state is Fetch */ + if (order == sioCNTL) { /* if this is a Control order */ + sim_activate (&sel_unit [0], sel_unit [0].wait); /* then start the SR timer */ + sequencer = Fetch_Sequence; /* and the next state is Fetch */ } else { /* otherwise it's a Write or Read (Chained) order */ @@ -1173,7 +1174,7 @@ while (sel_request && cycles > 0) { /* execute as long as a active_dib->service_request = FALSE; /* clear the current service request */ else /* otherwise the channel has stopped */ - sim_cancel (&sel_unit); /* so cancel the CHANSR timer */ + sim_cancel (&sel_unit [0]); /* so cancel the CHANSR timer */ } /* end while */ diff --git a/HP3000/hp3000_sys.c b/HP3000/hp3000_sys.c index fb6e6271..8c1bcba3 100644 --- a/HP3000/hp3000_sys.c +++ b/HP3000/hp3000_sys.c @@ -23,6 +23,13 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 15-Sep-16 JDB Modified "one_time_init" to set aux_cmds "message" field + 03-Sep-16 JDB Added the STOP_POWER and STOP_ARSINH messages + 01-Sep-16 JDB Moved the hp_cold_cmd routine to the CPU (as cpu_cold_cmd) + Added the POWER command + 03-Aug-16 JDB Improved "fmt_char" and "fmt_bitset" to allow multiple calls + 15-Jul-16 JDB Fixed the word count display for SIO read/write orders + 14-Jul-16 JDB Improved the cold dump invocation 21-Jun-16 JDB Changed fprint_instruction mask type from t_value to uint32 08-Jun-16 JDB Corrected %d format to %u for unsigned values 16-May-16 JDB Prefix in fprint_instruction is now a pointer-to-constant @@ -756,7 +763,6 @@ static t_bool fprint_stopped (FILE *st, t_stat reason); static void fprint_addr (FILE *st, DEVICE *dptr, t_addr addr); static t_addr parse_addr (DEVICE *dptr, CONST char *cptr, CONST char **tptr); -static t_stat hp_cold_cmd (int32 arg, CONST char *buf); static t_stat hp_exdep_cmd (int32 arg, CONST char *buf); static t_stat hp_run_cmd (int32 arg, CONST char *buf); static t_stat hp_brk_cmd (int32 arg, CONST char *buf); @@ -888,7 +894,9 @@ const char *sim_stop_messages [] = { /* an array of pointers to the s "Breakpoint", /* STOP_BRKPNT */ "Infinite loop", /* STOP_INFLOOP */ "Cold load complete", /* STOP_CLOAD */ - "Cold dump complete" /* STOP_CDUMP */ + "Cold dump complete", /* STOP_CDUMP */ + "Auto-restart disabled", /* STOP_ARSINH */ + "Power is off" /* STOP_POWER */ }; @@ -936,12 +944,19 @@ static CTAB aux_cmds [] = { { "IEXAMINE", &hp_exdep_cmd, 0, NULL }, { "DEPOSIT", &hp_exdep_cmd, 0, NULL }, { "IDEPOSIT", &hp_exdep_cmd, 0, NULL }, + { "RUN", &hp_run_cmd, 0, NULL }, { "GO", &hp_run_cmd, 0, NULL }, + { "BREAK", &hp_brk_cmd, 0, NULL }, { "NOBREAK", &hp_brk_cmd, 0, NULL }, - { "LOAD", &hp_cold_cmd, Cold_Load, "l{oad} {cntlword} cold load from a device\n" }, - { "DUMP", &hp_cold_cmd, Cold_Dump, "du{mp} {cntlword} cold dump to a device\n" }, + + { "LOAD", &cpu_cold_cmd, Cold_Load, "l{oad} {cntlword} cold load from a device\n" }, + { "DUMP", &cpu_cold_cmd, Cold_Dump, "du{mp} {cntlword} cold dump to a device\n" }, + + { "POWER", &cpu_power_cmd, 0, "p{ower} f{ail} fail the CPU power\n" + "p{ower} r{estore} restore the CPU power\n" }, + { NULL } }; @@ -1675,8 +1690,26 @@ return formatted; /* return a pointer to t Implementation notes: - 1. The longest string to be returned is a five-character escaped octal - string, consisting of a backslash, three digits, and a trailing NUL. + 1. The longest string to be returned is a five-character escaped string + consisting of a backslash, three octal digits, and a trailing NUL. The + end-of-buffer pointer has an allowance to ensure that the string will + fit. + + 2. The routine returns a pointer to a static buffer containing the printable + string. To allow the routine to be called more than once per trace line, + the null-terminated format strings are concatenated in the buffer, and + each call returns a pointer that is offset into the buffer to point at + the latest formatted string. + + 3. There is no explicit buffer-free action. Instead, newly formatted + strings are appended to the buffer until there is no more space + available. At that point, the pointers are reset to the start of the + buffer. In effect, this provides a circular buffer, as previously + formatted strings are overwritten by subsequent calls. + + 4. The buffer is sized to hold the maximum number of concurrent strings + needed for a single trace line. If more concurrent strings are used, one + or more strings from the earliest calls will be overwritten. */ const char *fmt_char (uint32 charval) @@ -1687,7 +1720,10 @@ static const char *const control [] = { "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; -static char printable [5]; +static char fmt_buffer [64]; /* the return buffer */ +static char *freeptr = fmt_buffer; /* pointer to the first free character in the buffer */ +static char *endptr = fmt_buffer + sizeof fmt_buffer - 5; /* pointer to the end of the buffer (less allowance) */ +const char *fmtptr; if (charval <= '\037') /* if the value is an ASCII control character */ return control [charval]; /* then return a readable representation */ @@ -1695,17 +1731,26 @@ if (charval <= '\037') /* if the value is an AS else if (charval == '\177') /* otherwise if the value is the delete character */ return "DEL"; /* then return a readable representation */ -else if (charval > '\177') { /* otherwise if the value is beyond the printable range */ - sprintf (printable, "\\%03o", charval & D8_MASK); /* then format the value */ - return printable; /* as an escaped octal code */ - } +else { + if (freeptr > endptr) /* if there is not enough room left to append the string */ + freeptr = fmt_buffer; /* then reset to point at the start of the buffer */ -else { /* otherwise it's a printable character */ - printable [0] = '\''; /* so form a representation */ - printable [1] = (char) charval; /* containing the character */ - printable [2] = '\''; /* surrounded by single quotes */ - printable [3] = '\0'; - return printable; + fmtptr = freeptr; /* initialize the return pointer */ + *freeptr = '\0'; /* and the format accumulator */ + + if (charval > '\177') /* otherwise if the value is beyond the printable range */ + freeptr = freeptr + sprintf (freeptr, "\\%03o", /* then format the value */ + charval & D8_MASK); /* and update the free pointer */ + + else { /* otherwise it's a printable character */ + *freeptr++ = '\''; /* so form a representation */ + *freeptr++ = (char) charval; /* containing the character */ + *freeptr++ = '\''; /* surrounded by single quotes */ + *freeptr = '\0'; + } + + freeptr = freeptr + 1; /* advance past the NUL terminator */ + return fmtptr; /* and return the formatted string */ } } @@ -1798,30 +1843,43 @@ else { /* otherwise it's a prin Implementation notes: 1. The routine returns a pointer to a static buffer containing the printable - string, so it cannot be called more than once per trace line, unless the - buffer contents are copied upon return. In particular, this type of - calling sequence: + string. To allow the routine to be called more than once per trace line, + the null-terminated format strings are concatenated in the buffer, and + each call returns a pointer that is offset into the buffer to point at + the latest formatted string. - dprintf (..., fmt_bitset (...), ..., fmt_bitset (...), ...); + 2. There is no explicit buffer-free action. Instead, newly formatted + strings are appended to the buffer until there is no more space + available. At that point, the string currently being assembled is moved + to the start of the buffer, and the pointers are reset. In effect, this + provides a circular buffer, as previously formatted strings are + overwritten by subsequent calls. - ...will fail, as the buffer will be overwritten by the second call before - the result of the first call is printed. + 3. The buffer is sized to hold the maximum number of concurrent strings + needed for a single trace line. If more concurrent strings are used, one + or more strings from the earliest calls will be overwritten. If an + attempt is made to format a string larger than the buffer, an error + indication string is returned. + 4. The location of the end of the buffer used to determine if the next name + will fit includes an allowance for two separators that might be placed on + either side of the name and a terminating NUL character. */ const char *fmt_bitset (uint32 bitset, const BITSET_FORMAT bitfmt) { -static char formatted_set [1024]; /* the return buffer */ - -const char *bnptr; +static const char separator [] = " | "; /* the separator to use between names */ +static char fmt_buffer [1024]; /* the return buffer */ +static char *freeptr = fmt_buffer; /* pointer to the first free character in the buffer */ +static char *endptr = fmt_buffer + sizeof fmt_buffer /* pointer to the end of the buffer */ + - 2 * (sizeof separator - 1) - 1; /* less allowance for two separators and a terminator */ +const char *bnptr, *fmtptr; uint32 test_bit, index, bitmask; -char *fsptr = formatted_set; +size_t name_length; -*fsptr = '\0'; /* initialize the format accumulator */ -index = 0; /* and the name index */ +if (bitfmt.name_count < D32_WIDTH) /* if the name count is the less than the mask width */ + bitmask = (1 << bitfmt.name_count) - 1; /* then create a mask for the name count specified */ -if (bitfmt.name_count < D32_WIDTH) /* if the bit count is the less than the mask variable width */ - bitmask = (1 << bitfmt.name_count) - 1; /* then create a mask for the bit count specified */ else /* otherwise use a predefined value for the mask */ bitmask = D32_MASK; /* to prevent shifting the bit off the MSB end */ @@ -1834,8 +1892,13 @@ else /* otherwise */ test_bit = 1 << bitfmt.offset; /* create a test bit for the LSB */ -while ((bitfmt.alternate || bitset) && index < bitfmt.name_count) { /* while more bits and more names exist */ - bnptr = bitfmt.names [index]; /* point at the name for the current bit */ +fmtptr = freeptr; /* initialize the return pointer */ +*freeptr = '\0'; /* and the format accumulator */ +index = 0; /* and the name index */ + +while ((bitfmt.alternate || bitset) /* while more bits */ + && index < bitfmt.name_count) { /* and more names exist */ + bnptr = bitfmt.names [index]; /* point at the name for the current bit */ if (bnptr) /* if the name is defined */ if (*bnptr == '\1') /* then if this name has an alternate */ @@ -1849,10 +1912,25 @@ while ((bitfmt.alternate || bitset) && index < bitfmt.name_count) { /* while mor bnptr = NULL; /* then clear the name pointer */ if (bnptr) { /* if the name pointer is set */ - if (formatted_set [0] != '\0') /* then if it is not the first one added */ - fsptr = strcat (fsptr, " | "); /* then add a separator to the string */ + name_length = strlen (bnptr); /* then get the length needed */ - strcat (fsptr, bnptr); /* append the bit's mnemonic to the accumulator */ + if (freeptr + name_length > endptr) { /* if there is not enough room left to append the name */ + strcpy (fmt_buffer, fmtptr); /* then move the partial string to the start of the buffer */ + + freeptr = fmt_buffer + (freeptr - fmtptr); /* point at the new first free character location */ + fmtptr = fmt_buffer; /* and reset the return pointer */ + + if (freeptr + name_length > endptr) /* if there is still not enough room left to append */ + return "(buffer overflow)"; /* then this call is requires a larger buffer! */ + } + + if (*fmtptr != '\0') { /* if this is not the first name added */ + strcpy (freeptr, separator); /* then add a separator to the string */ + freeptr = freeptr + strlen (separator); /* and move the free pointer */ + } + + strcpy (freeptr, bnptr); /* append the bit's mnemonic to the accumulator */ + freeptr = freeptr + name_length; /* and move the free pointer */ } if (bitfmt.direction == msb_first) /* if formatting is left-to-right */ @@ -1864,16 +1942,21 @@ while ((bitfmt.alternate || bitset) && index < bitfmt.name_count) { /* while mor } -if (formatted_set [0] == '\0') /* if the set is empty */ +if (*fmtptr == '\0') /* if no names were output */ if (bitfmt.bar == append_bar) /* then if concatenating with more information */ return ""; /* then return an empty string */ else /* otherwise it's a standalone format */ return "(none)"; /* so return a placeholder */ -else if (bitfmt.bar == append_bar) /* otherwise if a trailing separator is specified */ - fsptr = strcat (fsptr, " | "); /* then add it to the string */ +else if (bitfmt.bar == append_bar) { /* otherwise if a trailing separator is specified */ + strcpy (freeptr, separator); /* then add a separator to the string */ + freeptr = freeptr + strlen (separator) + 1; /* and account for it plus the trailing NUL */ + } -return formatted_set; /* return a pointer to the accumulator */ +else /* otherwise */ + freeptr = freeptr + 1; /* just account for the trailing NUL */ + +return fmtptr; /* return a pointer to the formatted string */ } @@ -1911,8 +1994,8 @@ return formatted_set; /* return a pointer to t call parameters. */ -#define FLAG_SIZE 50 /* sufficiently large to accommodate all flag names */ -#define FORMAT_SIZE 1000 /* sufficiently large to accommodate all format strings */ +#define FLAG_SIZE 32 /* sufficiently large to accommodate all flag names */ +#define FORMAT_SIZE 1024 /* sufficiently large to accommodate all format strings */ void hp_debug (DEVICE *dptr, uint32 flag, ...) { @@ -1973,7 +2056,9 @@ return; static void one_time_init (void) { -CTAB *systab, *auxtab = aux_cmds; +CTAB *contab, *systab, *auxtab = aux_cmds; + +contab = find_cmd ("CONT"); /* find the entry for the CONTINUE command */ while (auxtab->name != NULL) { /* loop through the auxiliary command table */ systab = find_cmd (auxtab->name); /* find the corresponding system command table entry */ @@ -1992,6 +2077,10 @@ while (auxtab->name != NULL) { /* loop through the auxi auxtab->message = systab->message; /* as we never override them */ } + if (auxtab->action == &cpu_cold_cmd /* if this is the LOAD or DUMP command entry */ + || auxtab->action == &cpu_power_cmd) /* or the POWER command entry */ + auxtab->message = contab->message; /* then set the execution completion message routine */ + auxtab++; /* point at the next table entry */ } @@ -2061,7 +2150,8 @@ else if (reason == STOP_CDUMP) { /* otherwise if this is fprint_val (st, CIR, cpu_dev.dradix, /* and the numeric value */ cpu_dev.dwidth, PV_RZRO); - return FALSE; /* return FALSE to omit the program counter */ + fputc ('\n', st); /* append an end-of-line character */ + return FALSE; /* and return FALSE to omit the program counter */ } else if (reason == STOP_SYSHALT) { /* otherwise if this is a system halt stop */ @@ -2197,62 +2287,6 @@ return address; /* return the linear add } -/* Execute the LOAD and DUMP commands. - - This routine implements the cold load and cold dump commands. The syntax is: - - LOAD { } - DUMP { } - - The is a number that is deposited into the SWCH register - before invoking the CPU cold load or cold dump facility. The CPU radix is - used to interpret the number; it defaults to octal. If the number is - omitted, the SWCH register value is not altered before loading or dumping. - - On entry, the "arg" parameter is "Cold_Load" for a LOAD command and - "Cold_Dump" for a DUMP command, and "buf" points at the remainder of the - command line. If characters exist on the command line, they are parsed, - converted to a numeric value, and stored in the SWCH register. Then the - CPU's cold load/dump routine is called to set up the CPU state. Finally, the - CPU is started to begin the requested action. - - - Implementation notes: - - 1. The run command invocation prepares the simulator for execution, which - includes a CPU and I/O reset. However, the cpu_reset routine does not - reset the CPU state if the cold dump switch is set. This allows the - value of the CPX2 register to be saved as part of the dump. -*/ - -static t_stat hp_cold_cmd (int32 arg, CONST char *buf) -{ -const char *cptr; -char gbuf [CBUFSIZE]; -t_stat status; -HP_WORD value; - -if (*buf != '\0') { /* if more characters exist on the command line */ - cptr = get_glyph (buf, gbuf, 0); /* then get the next glyph */ - - if (*cptr != '\0') /* if that does not exhaust the input */ - return SCPE_2MARG; /* then report that there are too many arguments */ - - value = (HP_WORD) get_uint (gbuf, cpu_dev.dradix, /* get the parameter value */ - D16_UMAX, &status); - - if (status == SCPE_OK) /* if a valid number was present */ - SWCH = value; /* then set it into the switch register */ - else /* otherwise */ - return status; /* return the error status */ - } - -cpu_front_panel (SWCH, (PANEL_TYPE) arg); /* set up the cold load or dump microcode */ - -return run_cmd (RU_RUN, ""); /* reset and execute the halt-mode routine */ -} - - /* Execute the EXAMINE, DEPOSIT, IEXAMINE, and IDEPOSIT commands. These commands are intercepted to configure address parsing. The following @@ -2438,8 +2472,8 @@ return; Implementation notes: - 1. The Return Residue value is printed as a positive number, even though - the value in memory is either negative or zero. + 1. The Return Residue and Read/Write count values are printed as positive + numbers, even though the values in memory are negative. */ static const char *const order_names [] = { /* indexed by SIO_ORDER */ @@ -2479,7 +2513,7 @@ switch (order) { /* dispatch operand prin break; case sioRTRES: /* print the residue count */ - fprint_value (ofile, - SEXT (ioaw), + fprint_value (ofile, NEG16 (ioaw), (radix ? radix : 10), DV_WIDTH, PV_LEFT); break; @@ -2516,7 +2550,7 @@ switch (order) { /* dispatch operand prin case sioWRITEC: case sioREAD: case sioREADC: /* print the count and address */ - fprint_value (ofile, - IOCW_COUNT (iocw), + fprint_value (ofile, NEG16 (IOCW_COUNT (iocw)), (radix ? radix : 10), DV_WIDTH, PV_LEFT); fputc (',', ofile); diff --git a/HP3000/hp_disclib.c b/HP3000/hp_disclib.c index db240bdf..75f70ad2 100644 --- a/HP3000/hp_disclib.c +++ b/HP3000/hp_disclib.c @@ -24,6 +24,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. + 03-Aug-16 JDB "fmt_bitset" now allows multiple concurrent calls 09-Jun-16 JDB Added casts for ptrdiff_t to int32 values 08-Jun-16 JDB Corrected %d format to %u for unsigned values 16-May-16 JDB DRIVE_PROPS.name is now a pointer-to-constant @@ -1540,7 +1541,6 @@ uint32 unit; int32 seek_wait_time; PRPTR props; CNTLR_IFN_IBUS outbound; -char s1_buffer [256], s2_buffer [256]; /* formatted bitset buffers for trace logging */ DIAG_ENTRY *dop = NULL; wait_timer (cvptr, CLEAR); /* stop the command wait timer */ @@ -1694,11 +1694,11 @@ else { /* otherwise the command dpprintf (cvptr->device, DL_DEB_CMD, "Unit %u %s returns %sunit %u | %s and %s%s | %s\n", unit, opcode_name [Request_Status], - strcpy (s1_buffer, fmt_bitset (cvptr->spd_unit, status_1_format)), + fmt_bitset (cvptr->spd_unit, status_1_format), CM_UNIT (cvptr->spd_unit), dl_status_name (cvptr->status), (cvptr->buffer [1] & S2_ERROR ? "error | " : ""), drive_props [S2_TO_DRIVE_TYPE (cvptr->buffer [1])].name, - strcpy (s2_buffer, fmt_bitset (cvptr->buffer [1], status_2_format))); + fmt_bitset (cvptr->buffer [1], status_2_format)); if (rptr) /* if the referenced unit is valid */ rptr->STATUS &= ~S2_FIRST_STATUS; /* then clear the First Status bit */ diff --git a/doc/hp3000_doc.doc b/doc/hp3000_doc.doc index 832caae497cedc0f4137f7b5edd21ad85bfc3b83..040f8714093e1aa3395d73de12fd444ad3dcbb1d 100644 GIT binary patch delta 74211 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
D9 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 delta 68098 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