diff --git a/HP2100/hp2100_baci.c b/HP2100/hp2100_baci.c index 6b844f4b..631c3554 100644 --- a/HP2100/hp2100_baci.c +++ b/HP2100/hp2100_baci.c @@ -1,6 +1,6 @@ /* hp2100_baci.c: HP 12966A Buffered Asynchronous Communications Interface simulator - Copyright (c) 2007-2017, J. David Bryan + Copyright (c) 2007-2019, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,8 @@ BACI 12966A Buffered Asynchronous Communications Interface + 23-Jan-19 JDB Removed DEV_MUX to avoid TMXR debug flags + 10-Jul-18 JDB Revised I/O model 01-Nov-17 JDB Fixed serial output buffer overflow handling 15-Mar-17 JDB Trace flags are now global Changed DEBUG_PRI calls to tprintfs @@ -164,6 +166,8 @@ #include #include "hp2100_defs.h" +#include "hp2100_io.h" + #include "sim_tmxr.h" @@ -322,43 +326,52 @@ #define baci_poll baci_unit[1] /* line polling unit */ +/* Interface state */ + +typedef struct { + FLIP_FLOP control; /* control flip-flop */ + FLIP_FLOP flag; /* flag flip-flop */ + FLIP_FLOP flag_buffer; /* flag buffer flip-flop */ + FLIP_FLOP srq; /* SRQ flip-flop */ + FLIP_FLOP lockout; /* interrupt lockout flip-flop */ + } CARD_STATE; + +static CARD_STATE baci; /* per-card state */ + + /* BACI state variables */ -struct { - FLIP_FLOP control; /* control flip-flop */ - FLIP_FLOP flag; /* flag flip-flop */ - FLIP_FLOP flagbuf; /* flag buffer flip-flop */ - FLIP_FLOP srq; /* SRQ flip-flop */ - FLIP_FLOP lockout; /* interrupt lockout flip-flop */ - } baci = { CLEAR, CLEAR, CLEAR, CLEAR, CLEAR }; +static uint16 baci_ibuf = 0; /* status/data in */ +static uint16 baci_obuf = 0; /* command/data out */ +static uint16 baci_status = 0; /* current status */ -uint16 baci_ibuf = 0; /* status/data in */ -uint16 baci_obuf = 0; /* command/data out */ -uint16 baci_status = 0; /* current status */ +static uint16 baci_edsiw = 0; /* enable device status word */ +static uint16 baci_dsrw = 0; /* device status reference word */ +static uint16 baci_cfcw = 0; /* character frame control word */ +static uint16 baci_icw = 0; /* interface control word */ +static uint16 baci_isrw = 0; /* interrupt status reset word */ -uint16 baci_edsiw = 0; /* enable device status word */ -uint16 baci_dsrw = 0; /* device status reference word */ -uint16 baci_cfcw = 0; /* character frame control word */ -uint16 baci_icw = 0; /* interface control word */ -uint16 baci_isrw = 0; /* interrupt status reset word */ +static uint32 baci_fput = 0; /* FIFO buffer add index */ +static uint32 baci_fget = 0; /* FIFO buffer remove index */ +static uint32 baci_fcount = 0; /* FIFO buffer counter */ +static uint32 baci_bcount = 0; /* break counter */ -uint32 baci_fput = 0; /* FIFO buffer add index */ -uint32 baci_fget = 0; /* FIFO buffer remove index */ -uint32 baci_fcount = 0; /* FIFO buffer counter */ -uint32 baci_bcount = 0; /* break counter */ +static uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */ +static uint8 baci_spchar [256]; /* special character RAM */ -uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */ -uint8 baci_spchar [256]; /* special character RAM */ +static uint16 baci_uart_thr = CLEAR_HR; /* UART transmitter holding register */ +static uint16 baci_uart_rhr = CLEAR_HR; /* UART receiver holding register */ +static int32 baci_uart_tr = CLEAR_R; /* UART transmitter register */ +static int32 baci_uart_rr = CLEAR_R; /* UART receiver register */ +static uint32 baci_uart_clk = 0; /* UART transmit/receive clock */ -uint16 baci_uart_thr = CLEAR_HR; /* UART transmitter holding register */ -uint16 baci_uart_rhr = CLEAR_HR; /* UART receiver holding register */ - int32 baci_uart_tr = CLEAR_R; /* UART transmitter register */ - int32 baci_uart_rr = CLEAR_R; /* UART receiver register */ -uint32 baci_uart_clk = 0; /* UART transmit/receive clock */ +static t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ +static uint32 baci_enq_cntr = 0; /* ENQ seen counter */ -t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ -uint32 baci_enq_cntr = 0; /* ENQ seen counter */ +/* BACI local SCP support routines */ + +static INTERFACE baci_interface; /* BACI local routines */ @@ -370,27 +383,35 @@ static uint16 fifo_get (void); static void fifo_put (uint8 ch); static void clock_uart (void); -/* BACI global routines */ +/* BACI local SCP support routines */ -IOHANDLER baci_io; - -t_stat baci_term_svc (UNIT *uptr); -t_stat baci_poll_svc (UNIT *uptr); -t_stat baci_reset (DEVICE *dptr); -t_stat baci_attach (UNIT *uptr, CONST char *cptr); -t_stat baci_detach (UNIT *uptr); +static t_stat baci_term_svc (UNIT *uptr); +static t_stat baci_poll_svc (UNIT *uptr); +static t_stat baci_reset (DEVICE *dptr); +static t_stat baci_attach (UNIT *uptr, CONST char *cptr); +static t_stat baci_detach (UNIT *uptr); -/* BACI data structures +/* BACI SCP data structures */ - baci_ldsc BACI terminal multiplexer line descriptor - baci_desc BACI terminal multiplexer device descriptor - baci_dib BACI device information block - baci_unit BACI unit list - baci_reg BACI register list - baci_mod BACI modifier list - baci_deb BACI debug list - baci_dev BACI device descriptor + +/* Terminal multiplexer library descriptors */ + +static TMLN baci_ldsc [] = { /* line descriptors */ + { 0 } + }; + +static TMXR baci_desc = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + baci_ldsc, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ + }; + + +/* Unit list. Two units are used: one to handle character I/O via the multiplexer library, and another to poll for connections and input. The character I/O service @@ -403,19 +424,26 @@ t_stat baci_detach (UNIT *uptr); ten millisecond period. */ -DEVICE baci_dev; - -TMLN baci_ldsc = { 0 }; /* line descriptor */ -TMXR baci_desc = { 1, 0, 0, &baci_ldsc, NULL, &baci_dev }; /* device descriptor */ - -DIB baci_dib = { &baci_io, BACI, 0 }; - -UNIT baci_unit[] = { +static UNIT baci_unit[] = { { UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */ { UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* line poll unit */ }; -REG baci_reg [] = { + +/* Device information block */ + +static DIB baci_dib = { + &baci_interface, /* the device's I/O interface function pointer */ + BACI, /* the device's select code (02-77) */ + 0, /* the card index */ + "12966A Buffered Asynchronous Communications Interface", /* the card description */ + NULL /* the ROM description */ + }; + + +/* Register list */ + +static REG baci_reg [] = { /* Macro Name Location Radix Width Offset Depth Flags */ /* ------ -------- -------------------- ----- ----- ------ ---------- --------------- */ { ORDATA (IBUF, baci_ibuf, 16), REG_FIT | REG_X }, @@ -450,36 +478,48 @@ REG baci_reg [] = { { FLDATA (LKO, baci.lockout, 0) }, { FLDATA (CTL, baci.control, 0) }, { FLDATA (FLG, baci.flag, 0) }, - { FLDATA (FBF, baci.flagbuf, 0) }, + { FLDATA (FBF, baci.flag_buffer, 0) }, { FLDATA (SRQ, baci.srq, 0) }, - { ORDATA (SC, baci_dib.select_code, 6), REG_HRO }, - { ORDATA (DEVNO, baci_dib.select_code, 6), REG_HRO }, + + DIB_REGS (baci_dib), + { NULL } }; -MTAB baci_mod[] = { - { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", NULL, NULL, NULL }, - { UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL }, - { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, - { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, +/* Modifier list */ - { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, - { UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, +static MTAB baci_mod[] = { +/* Mask Value Match Value Print String Match String Validation Display Descriptor */ +/* ------------- ------------- ------------------ ------------ ----------- ------- ---------- */ + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", NULL, NULL, NULL }, + { UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL }, - { MTAB_XDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &baci_desc }, - { MTAB_XDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &baci_desc }, + { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, + { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, - { MTAB_XDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &tmxr_show_cstat, &baci_desc }, - { MTAB_XDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &baci_desc }, - { MTAB_XDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc }, + { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, + { UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, + +/* Entry Flags Value Print String Match String Validation Display Descriptor */ +/* ------------------- ----- ------------ ------------ --------------- ---------------- ------------------- */ + { MTAB_XDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, (void *) &baci_desc }, + { MTAB_XDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, (void *) &baci_desc }, + + { MTAB_XDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &tmxr_show_cstat, (void *) &baci_desc }, + { MTAB_XDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &baci_desc }, + { MTAB_XDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &baci_desc }, + + { MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &baci_dib }, + { MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &baci_dib }, - { MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &baci_dib }, - { MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &baci_dib }, { 0 } }; -DEBTAB baci_deb [] = { + +/* Debugging trace list */ + +static DEBTAB baci_deb [] = { { "CMDS", DEB_CMDS }, { "CPU", DEB_CPU }, { "BUF", DEB_BUF }, @@ -488,36 +528,39 @@ DEBTAB baci_deb [] = { { NULL, 0 } }; + +/* Device descriptor */ + DEVICE baci_dev = { - "BACI", /* device name */ - baci_unit, /* unit array */ - baci_reg, /* register array */ - baci_mod, /* modifier array */ - 2, /* number of units */ - 10, /* address radix */ - 31, /* address width */ - 1, /* address increment */ - 8, /* data radix */ - 8, /* data width */ - &tmxr_ex, /* examine routine */ - &tmxr_dep, /* deposit routine */ - &baci_reset, /* reset routine */ - NULL, /* boot routine */ - &baci_attach, /* attach routine */ - &baci_detach, /* detach routine */ - &baci_dib, /* device information block */ - DEV_DEBUG | DEV_DISABLE | DEV_MUX, /* device flags */ - 0, /* debug control flags */ - baci_deb, /* debug flag name table */ - NULL, /* memory size change routine */ - NULL, /* logical device name */ - NULL, /* help routine */ - NULL, /* help attach routine*/ - (void *) &baci_desc /* help context */ + "BACI", /* device name */ + baci_unit, /* unit array */ + baci_reg, /* register array */ + baci_mod, /* modifier array */ + 2, /* number of units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + &tmxr_ex, /* examine routine */ + &tmxr_dep, /* deposit routine */ + &baci_reset, /* reset routine */ + NULL, /* boot routine */ + &baci_attach, /* attach routine */ + &baci_detach, /* detach routine */ + &baci_dib, /* device information block */ + DEV_DISABLE | DEV_DEBUG, /* device flags */ + 0, /* debug control flags */ + baci_deb, /* debug flag name table */ + NULL, /* memory size change routine */ + NULL, /* logical device name */ + NULL, /* help routine */ + NULL, /* help attach routine*/ + (void *) &baci_desc /* help context */ }; -/* I/O signal handler. +/* BACI interface. The BACI processes seven types of output words and supplies two types of input words. Output word type is identified by an ID code in bits 14-12. @@ -534,31 +577,39 @@ DEVICE baci_dev = { interrupts until the cause of the first interrupt is identified and cleared by the CPU. + Implementation notes: 1. The STC handler checks to see if it was invoked for STC SC or STC SC,C. In the latter case, the check for new interrupt requests is deferred until after the CLF. Otherwise, the flag set by the interrupt check would be cleared, and the interrupt would be lost. + + 2. POPIO and CRS are ORed together on the interface card. In simulation, we + skip processing for POPIO because CRS is always asserted with POPIO + (though the reverse is not true), and we don't need to call master_reset + twice in succession. */ -uint32 baci_io (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) +static SIGNALS_VALUE baci_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) { -const char *hold_or_clear = (signal_set & ioCLF ? ",C" : ""); -uint8 ch; -uint16 data; -uint32 mask; -IOSIGNAL signal; -IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ +const char * const hold_or_clear = (inbound_signals & ioCLF ? ",C" : ""); +uint8 ch; +uint32 mask; +INBOUND_SIGNAL signal; +INBOUND_SET working_set = inbound_signals; +SIGNALS_VALUE outbound = { ioNONE, 0 }; +t_bool irq_enabled = FALSE; -while (working_set) { - signal = IONEXT (working_set); /* isolate next signal */ +while (working_set) { /* while signals remain */ + signal = IONEXTSIG (working_set); /* isolate the next signal */ - switch (signal) { /* dispatch I/O signal */ + switch (signal) { /* dispatch the I/O signal */ - case ioCLF: /* clear flag flip-flop */ - baci.flag = baci.flagbuf = CLEAR; /* clear flag and flag buffer */ - baci.srq = CLEAR; /* clear SRQ */ + case ioCLF: /* Clear Flag flip-flop */ + baci.flag_buffer = CLEAR; /* reset the flag buffer */ + baci.flag = CLEAR; /* and flag flip-flops */ + baci.srq = CLEAR; /* clear SRQ */ tprintf (baci_dev, DEB_CMDS, "[CLF] Flag and SRQ cleared\n"); @@ -566,8 +617,9 @@ while (working_set) { break; - case ioSTF: /* set flag flip-flop */ - baci.flag = baci.flagbuf = SET; /* set flag and flag buffer */ + case ioSTF: /* Set Flag flip-flop */ + baci.flag_buffer = SET; /* set the flag buffer flip-flop */ + baci.lockout = SET; /* set lockout */ baci.srq = SET; /* set SRQ */ @@ -575,19 +627,23 @@ while (working_set) { break; - case ioENF: /* enable flag */ - baci.flag = baci.flagbuf = SET; /* set device flag and flag buffer */ + case ioENF: /* Enable Flag */ + if (baci.flag_buffer == SET) /* if the flag buffer flip-flop is set */ + baci.flag = SET; /* then set the flag flip-flop */ + baci.lockout = SET; /* set lockout */ break; - case ioSFC: /* skip if flag is clear */ - setstdSKF (baci); + case ioSFC: /* Skip if Flag is Clear */ + if (baci.flag == CLEAR) /* if the flag flip-flop is clear */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ break; - case ioSFS: /* skip if flag is set */ - setstdSKF (baci); + case ioSFS: /* Skip if Flag is Set */ + if (baci.flag == SET) /* if the flag flip-flop is set */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ break; @@ -598,32 +654,30 @@ while (working_set) { if (IO_MODE == RECV) /* receiving? */ baci_ibuf = baci_ibuf | fifo_get (); /* add char and validity flag */ - data = baci_ibuf; /* return received data */ + outbound.value = baci_ibuf; /* return received data */ tprintf (baci_dev, DEB_CPU, "[LIx%s] Received data = %06o\n", hold_or_clear, baci_ibuf); } else { /* control clear? */ - data = baci_status; /* return status */ + outbound.value = baci_status; /* return status */ tprintf (baci_dev, DEB_CPU, "[LIx%s] Status = %06o\n", hold_or_clear, baci_status); } - - stat_data = IORETURN (SCPE_OK, data); /* merge in return status */ break; - case ioIOO: /* I/O data output */ - baci_obuf = IODATA (stat_data); /* get data value */ + case ioIOO: /* I/O data output */ + baci_obuf = (uint16) inbound_value; /* get data value */ tprintf (baci_dev, DEB_CPU, "[OTx%s] Command = %06o\n", hold_or_clear, baci_obuf); - if (baci_obuf & OUT_MR) { /* master reset? */ - master_reset (); /* do before processing */ - baci_io (&baci_dib, ioSIR, 0); /* set interrupt request */ + if (baci_obuf & OUT_MR) { /* master reset? */ + master_reset (); /* do before processing */ + working_set |= ioSIR; /* assert the Set Interrupt Request backplane signal */ tprintf (baci_dev, DEB_CMDS, "[OTx%s] Master reset\n", hold_or_clear); } @@ -671,13 +725,13 @@ while (working_set) { baci_cfcw = baci_obuf; /* load new frame word */ break; - case 4: /* interface control */ + case 4: /* interface control */ if ((baci_icw ^ baci_obuf) & OUT_BAUDRATE) { /* baud rate change? */ baci_term.wait = service_time (baci_obuf); /* set service time to match rate */ - if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */ - if (baci_obuf & OUT_BAUDRATE) { /* internal baud rate requested? */ - sim_activate (&baci_term, /* activate I/O service */ + if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */ + if (baci_obuf & OUT_BAUDRATE) { /* internal baud rate requested? */ + sim_activate (&baci_term, /* activate I/O service */ baci_term.wait); tprintf (baci_dev, DEB_CMDS, "[OTx%s] Terminal service scheduled, " @@ -716,51 +770,77 @@ while (working_set) { break; - case ioCRS: /* control reset */ + case ioPOPIO: /* Power-On Preset to I/O */ + break; /* POPIO and CRS are ORed on the interface */ + + + case ioCRS: /* Control Reset */ master_reset (); /* issue master reset */ tprintf (baci_dev, DEB_CMDS, "[CRS] Master reset\n"); break; - case ioCLC: /* clear control flip-flop */ - baci.control = CLEAR; /* clear control */ + case ioCLC: /* Clear Control flip-flop */ + baci.control = CLEAR; /* clear the control flip-flop */ tprintf (baci_dev, DEB_CMDS, "[CLC%s] Control cleared\n", hold_or_clear); break; - case ioSTC: /* set control flip-flop */ - baci.control = SET; /* set control */ - baci.lockout = CLEAR; /* clear lockout */ + case ioSTC: /* Set Control flip-flop */ + baci.control = SET; /* set the control flip-flop */ + baci.lockout = CLEAR; /* and clear lockout */ tprintf (baci_dev, DEB_CMDS, "[STC%s] Control set and lockout cleared\n", hold_or_clear); - if (!(signal_set & ioCLF)) /* STC without ,C ? */ + if (!(inbound_signals & ioCLF)) /* STC without ,C ? */ update_status (); /* clearing lockout might interrupt */ break; - case ioSIR: /* set interrupt request */ - setstdPRL (baci); /* set standard PRL signal */ - setstdIRQ (baci); /* set standard IRQ signal */ - setSRQ (dibptr->select_code, baci.srq); /* set SRQ signal */ + case ioSIR: /* Set Interrupt Request */ + if (baci.control & baci.flag) /* if the control and flag flip-flops are set */ + outbound.signals |= cnVALID; /* then deny PRL */ + else /* otherwise */ + outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */ + + if (baci.control & baci.flag & baci.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */ + outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */ + + if (baci.srq == SET) /* if the SRQ flip-flop is set */ + outbound.signals |= ioSRQ; /* then assert SRQ */ break; - case ioIAK: /* interrupt acknowledge */ - baci.flagbuf = CLEAR; + case ioIAK: /* Interrupt Acknowledge */ + baci.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */ break; - default: /* all other signals */ - break; /* are ignored */ + case ioIEN: /* Interrupt Enable */ + irq_enabled = TRUE; /* permit IRQ to be asserted */ + break; + + + case ioPRH: /* Priority High */ + if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */ + outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */ + + if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */ + outbound.signals |= ioPRL; /* then assert it unconditionally */ + break; + + + case ioEDT: /* not used by this interface */ + case ioPON: /* not used by this interface */ + break; } - working_set = working_set & ~signal; /* remove current signal from set */ - } + IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ + } /* and continue until all signals are processed */ -return stat_data; +return outbound; /* return the outbound signals and value */ } @@ -869,19 +949,19 @@ return stat_data; This permits the simulation to continue while ignoring the output. */ -t_stat baci_term_svc (UNIT *uptr) +static t_stat baci_term_svc (UNIT *uptr) { uint32 data_bits, data_mask; const t_bool fast_timing = (baci_term.flags & UNIT_FASTTIME) != 0; const t_bool is_attached = (baci_term.flags & UNIT_ATT) != 0; t_stat status = SCPE_OK; t_bool recv_loop = TRUE; -t_bool xmit_loop = (baci_ldsc.xmte != 0); /* TRUE if the transmit buffer is not full */ +t_bool xmit_loop = (baci_ldsc [0].xmte != 0); /* TRUE if the transmit buffer is not full */ /* Transmission */ -if (baci_ldsc.xmte == 0) /* if the transmit buffer is full */ +if (baci_ldsc [0].xmte == 0) /* if the transmit buffer is full */ tprintf (baci_dev, DEB_XFER, "Transmission stalled for full buffer\n"); @@ -903,7 +983,7 @@ while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UA baci_enq_cntr = 0; /* reset ENQ counter */ if (is_attached) { /* attached to network? */ - status = tmxr_putc_ln (&baci_ldsc, /* transmit the character */ + status = tmxr_putc_ln (baci_ldsc, /* transmit the character */ baci_uart_tr); if (status == SCPE_OK) /* transmitted OK? */ @@ -933,7 +1013,7 @@ while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UA baci_uart_thr = CLEAR_HR; /* clear holding register */ xmit_loop = (fast_timing && ! baci_enq_seen /* loop if fast mode and char not ENQ */ - && baci_ldsc.xmte != 0); /* and buffer space is available */ + && baci_ldsc [0].xmte != 0); /* and buffer space is available */ } else /* otherwise transmission failed */ @@ -961,7 +1041,7 @@ if (recv_loop && /* ok to process? */ /* Reception */ while (recv_loop) { /* OK to process? */ - baci_uart_rr = tmxr_getc_ln (&baci_ldsc); /* get a new character */ + baci_uart_rr = tmxr_getc_ln (baci_ldsc); /* get a new character */ if (baci_uart_rr == 0) /* if there are no more characters available */ break; /* then quit the reception loop */ @@ -984,14 +1064,14 @@ while (recv_loop) { /* OK to process? */ baci_uart_rhr = (uint16) toupper (baci_uart_rhr); /* convert to upper case if lower */ if (baci_cfcw & OUT_ECHO) /* echo wanted? */ - tmxr_putc_ln (&baci_ldsc, baci_uart_rhr); /* send it back */ + tmxr_putc_ln (baci_ldsc, baci_uart_rhr); /* send it back */ if ((IO_MODE == RECV) && !baci_enq_seen) { /* receive mode and not ENQ/ACK? */ fifo_put ((uint8) baci_uart_rhr); /* put data in FIFO */ baci_uart_rhr = CLEAR_HR; /* clear RHR */ update_status (); /* update FIFO status (may set flag) */ - recv_loop = fast_timing && !IRQ (baci_dib.select_code); /* loop if fast mode and no IRQ */ + recv_loop = fast_timing && baci.flag_buffer == CLEAR; /* loop if fast mode and no IRQ */ } else { /* xmit or ENQ/ACK, leave char in RHR */ @@ -1016,7 +1096,7 @@ if (is_attached) /* attached to network? tmxr_poll_tx (&baci_desc); /* output any accumulated chars */ if ((baci_uart_thr & IN_VALID) || baci_enq_seen || /* more to transmit? */ - tmxr_rqln (&baci_ldsc)) /* or more to receive? */ + tmxr_rqln (baci_ldsc)) /* or more to receive? */ sim_activate (uptr, uptr->wait); /* reschedule service */ else tprintf (baci_dev, DEB_CMDS, "Terminal service stopped\n"); @@ -1040,20 +1120,20 @@ return status; client would connect but then would be silently unresponsive. */ -t_stat baci_poll_svc (UNIT *uptr) +static t_stat baci_poll_svc (UNIT *uptr) { if (tmxr_poll_conn (&baci_desc) >= 0) /* if new connection is established */ - baci_ldsc.rcve = 1; /* then enable line to receive */ + baci_ldsc [0].rcve = 1; /* then enable line to receive */ tmxr_poll_rx (&baci_desc); /* poll for input */ -if (tmxr_rqln (&baci_ldsc)) /* chars available? */ +if (tmxr_rqln (baci_ldsc)) /* chars available? */ sim_activate (&baci_term, baci_term.wait); /* activate I/O service */ if (uptr->wait == POLL_FIRST) /* first poll? */ - uptr->wait = sync_poll (INITIAL); /* initial synchronization */ + uptr->wait = hp_sync_poll (INITIAL); /* initial synchronization */ else /* not first */ - uptr->wait = sync_poll (SERVICE); /* continue synchronization */ + uptr->wait = hp_sync_poll (SERVICE); /* continue synchronization */ sim_activate (uptr, uptr->wait); /* continue polling */ @@ -1063,9 +1143,9 @@ return SCPE_OK; /* Simulator reset routine */ -t_stat baci_reset (DEVICE *dptr) +static t_stat baci_reset (DEVICE *dptr) { -IOPRESET (&baci_dib); /* PRESET device (does not use PON) */ +io_assert (&baci_dev, ioa_POPIO); /* PRESET the device */ baci_ibuf = 0; /* clear input buffer */ baci_obuf = 0; /* clear output buffer */ @@ -1089,7 +1169,7 @@ return SCPE_OK; /* Attach line */ -t_stat baci_attach (UNIT *uptr, CONST char *cptr) +static t_stat baci_attach (UNIT *uptr, CONST char *cptr) { t_stat status = SCPE_OK; @@ -1105,11 +1185,11 @@ return status; /* Detach line */ -t_stat baci_detach (UNIT *uptr) +static t_stat baci_detach (UNIT *uptr) { t_stat status; -baci_ldsc.rcve = 0; /* disable line reception */ +baci_ldsc [0].rcve = 0; /* disable line reception */ sim_cancel (&baci_poll); /* stop line poll */ status = tmxr_detach (&baci_desc, uptr); /* detach socket */ return status; @@ -1149,7 +1229,7 @@ baci_uart_clk = 0; /* clear UART clock */ baci_bcount = 0; /* clear break counter */ baci.control = CLEAR; /* clear control */ -baci.flag = baci.flagbuf = SET; /* set flag and flag buffer */ +baci.flag = baci.flag_buffer = SET; /* set flag and flag buffer */ baci.srq = SET; /* set SRQ */ baci.lockout = SET; /* set lockout flip-flop */ @@ -1160,7 +1240,7 @@ baci_icw = baci_icw & OUT_BAUDRATE; /* clear interface contr if (baci_term.flags & UNIT_DIAG) { /* diagnostic mode? */ baci_status = baci_status & ~IN_MODEM | IN_SPARE; /* clear loopback status, set BA */ - baci_ldsc.xmte = 1; /* enable transmitter */ + baci_ldsc [0].xmte = 1; /* enable transmitter */ } return; @@ -1226,7 +1306,8 @@ if ((baci_status & IN_STDIRQ) || /* standard interrupt? * baci_status); else { - baci_io (&baci_dib, ioENF, 0); /* set flag */ + baci.flag_buffer = SET; /* set the flag buffer */ + io_assert (&baci_dev, ioa_ENF); /* and flag flip-flops */ tprintf (baci_dev, DEB_CMDS, "Flag and lockout set, status = %06o\n", baci_status); @@ -1241,8 +1322,8 @@ if ((baci_icw & OUT_DCPC) && /* DCPC enabled? */ baci_status); else { - baci.srq = SET; /* set SRQ */ - baci_io (&baci_dib, ioSIR, 0); /* set interrupt request */ + baci.srq = SET; /* set the SRQ flip-flop */ + io_assert (&baci_dev, ioa_SIR); /* and assert SRQ */ tprintf (baci_dev, DEB_CMDS, "SRQ set, status = %06o\n", baci_status); @@ -1493,7 +1574,7 @@ if (baci_uart_clk > 0) { /* transfer in progress? baci_uart_tr = baci_uart_tr >> 1; /* shift new bit onto line */ else /* receive? */ baci_uart_rr = (baci_uart_rr >> 1) & /* shift new bit in */ - (bit_low ? ~SIGN : -1); /* (inverted sense) */ + (bit_low ? ~D16_SIGN : ~0u); /* (inverted sense) */ if (bit_low) { /* another low bit? */ baci_bcount = baci_bcount + 1; /* update break counter */ @@ -1598,7 +1679,7 @@ if ((baci_uart_clk == 0) && /* start of transfer? */ baci_uart_tr = (~data_mask | baci_uart_tr) << 2 | 1; /* form serial data stream */ tprintf (baci_dev, DEB_XFER, "UART transmitter = %06o (%s), " - "clock count = %d\n", baci_uart_tr & DMASK, + "clock count = %d\n", baci_uart_tr & D16_MASK, fmt_char ((uint8) (baci_uart_thr & data_mask)), baci_uart_clk); } diff --git a/HP2100/hp2100_bugfixes.txt b/HP2100/hp2100_bugfixes.txt index e99b1131..563862cf 100644 --- a/HP2100/hp2100_bugfixes.txt +++ b/HP2100/hp2100_bugfixes.txt @@ -504,7 +504,7 @@ date above. CAUSE: DMA termination before the end-of-record is not being handled properly by the 7970E simulator. The RTE driver sets up DMA control word 1 - with the STC bit (bit 15) clear and the CLC bit (bit 13) set. The DMA + with the STC bit (bit 15) clear and the CLC bit (bit 13) set. The DMA transfer proceeds apace until DMA control word 3 (word count) goes to zero. At this point, the last cycle logic in "dma_cycle" (hp2100_cpu.c, lines 2477-2480) issues a CLC to the mag tape data channel. In "msdio" @@ -697,7 +697,7 @@ date above. CAUSE: To perform a disc read, the TSB disc driver issues a seek command but does not wait for seek completion before issuing the read command to the - interface. This is allowed by the interface manual. The eventual interrupt + interface. This is allowed by the interface manual. The eventual interrupt signifies the completion of both the seek and the read commands. However, the "drive attention" flag that is normally generated at the end of @@ -3560,7 +3560,7 @@ date above. The diagnostic sets up a one-word output with STC and CLC specified in the control word. At the end of the transfer, "dma_cycle" (hp2100_cpu.c) - correctly sends LPS a STC,C followed by a CLC,C. The STC,C starts a + correctly sends LPS a STC,C followed by a CLC,C. The STC,C starts a transfer and therefore schedules an I/O event for completion in one instruction. The CLC,C clears the control flip-flop and the device flag, but because "sim_cancel" is not called, the I/O event remains. When it @@ -4145,10 +4145,10 @@ date above. OBSERVATION: After reaching a map-specific breakpoint (e.g., a system-map breakpoint to debug a device driver), the most common action is to examine - memory locations and set another breakpoint farther ahead in the code. That - breakpoint will, of course, be set in the same mapping mode as the one just - reached, i.e., in the current DMS mapping mode. Therefore, defaulting to - "the same map as is currently enabled" leads to the most-used cases not + memory locations and set another breakpoint farther ahead in the code. + That breakpoint will, of course, be set in the same mapping mode as the one + just reached, i.e., in the current DMS mapping mode. Therefore, defaulting + to "the same map as is currently enabled" leads to the most-used cases not requiring additional switches (and therefore the chance of operator error). RESOLUTION: Before exiting "sim_instr" (hp2100_cpu.c), set "sim_brk_dflt" @@ -5413,7 +5413,7 @@ date above. RESOLUTION: Modify the instruction execution loop (hp2100_cpu.c) to recalculate SRQ requests after each DMA cycle, and if a request is still - pending, skip instruction execution. This allows consecutive DMA cycles + pending, skip instruction execution. This allows consecutive DMA cycles without intervening instruction executions if SRQ is asserted continuously. STATUS: Fixed in version 3.9-0. @@ -6714,7 +6714,7 @@ date above. initialized to NULL and can be overridden by the VM, and, if overridden, call it after printing the message string and before printing the program counter. If the VM printer returns TRUE, print the comma and the program - counter as before. If the VM routine returns FALSE, print just the + counter as before. If the VM routine returns FALSE, print just the terminating newline. STATUS: Fixed in version 4.0-0. diff --git a/HP2100/hp2100_cpu.c b/HP2100/hp2100_cpu.c index fa758bb7..48aa0ee2 100644 --- a/HP2100/hp2100_cpu.c +++ b/HP2100/hp2100_cpu.c @@ -1,7 +1,7 @@ -/* hp2100_cpu.c: HP 21xx/1000 Central Processing Unit/MEM/MP/DCPC simulator +/* hp2100_cpu.c: HP 21xx/1000 Central Processing Unit simulator Copyright (c) 1993-2016, Robert M. Supnik - Copyright (c) 2017-2018, J. David Bryan + Copyright (c) 2017-2019, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,11 +25,18 @@ in this Software without prior written authorization from the authors. CPU 2114C/2115A/2116C/2100A/1000-M/E/F Central Processing Unit - 12731A Memory Expansion Module - DMA1,DMA2 12607B/12578A/12895A Direct Memory Access - DCPC1,DCPC2 12897B Dual Channel Port Controller - MP 12581A/12892B Memory Protect + I/O subsystem + Power Fail Recovery System + 12-Feb-19 JDB Worked around idle problem (SIMH issue 622) + 06-Feb-19 JDB Corrected trace report for simulation stop + 05-Feb-19 JDB sim_dname now takes a const pointer + 13-Aug-18 JDB Renamed "ion_defer" to "cpu_interrupt_enable" and flipped sense + 24-Jul-18 JDB Removed unneeded "iotrap" parameter from "cpu_iog" routine + 20-Jul-18 JDB Moved memory/MEM/MP and DMA into separate source files + 29-Jun-18 JDB Fixed "clear_option" return type definition + 14-Jun-18 JDB Renamed PRO device to MPPE + 05-Jun-18 JDB Revised I/O model 21-May-18 JDB Changed "access" to "mem_access" to avoid clashing 07-May-18 JDB Modified "io_dispatch" to display outbound signals 01-May-18 JDB Multiple consecutive CLC 0 operations are now omitted @@ -191,21 +198,13 @@ References: - 2100A Computer Reference Manual - (02100-90001, Dec-1971) + (02100-90001, December 1971) - Model 2100A Computer Installation and Maintenance Manual - (02100-90002, Aug-1972) + (02100-90002, August 1972) - HP 1000 M/E/F-Series Computers Technical Reference Handbook - (5955-0282, Mar-1980) + (5955-0282, March 1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation - (92851-90001, Mar-1981) - - HP 1000 M/E/F-Series Computers I/O Interfacing Guide - (02109-90006, Sep-1980) - - 12607A Direct Memory Access Operating and Service Manual - (12607-90002, Jan-1970) - - 12578A/12578A-01 Direct Memory Access Operating and Service Manual - (12578-9001, Mar-1972) - - 12892B Memory Protect Installation Manual - (12892-90007, Jun-1978) + (92851-90001, March 1981) - HP 1000 Computer Real-Time Systems (5091-4479, August 1992) @@ -226,19 +225,19 @@ simulator does not model the 1000 L/A-Series machines. All of the machines support a 15-bit logical address space, addressing a - maximum of 32 K words, divided into 1K-word pages. Memory-referencing + maximum of 32K words, divided into 1K-word pages. Memory-referencing instructions in the base set can directly address the 1024 words of the base page (page 0) or the 1024 words of the current page (the page containing the instruction). The instructions in the extended set directly address the 32768 words in the full logical address space. The A and B accumulators may - be addressed as logical addresses 0 and 1, respectively. + be addressed as logical memory addresses 0 and 1, respectively. Peripheral devices are connected to the CPU by interface cards installed in - the I/O card cages present in the CPU and optional I/O extender chassis. Each - slot in the card cage is assigned an address, called a select code, that may - be referenced by I/O instructions in the base set. Select codes range from 0 - to 77 octal, with the first eight select codes reserved for the system, - providing connections for 56 possible interfaces. + the I/O card cages present in the CPU and optional I/O extender chassis. + Each slot in the card cage is assigned an address, called a select code, that + may be referenced by I/O instructions in the base set. Select codes range + from 0 to 77 octal, with the first eight select codes reserved for the + system, providing connections for 56 possible interfaces. The 211x machines use a hardwired processor providing 70 basic instructions and up to 32K of core memory. The base instruction set is divided into the @@ -252,7 +251,7 @@ The 2100 machine uses a microprogrammed processor that provides the 80 instructions of the base set and the EAU as standard equipment. Optional - floating-point microcode adds six two-word single-precision instructions. + floating-point microcode adds six two-word (single-precision) instructions. User microprogramming is also supported. When used as part of an HP 2000 Time-Shared BASIC system, the CPU designated as the I/O processor may be equipped with microcode implementing 18 additional OS accelerator @@ -264,8 +263,7 @@ 10 word-and-byte-manipulation instructions. The six 2100 floating-point instructions are also standard. The 1000 F-Series adds a hardware floating-point processor with 18 new triple- and quad-word instructions. A - number of new optional microcode extensions are available with the - M/E/F-Series. + number of optional microcode extensions are available with the M/E/F-Series. 1000 CPUs offer the optional Dynamic Mapping System, which provides memory mapping on a page-by-page basis. The 5-bit page number of a logical memory @@ -279,10 +277,10 @@ Optional memory protection is accomplished by dividing the logical address space into protected and unprotected parts. When protection is enabled, any - attempt to write below the fence separating the two parts is inhibited, and - an interrupt to the operating system occurs, which aborts the offending user - program. If the DMS option is enabled as well, protection is enhanced by - specifying read and write permissions on a page-by-page basis. + attempt to write or jump below the fence separating the two parts is + inhibited, and an interrupt to the operating system occurs, which aborts the + offending user program. If the DMS option is enabled as well, protection is + enhanced by specifying read and write permissions on a page-by-page basis. A note on terminology: the 1000 series of computers was originally called the 21MX at introduction. The 21MX (occasionally, 21MXM) corresponds to the 1000 @@ -304,14 +302,25 @@ Name Width Description ---- ----- ---------------------------------------------- - A 16 accumulator (addressable as memory location 0) - B 16 accumulator (addressable as memory location 1) - P 15 program counter - S 16 switch and display register - M 15 memory address register - T 16 memory data register - E 1 extend flag (carry out) - O 1 overflow flag + A 16 Accumulator (addressable as memory location 0) + B 16 Accumulator (addressable as memory location 1) + P 15 Program counter + S 16 Switch and display register + M 15 Memory address register + T 16 Memory data register + E 1 Extend flag (arithmetic carry out) + O 1 Overflow flag (arithmetic overflow) + + In addition, there are two internal registers that are not visible to the + programmer but are used by the hardware: + + Name Width Description + ---- ----- ---------------------------------------------- + IR 16 Instruction register + CIR 6 Central interrupt register + + The Instruction Register holds the current instruction, while the Central + Interrupt Register holds the select code identifying an interrupting device. The 1000 Series adds these CPU hardware registers: @@ -347,13 +356,15 @@ All of the instructions added after the 2116 are in the Macroinstruction Group. - The 2116 offered two hardware options that extended the instruction set. The + The 2116 offers two hardware options that extended the instruction set. The first is the 12579A Extended Arithmetic Unit. The second is the 2152A Floating Point Processor, which is interfaced through, and therefore requires, the EAU. The EAU adds 10 instructions including integer multiply and divide and double-word loads, stores, shifts, and rotates. The FPP adds 30 floating-point arithmetic, trigonometric, logarithmic, and exponential - instructions. (The 2116 FFP is not simulated.) + instructions. The 2116 EAU is compatible with the 2100 and 1000 EAU + implementations and is provided by the simulator. The 2116 FPP is unique + to that machine and is not simulated. The base set groups are decoded from bits 15-12 and 10, as follows: @@ -396,7 +407,7 @@ are machine-to-machine variations, and some unimplemented instructions execute as other, defined instructions. - The instruction set groups are encoded as follows: + The Memory-Reference Group instructions are encoded as follows: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -416,17 +427,17 @@ The "mem ops" are encoded as follows: 14-11 Mnemonic Action - ----- -------- ---------------------------------------- + ----- -------- --------------------------------------------- 0010 AND A = A & M [MA] 0011 JSB M [MA] = P, P = MA + 1 0100 XOR A = A ^ M [MA] 0101 JMP P = MA 0110 IOR A = A | M [MA] - 0111 ISZ M [MA] = M [MA] + 1, skip if M [MA] == 0 + 0111 ISZ M [MA] = M [MA] + 1, P = P + 2 if M [MA] == 0 1000 ADA A = A + M [MA] 1001 ADB B = B + M [MA] - 1010 CPA skip if A != M [MA] - 1011 CPB skip if B != M [MA] + 1010 CPA P = P + 2 if A != M [MA] + 1011 CPB P = P + 2 if B != M [MA] 1100 LDA A = M [MA] 1101 LDB B = M [MA] 1110 STA M [MA] = A @@ -434,14 +445,16 @@ Bits 15 and 10 encode the type of access, as follows: - 15,10 Access Type Action - ----- --------------------- -------------------------- - 0,0 base page direct MA = I <9:0> - 0,1 current page direct MA = P <14:0>'I <9:0> - 1,0 base page indirect MA = M [I <9:0>] - 1,1 current page indirect MA = M [P <14:10>'I <9:0>] + 15 10 Access Type Action + --- --- --------------------- ----------------------------- + 0 0 base page direct MA = IR <9:0> + 0 1 current page direct MA = P <14:10> | IR <9:0> + 1 0 base page indirect MA = M [IR <9:0>] + 1 1 current page indirect MA = M [P <14:10> | IR <9:0>] + The Shift-Rotate Group instructions are encoded as follows: + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | 0 | 0 0 0 | R | 0 | E | op 1 | C | E | S | op 2 | SRG @@ -454,6 +467,21 @@ C = CLE S = SL* + Bits 8-6 and 2-0 each encode one of eight operations, as follows: + + Op N Operation + ---- --------------------------- + 000 Arithmetic left shift + 001 Arithmetic right shift + 010 Rotate left + 011 Rotate right + 100 Shift left and clear sign + 101 Rotate right through Extend + 110 Rotate left through Extend + 111 Rotate left four bits + + + The Alter-Skip Group instructions are encoded as follows: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -470,6 +498,22 @@ Z = SZ* V = RSS + Bits 9-8 and 7-6 each encode one of three operations, as follows: + + 9-8 Operation + --- ------------------------ + 01 Clear A/B + 10 Complement A/B + 11 Clear and complement A/B + + 7-6 Operation + --- --------------------------- + 01 Clear Extend + 10 Complement Extend + 11 Clear and complement Extend + + + The Input-Output Group instructions are encoded as follows: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -481,23 +525,69 @@ R = A/B register (0/1) H = hold/clear flag (0/1) + There are ten instructions. Six are encoded directly by bits 8-6, with + bits 11 and 9 assuming the definitions above. The other four are encoded + with bits 11 and 9 differentiating, as follows: + + 11 9 8-6 Operation + --- --- --- --------------------------- + x H 000 Halt + x 0 001 Set the flag flip-flop + x 1 001 Clear the flag flip-flop + x H 010 Skip if the flag is clear + x H 011 Skip if the flag is set + R H 100 Merge input data into A/B + R H 101 Load input data into A/B + R H 110 Store output data from A/B + 0 H 111 Set the control flip-flop + 1 H 111 Clear the control flip-flop + An I/O group instruction controls the device specified by the select code. Depending on the opcode, the instruction may set or clear the device flag, start or stop I/O, or read or write data. + The Macroinstruction Group instructions are encoded with bits 15-12 and 10 as + 1 000 0. Bits 11 and 9-0 determine the specific EAU or UIG instruction. + + The Extended Arithmetic Group instructions are encoded as follows: + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 1 | 0 0 0 | | 0 | eau op | 0 0 0 0 0 0 | EAU + | 1 | 0 0 0 | op| 0 | operation | 0 0 0 0 0 0 | EAG +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + Operations: + + 11 9-6 Operation + --- ---- ----------------------------------------------------- + 0 0010 Multiply 16 x 16 = 32-bit product + 0 0100 Divide 32 / 16 = 16-bit quotient and 16-bit remainder + 1 0010 Double load A and B registers from memory + 1 0100 Double store A and B registers to memory + + All other encodings are undefined. + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 1 | 0 0 0 | | 0 | eau shift/rotate op | shift count | EAU + | 1 | 0 0 0 | 0 | 0 | shift/rotate op | shift count | EAG +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - MAC ops decode when bits 15-12 and 10 are 1 000 0. Bits 11 and 9-0 determine - the specific EAU instruction. + Operations: + 9-4 Operation + ------ -------------------------------------------------- + 100001 Arithmetic shift right A and B registers 1-16 bits + 000001 Arithmetic shift left A and B registers 1-16 bits + 100010 Logical shift right A and B registers 1-16 bits + 000010 Logical shift left A and B registers 1-16 bits + 100100 Rotate right A and B registers 1-16 bits + 000100 Rotate left A and B registers 1-16 bits + + The shift count encodes the number of bits shifted, with a count of zero + representing a shift of 16 bits. + + + The User Instruction Group instructions are encoded as follows: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -508,45 +598,103 @@ R = A/B register (0/1) + Bits 8-4 encode the microcode module containing the instructions, and bits + 3-0 encode the specific instructions. See the individual UIG instruction + simulator source files for the specific encodings used. - In simulation, I/O devices are modelled by substituting software states for - I/O backplane signals. The set of signals generated by I/O instructions and - DMA cycles is dispatched to the target device for action. Backplane signals - are processed sequentially. For example, the "STC sc,C" instruction - generates the "set control" and the "clear flag" signals that are processed - in that order. - CPU interrupt signals are modelled as three parallel arrays: + I/O device interfaces and their connected devices are simulated by + substituting software states for I/O backplane signals. The set of signals + generated by I/O instructions and DMA cycles is dispatched to the target + interface simulator for action, and the set of signals asserted or denied in + response is returned from the call. Backplane signals are processed + sequentially. For example, the "STC sc,C" instruction generates the "set + control" and the "clear flag" signals that are processed in that order. - - device request priority as bit vector dev_prl [2] [31..0] - - device interrupt requests as bit vector dev_irq [2] [31..0] - - device service requests as bit vector dev_srq [2] [31..0] + The HP 21xx/1000 interrupt structure is based on the PRH, PRL, IEN, IRQ, and + IAK signals. PRH indicates that no higher-priority device is interrupting. + PRL indicates to lower-priority devices that a given device is not + interrupting. IEN asserts when the interrupt system is enabled. IRQ + indicates that a given device is requesting an interrupt. IAK indicates that + the given device's interrupt request is being acknowledged. - Each array forms a 64-bit vector, with bits 0-31 of the first element - corresponding to select codes 00-37 octal, and bits 0-31 of the second - element corresponding to select codes 40-77 octal. - - The HP 21xx/1000 interrupt structure is based on the PRH, PRL, IRQ, and IAK - signals. PRH indicates that no higher-priority device is interrupting. PRL - indicates to lower-priority devices that a given device is not interrupting. - IRQ indicates that a given device is requesting an interrupt. IAK indicates - that the given device's interrupt request is being acknowledged. - - PRH and PRL form a hardware priority chain that extends from interface to - interface on the backplane. We model just PRL, as PRH is calculated from the - PRLs of higher-priority devices. - - Typical I/O devices have a flag, flag buffer, and control flip-flops. If a - device's flag, flag buffer, and control bits are set, and the device is the - highest priority on the interrupt chain, it requests an interrupt by - asserting IRQ. When the interrupt is acknowledged with IAK, the flag buffer - is cleared, preventing further interrupt requests from that device. The - combination of flag and control set blocks interrupts from lower priority + Typical I/O interfaces have a flag buffer, a flag, and a control flip-flop. + If an interface's flag buffer, flag, and control flip-flops are all set, the + interrupt system is enabled, and the interface has the highest priority on + the interrupt chain, it requests an interrupt by asserting the IRQ signal. + When the interrupt is acknowledged with the IAK signal, the flag buffer is + cleared, preventing further interrupt requests from that interface. The + combination of flag set and control set blocks interrupts from lower priority devices. Service requests are used to trigger the DMA service logic. Setting the - device flag typically also sets SRQ, although SRQ may be calculated - independently. + interface flag flip-flop typically asserts SRQ, although SRQ may be + calculated independently. + + Most of the I/O signals are generated by execution of IOG instructions or DMA + cycles; the target interface simulator is called explicitly to respond to the + signal assertions. However, two hardware signals (ENF and SIR) are periodic, + and a direct simulation would call each interface simulator with these + signals after each machine instruction. Instead, the interface simulator is + called only when these signals would have an effect on the state of the + interface. Also, two signals (IEN and PRH) are combinatorial, in that + changing either potentially affects all interfaces in the system. These are + handled efficiently by saving the states of each interface in bit vectors, as + described below. + + PRH and PRL form a hardware priority chain that extends from interface to + interface on the backplane. For an interface to generate an interrupt, PRH + must be asserted by the next-higher-priority interface. When an interface + generates an interrupt, it denies PRL to the next-lower-priority interface. + When an interface is not interrupting, it passes PRH to PRL unaltered. This + means that a given interface can interrupt only if all higher-priority + devices are receiving PRH asserted and are asserting PRL. It also means that + clearing the interrupt on a given interface, i.e., reasserting PRL, may + affect all lower-priority interfaces. + + As an example, assume that the interface at select code 10 ("SC 10") has its + flag buffer, flag, and control flip-flops set, that IEN and PRH are asserted, + and that no other interfaces have their flag flip-flops set. SC 10 will + assert IRQ and deny PRL. SC 11 sees its PRH low (because PRL 10 is connected + to PRH 11) and so denies PRL, and this action ripples through all of the + lower-priority interfaces. + + Then, while the interrupt for SC 10 is being serviced, the interface at + select code 17 sets its flag buffer, flag, and control flip-flops. SC 17 is + inhibited from interrupting by PRH denied. + + When the interrupt service routine clears the interrupt by clearing the flag + buffer and flag flip-flops, SC 10 reasserts PRL to SC 11, and that signal + ripples through the interfaces at select codes 12-16, arriving at SC 17 as + PRH assertion. With PRH asserted, SC 17 generates an interrupt and denies + PRL to SC 20 and above. + + A direct simulation of this hardware behavior would require calling all + lower-priority interface simulators in ascending select code (and therefore + priority) order with the PRH signal and checking for an asserted IRQ signal + or a denied PRL signal. This is inefficient. + + To avoid making a potentially long sequence of calls, each interface + simulator returns a "conditional IRQ" signal and a "conditional PRL" signal + in addition to the standard IRQ and PRL signals. The conditional signals + are those that would result if the higher-priority interface is asserting + PRH and the interrupt system is on. So, for instance, an interface simulator + with its flag buffer, flag, and control flip-flops set will assert its + conditional IRQ signal and deny its conditional PRL signal. If PRH and IEN + are asserted, then its "real" IRQ and PRL signals are also asserted and + denied respectively. + + For fast assertion checking, the conditional IRQ and PRL signal states are + kept in bit vectors, which are updated after each interface simulator call. + Each vector is represented as a two-element array of 32-bit unsigned + integers, forming a 64-bit vector in which bits 0-31 of the first element + correspond to select codes 00-37 octal, and bits 0-31 of the second element + correspond to select codes 40-77 octal. The "interrupt_request_set" array + holds conditional IRQ states, and the "priority_holdoff_set" array holds the + complement of the conditional PRL states (the complement is used to simplify + the priority calculation). These vectors permit rapid determination of an + interrupting interface when a higher-priority interface reasserts PRL or when + the interrupt system is reenabled. The simulator provides three stop conditions related to instruction execution @@ -613,119 +761,6 @@ likely represents a programming error. The default setting is 16 levels. - In addition to the CPU, this module simulates the 12578A/12607B/12895A Direct - Memory Access and 12897B Dual-Channel Port Controller devices (hereafter, - "DMA"). These controllers permit the CPU to transfer data directly between - an I/O device and memory on a cycle-stealing basis. Depending on the CPU, - the device interface, and main memory speed, DMA is capable of transferring - data blocks from 1 to 32,768 words in length at rates between 500,000 and - 1,000,000 words per second. The 2114 supports a single DMA channel. All - other CPUs support two DMA channels. - - DMA is programmed by setting three control words via two select codes: 2 and - 6 for channel 1, and 3 and 7 for channel 2. During simultaneous transfers, - channel 1 has priority over channel 2. Otherwise, the channels are - identical. Channel programming involves setting three control words, as - follows: - - SC 06 Control Word 1 format: - - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | S | B | C | - - - - - - - | device select code | - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - - Where: - - S = assert STC during each cycle - B = enable byte packing and unpacking (12578A only) - C = assert CLC at the end of the block transfer - - SC 02 Control Word 2/3 format: - - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | D | starting memory address | word 2 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | negative word count | word 3 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - - Where: - - D = transfer direction is out of/into memory (0/1) - - Control word 2 is stored if the control flip-flop of select code 2 is clear, - i.e., if the OTA/B is preceded by CLC; control word 3 is stored if the - flip-flop is set by a preceding STC. - - The 12607B supports 14-bit addresses and 13-bit word counts. The 12578A - supports 15-bit addresses and 14-bit word counts. The 12895A and 12897B - support 15-bit addresses and 16-bit word counts. - - DMA is started by setting the control flip-flop on select code 6. DMA - completion is indicated when the flag flip-flop sets on select code 8, which - causes an interrupt if enabled. - - - This module also simulates the 12581A/12892B Memory Protect devices for the - 2116 and 1000 M/E/F-Series, respectively, and the memory protect feature that - is standard equipment for the 2100. MP is addressed via select code 5 and - provides a fence register that holds the address of the start of unprotected - memory and a violation register that holds the address of the instruction - that has caused a memory protect interrupt, as follows: - - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 | starting address of unprotected memory | fence - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 | violating instruction address | violation - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - - After setting the fence regiater with an OTA 5 or OTB 5 instruction, MP is - enabled by an STC 5. - - - This module also simulates the 12731A Memory Expansion Module for the 1000 - M/E/F-Series machines. The MEM provides mapping of the 32 1024-word logical - memory pages into a one-megaword physical memory. Four separate maps are - provided: system, user, DCPC port A, and DCPC port B. The MEM is controlled - by the associated Dynamic Mapping System instructions and contains status and - violation registers, as follows: - - MEM Status Register: - - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | I | M | E | U | P | B | base page fence address | - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - - Where: - - I = MEM disabled/enabled (0/1) at last interrupt - M = System/user map (0/1) selected at last interrupt - E = MEM disabled/enabled (0/1) currently - U = System/user map (0/1) selected currently - P = Protected mode disabled/enabled (0/1) currently - B = Base-page portion mapped (0/1 = above/below the fence) - - MEM Violation Register: - - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | R | W | B | P | - - - - | S | E | M | map address | - +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - - Where: - - R = Read violation - W = Write violation - B = Base-page violation - P = Privileged instruction violation - S = ME bus disabled/enabled (0/1) at violation - E = MEM disabled/enabled (0/1) at violation - M = System/user map (0/1) selected at violation - - The CPU simulator provides extensive tracing capabilities that may be enabled with the SET DEBUG and SET CPU DEBUG= commands. The trace options that may be specified are: @@ -845,30 +880,30 @@ | +------------------------- octal physical page number +----------------------------- memory map (S/U/A/B/- system/user/port A/port B/disabled) - >>CPU reg: P .... 01535 040013 A 123003, B 001340, X 000000, Y 000000, e O I + >>CPU reg: P **** 01535 040013 A 123003, B 001340, X 000000, Y 000000, e O I ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | | | +-- A, B, X, Y, E, O, interrupt system registers | | | | (lower/upper case = 0/1 or off/on) | | | +------------ S register | | +-------------------- MEM fence - | +------------------------- + | +------------------------- (place holder) +----------------------------- protection state (P/- protected/unprotected) - >>CPU reg: P .... ..... ...... MPF 00000, MPV 000000, MES 000000, MEV 000000 + >>CPU reg: P **** ***** ****** MPF 00000, MPV 000000, MES 000000, MEV 000000 ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | | | +-- memory protect fence and violation registers | | | | memory expansion status and violation registers - | | | +------------ - | | +-------------------- - | +------------------------- + | | | +------------ (place holder) + | | +-------------------- (place holder) + | +------------------------- (place holder) +----------------------------- protection state (P/- protected/unprotected) - >>CPU opnd: . .... 36002 101475 return location is P+3 (error EM21) - >>CPU opnd: . .... 22067 105355 entry is for a dynamic mapping violation + >>CPU opnd: . **** 36002 101475 return location is P+3 (error EM21) + >>CPU opnd: . **** 22067 105355 entry is for a dynamic mapping violation ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | +-- operand-specific value @@ -887,33 +922,162 @@ instruction, the interrupt is taken at the appropriate point; but there is no testing for new interrupts during execution (that is, the event timer is not called). + + 2. The Power Fail option is not currently implemented. + + 3. Idling with 4.x simulator framework versions after June 14, 2018 (git + commit ID d3986466) loses TBG ticks. This causes the DOS/RTE/TSB system + clock to run slowly, losing about one second per minute. A workaround is + to prevent "sim_interval" from going negative on return from "sim_idle". + Issue 622 on the SIMH site describes the problem. */ +#include + #include "hp2100_defs.h" #include "hp2100_cpu.h" -#include "hp2100_cpu1.h" +#include "hp2100_cpu_dmm.h" + + + +/* Lost time workaround */ + +#if (SIM_MAJOR >= 4) + #define sim_idle(timer,decrement) \ + if (sim_idle (timer, decrement) == TRUE /* [workaround] idle the simulator; if idling occurred */ \ + && sim_interval < 0) /* [workaround] and the time interval is negative */ \ + sim_interval = 0 /* [workaround] then reset it to zero */ +#endif /* CPU program constants */ +/* Alter-Skip Group instruction register fields */ -/* Command line switches */ +#define IR_CMx 0001000u /* CMA/B */ +#define IR_CLx 0000400u /* CLA/B */ +#define IR_CME 0000200u /* CME */ +#define IR_CLE 0000100u /* CLE */ +#define IR_SEZ 0000040u /* SEZ */ +#define IR_SSx 0000020u /* SSA/B */ +#define IR_SLx 0000010u /* SLA/B */ +#define IR_INx 0000004u /* INA/B */ +#define IR_SZx 0000002u /* SZA/B */ +#define IR_RSS 0000001u /* RSS */ -#define ALL_MAPMODES (SWMASK ('S') | SWMASK ('U') | SWMASK ('P') | SWMASK ('Q')) +#define IR_SSx_SLx_RSS (IR_SSx | IR_SLx | IR_RSS) /* a special case */ +#define IR_ALL_SKIPS (IR_SEZ | IR_SZx | IR_SSx_SLx_RSS) /* another special case */ + +/* Shift-Rotate Group instruction register micro-ops */ + +#define IR_xLS 0000000u /* ALS/BLS */ +#define IR_xRS 0000001u /* ARS/BRS */ +#define IR_RxL 0000002u /* RAL/RBL */ +#define IR_RxR 0000003u /* RAR/RBR */ +#define IR_xLR 0000004u /* ALR/BLR */ +#define IR_ERx 0000005u /* ERA/ERB */ +#define IR_ELx 0000006u /* ELA/ELB */ +#define IR_xLF 0000007u /* ALF/BLF */ + +#define SRG_DIS 0000000u /* micro-op disable */ +#define SRG1_EN 0000010u /* micro-op 1 enable */ +#define SRG2_EN 0000020u /* micro-op 2 enable */ + +/* Instruction register masks */ + +#define IR_MRG (MRG | AB_MASK) /* MRG instructions mask */ +#define IR_MRG_I (IR_MRG | IR_IND) /* MRG indirect instructions mask */ + +#define IR_JSB 0014000u /* JSB instruction */ +#define IR_JSB_I (IR_JSB | IR_IND) /* JSB,I instruction */ +#define IR_JMP 0024000u /* JMP instruction */ + +#define IR_HLT_MASK 0172700u /* I/O group mask for HLT[,C] instruction */ +#define IR_CLC_MASK 0176700u /* I/O group mask for a CLC[,C] instruction */ + +#define IR_HLT 0102000u /* HLT instruction */ +#define IR_CLC 0106700u /* CLC instruction */ -/* RTE base-page addresses */ +/* CPU unit flags and accessors. -static const uint32 xeqt = 0001717; /* XEQT address */ -static const uint32 tbg = 0001674; /* TBG address */ + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | r | - | G | V | O | E | D | F | M | I | P | U | CPU model | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -/* DOS base-page addresses */ + Where: -static const uint32 m64 = 0000040; /* constant -64 address */ -static const uint32 p64 = 0000067; /* constant +64 address */ + r = reserved + G = SIGNAL/1000 firmware is present + V = Vector Instruction Set firmware is present + O = RTE-6/VM VMA and OS firmware is present + E = RTE-IV EMA firmware is present + D = Double Integer firmware is present + F = Fast FORTRAN Processor firmware is present + M = Dynamic Mapping System firmware is present + I = 2000 I/O Processor firmware is present + P = Floating Point hardware or firmware is present + U = Extended Arithmetic Unit is present +*/ + +#define UNIT_MODEL_SHIFT (UNIT_V_UF + 0) /* bits 0- 3: CPU model (OPTION_ID value) */ +#define UNIT_OPTION_SHIFT (UNIT_V_UF + 4) /* bits 4-15: CPU options installed */ + +#define UNIT_MODEL_MASK 0000017u /* model ID mask */ +#define UNIT_OPTION_MASK 0003777u /* option ID mask */ + +#define UNIT_MODEL_FIELD (UNIT_MODEL_MASK << UNIT_MODEL_SHIFT) +#define UNIT_OPTION_FIELD (UNIT_OPTION_MASK << UNIT_OPTION_SHIFT) + +#define UNIT_MODEL(f) ((f) >> UNIT_MODEL_SHIFT & UNIT_MODEL_MASK) +#define UNIT_OPTION(f) ((f) >> UNIT_OPTION_SHIFT & UNIT_OPTION_MASK) + +#define TO_UNIT_OPTION(id) (1u << UNIT_OPTION_SHIFT + (id) - Option_BASE - 1) + +/* Unit models */ + +#define UNIT_2116 (Option_2116 << UNIT_MODEL_SHIFT) +#define UNIT_2115 (Option_2115 << UNIT_MODEL_SHIFT) +#define UNIT_2114 (Option_2114 << UNIT_MODEL_SHIFT) +#define UNIT_2100 (Option_2100 << UNIT_MODEL_SHIFT) +#define UNIT_1000_M (Option_1000_M << UNIT_MODEL_SHIFT) +#define UNIT_1000_E (Option_1000_E << UNIT_MODEL_SHIFT) +#define UNIT_1000_F (Option_1000_F << UNIT_MODEL_SHIFT) + +/* Unit options */ + +#define UNIT_EAU TO_UNIT_OPTION (Option_EAU) +#define UNIT_FP TO_UNIT_OPTION (Option_FP) +#define UNIT_IOP TO_UNIT_OPTION (Option_IOP) +#define UNIT_DMS TO_UNIT_OPTION (Option_DMS) +#define UNIT_FFP TO_UNIT_OPTION (Option_FFP) +#define UNIT_DBI TO_UNIT_OPTION (Option_DBI) +#define UNIT_EMA TO_UNIT_OPTION (Option_EMA) +#define UNIT_VMAOS TO_UNIT_OPTION (Option_VMAOS) +#define UNIT_VIS TO_UNIT_OPTION (Option_VIS) +#define UNIT_SIGNAL TO_UNIT_OPTION (Option_SIGNAL) +#define UNIT_DS TO_UNIT_OPTION (Option_DS) + +#define UNIT_EMA_VMA (UNIT_EMA | UNIT_VMAOS) + +/* Unit conversions to CPU options */ + +#define TO_CPU_MODEL(f) (CPU_OPTION) (1u << UNIT_MODEL (f)) +#define TO_CPU_OPTION(f) (CPU_OPTION) (UNIT_OPTION (f) << CPU_OPTION_SHIFT) + +/* "Pseudo-option" flags used only for option testing; never set into the UNIT structure */ + +#define UNIT_V_PFAIL (UNIT_V_UF - 1) /* Power fail is installed */ +#define UNIT_V_DMA (UNIT_V_UF - 2) /* DMA is installed */ +#define UNIT_V_MP (UNIT_V_UF - 3) /* Memory protect is installed */ + +#define UNIT_PFAIL (1 << UNIT_V_PFAIL) +#define UNIT_DMA (1 << UNIT_V_DMA) +#define UNIT_MP (1 << UNIT_V_MP) /* CPU global SCP data definitions */ @@ -926,95 +1090,79 @@ REG *sim_PC = NULL; /* the pointer to the P register /* CPU registers */ -HP_WORD ABREG [2] = { 0, 0}; /* A and B registers */ -HP_WORD PR = 0; /* P register */ -HP_WORD SR = 0; /* S register */ -HP_WORD MR = 0; /* M register */ -HP_WORD TR = 0; /* T register */ -HP_WORD XR = 0; /* X register */ -HP_WORD YR = 0; /* Y register */ -uint32 E = 0; /* E register */ -uint32 O = 0; /* O register */ +HP_WORD ABREG [2] = { 0, 0 }; /* A and B registers */ + +HP_WORD PR = 0; /* P register */ +HP_WORD SR = 0; /* S register */ +HP_WORD MR = 0; /* M register */ +HP_WORD TR = 0; /* T register */ +HP_WORD XR = 0; /* X register */ +HP_WORD YR = 0; /* Y register */ + +uint32 E = 0; /* E register */ +uint32 O = 0; /* O register */ HP_WORD IR = 0; /* Instruction Register */ HP_WORD CIR = 0; /* Central Interrupt Register */ +HP_WORD SPR = 0; /* 1000 Stack Pointer Register / 2100 F Register */ /* CPU global state */ -FLIP_FLOP ion = CLEAR; /* interrupt enable */ -t_bool ion_defer = FALSE; /* interrupt defer */ +FLIP_FLOP cpu_interrupt_enable = SET; /* interrupt enable flip-flop */ +uint32 cpu_pending_interrupt = 0; /* pending interrupt select code or zero if none */ -t_stat cpu_ss_unimpl = SCPE_OK; /* status return for unimplemented instruction execution */ -t_stat cpu_ss_undef = SCPE_OK; /* status return for undefined instruction execution */ -t_stat cpu_ss_unsc = SCPE_OK; /* status return for I/O to an unassigned select code */ -t_stat cpu_ss_ioerr = SCPE_OK; /* status return for an unreported I/O error */ -t_stat cpu_ss_inhibit = SCPE_OK; /* CPU stop inhibition mask */ -UNIT *cpu_ioerr_uptr = NULL; /* pointer to a unit with an unreported I/O error */ +t_stat cpu_ss_unimpl = SCPE_OK; /* status return for unimplemented instruction execution */ +t_stat cpu_ss_undef = SCPE_OK; /* status return for undefined instruction execution */ +t_stat cpu_ss_unsc = SCPE_OK; /* status return for I/O to an unassigned select code */ +t_stat cpu_ss_ioerr = SCPE_OK; /* status return for an unreported I/O error */ +t_stat cpu_ss_inhibit = SCPE_OK; /* CPU stop inhibition mask */ +UNIT *cpu_ioerr_uptr = NULL; /* pointer to a unit with an unreported I/O error */ -uint16 pcq [PCQ_SIZE] = { 0 }; /* PC queue (must be 16-bits wide for REG array entry) */ -uint32 pcq_p = 0; /* PC queue ptr */ -REG *pcq_r = NULL; /* PC queue reg ptr */ +HP_WORD err_PR = 0; /* error PC */ +uint16 pcq [PCQ_SIZE] = { 0 }; /* PC queue (must be 16-bits wide for REG array entry) */ +uint32 pcq_p = 0; /* PC queue pointer */ +REG *pcq_r = NULL; /* PC queue register pointer */ -uint32 cpu_configuration; /* the current CPU option set and model */ -uint32 cpu_speed = 1; /* the CPU speed, expressed as a multiplier of a real machine */ -t_bool is_1000 = FALSE; /* TRUE if the CPU is a 1000 M/E/F-Series */ - -uint32 dev_prl [2] = { ~0u, ~0u }; /* device priority low bit vector */ -uint32 dev_irq [2] = { 0u, 0u }; /* device interrupt request bit vector */ -uint32 dev_srq [2] = { 0u, 0u }; /* device service request bit vector */ +CPU_OPTION_SET cpu_configuration; /* the current CPU option set and model */ +uint32 cpu_speed = 1; /* the CPU speed, expressed as a multiplier of a real machine */ -/* Main memory global state */ - -MEMORY_WORD *M = NULL; /* pointer to allocated memory */ +/* CPU local state. -/* Memory Expansion Unit global state */ + Implementation notes: -uint32 dms_enb = 0; /* dms enable */ -uint32 dms_ump = 0; /* dms user map */ -HP_WORD dms_sr = 0; /* dms status reg */ + 1. The "is_1000" variable is used to index into tables where the row + selected depends on whether or not the CPU is a 1000 M/E/F-series model. + For logical tests that depend on this, it is faster (by one x86 machine + instruction) to test the "cpu_configuration" variable for the presence of + one of the three 1000 model flags. +*/ +static jmp_buf abort_environment; /* microcode abort environment */ -/* CPU local state */ +static FLIP_FLOP interrupt_system = CLEAR; /* interrupt system */ +static uint32 interrupt_request = 0; /* the currently interrupting select code or zero if none */ -static HP_WORD saved_MR = 0; /* M-register value between SCP commands */ -static uint32 fwanxm = 0; /* first word addr of nx mem */ -static uint32 jsb_plb = 2; /* protected lower bound for JSB */ +static uint32 interrupt_request_set [2] = { 0, 0 }; /* device interrupt request bit vector */ +static uint32 priority_holdoff_set [2] = { 0, 0 }; /* device priority holdoff bit vector */ -static uint32 exec_mask = 0; /* the current instruction execution trace mask */ -static uint32 exec_match = D16_UMAX; /* the current instruction execution trace matching value */ -static uint32 indirect_limit = 16; /* the indirect chain length limit */ -static uint32 last_select_code = 0; /* the last select code sent over the I/O backplane */ +static uint32 exec_mask = 0; /* the current instruction execution trace mask */ +static uint32 exec_match = D16_UMAX; /* the current instruction execution trace matching value */ +static uint32 indirect_limit = 16; /* the indirect chain length limit */ -static uint32 tbg_select_code = 0; /* the time-base generator select code (for RTE idle check) */ -static DEVICE *loader_rom [4] = { NULL }; /* the four boot loader ROM sockets in a 1000 CPU */ +static t_bool is_1000 = FALSE; /* TRUE if the CPU is a 1000 M/E/F-Series */ +static t_bool mp_is_present = FALSE; /* TRUE if Memory Protect is present */ +static uint32 last_select_code = 0; /* the last select code sent over the I/O backplane */ +static HP_WORD saved_MR = 0; /* the M-register value between SCP commands */ - -/* Memory Expansion Unit local state */ - -static HP_WORD dms_vr = 0; /* dms violation reg */ -static uint16 dms_map [MAP_NUM * MAP_LNT] = { 0 }; /* dms maps (must be 16-bits wide for REG array entry) */ +static DEVICE *loader_rom [4] = { NULL }; /* the four boot loader ROM sockets in a 1000 CPU */ /* CPU local data structures */ -/* Interrupt deferral table (1000 version) */ - -static t_bool defer_tab [] = { /* deferral table, indexed by I/O sub-opcode */ - FALSE, /* soHLT */ - TRUE, /* soFLG */ - TRUE, /* soSFC */ - TRUE, /* soSFS */ - FALSE, /* soMIX */ - FALSE, /* soLIX */ - FALSE, /* soOTX */ - TRUE /* soCTL */ - }; - - /* CPU features table. The feature table is used to validate CPU feature changes within the subset @@ -1025,55 +1173,46 @@ static t_bool defer_tab [] = { /* deferral table, indexed by I/ enabled or disabled as desired by the user. */ -struct FEATURE_TABLE { /* CPU model feature table: */ - uint32 typ; /* - typical features */ - uint32 opt; /* - optional features */ - uint32 maxmem; /* - maximum memory */ - }; +typedef struct { /* CPU model feature table */ + uint32 typ; /* standard features plus typically configured options */ + uint32 opt; /* complete list of optional features */ + uint32 maxmem; /* maximum configurable memory in 16-bit words */ + } FEATURE_TABLE; -static struct FEATURE_TABLE cpu_features [] = { /* features indexed by CPU model */ - { UNIT_DMA | UNIT_MP, /* UNIT_2116 */ +static const FEATURE_TABLE cpu_features [] = { /* CPU features indexed by OPTION_ID */ + { UNIT_DMA | UNIT_MP, /* Option_2116 */ UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU, 32 * 1024 }, - { UNIT_DMA, /* UNIT_2115 */ + { UNIT_DMA, /* Option_2115 */ UNIT_PFAIL | UNIT_DMA | UNIT_EAU, 8 * 1024 }, - { UNIT_DMA, /* UNIT_2114 */ + { UNIT_DMA, /* Option_2114 */ UNIT_PFAIL | UNIT_DMA, - 16 * 1024 }, - - { 0, 0, 0 /* unused model */ + 16 * 1024 }, - { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* UNIT_2100 */ + { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* Option_2100 */ UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP, 32 * 1024 }, - { 0, 0, 0 /* unused model */ - }, - { 0, 0, 0 /* unused model */ - }, - { 0, 0, 0 /* unused model */ - }, - - { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_M */ + { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* Option_1000_M */ UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | UNIT_IOP | UNIT_FFP | UNIT_DS, 1024 * 1024 }, - { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_E */ + { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* Option_1000_E */ UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | UNIT_IOP | UNIT_FFP | UNIT_DBI | UNIT_DS | UNIT_EMA_VMA, 1024 * 1024 }, - { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* UNIT_1000_F */ + { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* Option_1000_F */ UNIT_FFP | UNIT_DBI | UNIT_DMS, UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | UNIT_VIS | UNIT_DS | UNIT_SIGNAL | UNIT_EMA_VMA, @@ -1084,9 +1223,9 @@ static struct FEATURE_TABLE cpu_features [] = { /* features indexed by C /* CPU local SCP support routine declarations */ -static IOHANDLER cpuio; -static IOHANDLER ovflio; -static IOHANDLER pwrfio; +static INTERFACE cpu_interface; +static INTERFACE ovf_interface; +static INTERFACE pwr_interface; static t_stat cpu_examine (t_value *eval, t_addr address, UNIT *uptr, int32 switches); static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches); @@ -1106,24 +1245,17 @@ static t_stat set_exec (UNIT *uptr, int32 option, CONST char *cptr, void static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat show_roms (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat show_cage (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat show_exec (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 */ -static t_stat ea (HP_WORD IR, HP_WORD *address, uint32 irq); +static t_stat mrg_address (void); static HP_WORD srg_uop (HP_WORD value, HP_WORD operation); -static t_stat machine_instruction (HP_WORD IR, t_bool iotrap, uint32 irq_pending, uint32 *idle_save); -static t_bool check_deferral (uint32 irq_sc); -static uint32 map_address (HP_WORD logical, int32 switches); -static t_bool mem_is_empty (uint32 starting_address); - - -/* Memory Expansion Unit local utility routine declarations */ - -static t_bool is_mapped (uint32 address); -static uint32 meu_map (HP_WORD address, uint32 map, HP_WORD prot); +static t_stat machine_instruction (t_bool int_ack, uint32 *idle_save); +static t_bool reenable_interrupts (void); /* CPU SCP data structures */ @@ -1131,38 +1263,41 @@ static uint32 meu_map (HP_WORD address, uint32 map, HP_WORD prot); /* Device information blocks */ -static DIB cpu_dib = { /* CPU select code 0 */ - &cpuio, /* device interface */ - CPU, /* select code */ - 0 /* card index */ +static DIB cpu_dib = { /* CPU (select code 0) */ + &cpu_interface, /* the device's I/O interface function pointer */ + CPU, /* the device's select code (02-77) */ + 0, /* the card index */ + NULL, /* the card description */ + NULL /* the ROM description */ }; -static DIB ovfl_dib = { /* Overflow select code 1 */ - &ovflio, /* device interface */ - OVF, /* select code */ - 0 /* card index */ +static DIB ovfl_dib = { /* Overflow (select code 1) */ + &ovf_interface, /* the device's I/O interface function pointer */ + OVF, /* the device's select code (02-77) */ + 0, /* the card index */ + NULL, /* the card description */ + NULL /* the ROM description */ }; -static DIB pwrf_dib = { /* Power Fail select code 4 */ - &pwrfio, /* device interface */ - PWR, /* select code */ - 0 /* card index */ +static DIB pwrf_dib = { /* Power Fail (select code 4) */ + &pwr_interface, /* the device's I/O interface function pointer */ + PWR, /* the device's select code (02-77) */ + 0, /* the card index */ + NULL, /* the card description */ + NULL /* the ROM description */ }; /* Unit list. - The CPU unit holds the main memory capacity. - - - Implementation notes: - - 1. The unit structure must be global for other modules to access the unit - flags, which describe the installed options, and to obtain the memory - size via the MEMSIZE macro, which references the "capac" field. + The CPU unit "capac" field is set to the current main memory capacity in + 16-bit words by the "set_size" utility routine. The CPU does not use a unit + event service routine. */ -UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK, 0) }; +static UNIT cpu_unit [] = { + { UDATA (NULL, UNIT_FIX | UNIT_BINK, 0) } + }; /* Register list. @@ -1178,49 +1313,51 @@ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK, 0) }; 2. The REG_X flag indicates that the register may be displayed in symbolic form. + + 3. The T register cannot be modified. To change a memory location value, + the DEPOSIT CPU command must be used. */ static REG cpu_reg [] = { -/* Macro Name Location Radix Width Offset Depth Flags */ -/* ------ --------- ------------------ ----- ----- -------- ----------------- ----------------- */ - { ORDATA (P, PR, 15) }, - { ORDATA (A, AR, 16), REG_X }, - { ORDATA (B, BR, 16), REG_X }, - { ORDATA (M, MR, 15) }, - { ORDATA (T, TR, 16), REG_RO | REG_X }, - { ORDATA (X, XR, 16), REG_X }, - { ORDATA (Y, YR, 16), REG_X }, - { ORDATA (S, SR, 16), REG_X }, - { FLDATA (E, E, 0) }, - { FLDATA (O, O, 0) }, - { ORDATA (CIR, CIR, 6) }, +/* Macro Name Location Radix Width Offset Depth Flags */ +/* ------ ------- ------------------ ----- ----- -------- ----------------- ----------------- */ + { ORDATA (P, PR, 15) }, + { ORDATA (A, AR, 16), REG_X }, + { ORDATA (B, BR, 16), REG_X }, + { ORDATA (M, MR, 15) }, + { ORDATA (T, TR, 16), REG_RO | REG_X }, + { ORDATA (X, XR, 16), REG_X }, + { ORDATA (Y, YR, 16), REG_X }, + { ORDATA (S, SR, 16), REG_X }, + { FLDATA (E, E, 0) }, + { FLDATA (O, O, 0) }, + { ORDATA (CIR, CIR, 6) }, - { FLDATA (ION, ion, 0) }, - { FLDATA (ION_DEFER, ion_defer, 0) }, - { FLDATA (DMSENB, dms_enb, 0) }, - { FLDATA (DMSCUR, dms_ump, VA_N_PAG) }, + { FLDATA (INTSYS, interrupt_system, 0) }, + { FLDATA (INTEN, cpu_interrupt_enable, 0) }, - { ORDATA (DMSSR, dms_sr, 16) }, - { ORDATA (DMSVR, dms_vr, 16) }, - { BRDATA (DMSMAP, dms_map, 8, 16, MAP_NUM * MAP_LNT) }, + { ORDATA (IOPSP, SPR, 16) }, + { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_CIRC | REG_RO }, - { ORDATA (IOPSP, iop_sp, 16) }, - { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_CIRC | REG_RO }, + { ORDATA (IR, IR, 16), REG_HRO }, + { ORDATA (SAVEDMR, saved_MR, 32), REG_HRO }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, - { ORDATA (IR, IR, 16), REG_HRO }, - { ORDATA (PCQP, pcq_p, 6), REG_HRO }, - { ORDATA (JSBPLB, jsb_plb, 32), REG_HRO }, - { ORDATA (SAVEDMR, saved_MR, 32), REG_HRO }, - { ORDATA (FWANXM, fwanxm, 32), REG_HRO }, - { ORDATA (CONFIG, cpu_configuration, 32), REG_HRO }, + { ORDATA (EMASK, exec_mask, 16), REG_HRO }, + { ORDATA (EMATCH, exec_match, 16), REG_HRO }, + { DRDATA (ILIMIT, indirect_limit, 16), REG_HRO }, + { ORDATA (FWANXM, mem_end, 32), REG_HRO }, + { ORDATA (CONFIG, cpu_configuration, 32), REG_HRO }, + { BRDATA (ROMS, loader_rom, 8, 32, 4), REG_HRO }, - { ORDATA (WRU, sim_int_char, 8), REG_HRO }, - { ORDATA (BRK, sim_brk_char, 8), REG_HRO }, - { ORDATA (DEL, sim_del_char, 8), REG_HRO }, + { FLDATA (IS1000, is_1000, 0), REG_HRO }, + { ORDATA (INTREQ, interrupt_request, 6), REG_HRO }, + { ORDATA (LASTSC, last_select_code, 6), REG_HRO }, + + { ORDATA (WRU, sim_int_char, 8), REG_HRO }, + { ORDATA (BRK, sim_brk_char, 8), REG_HRO }, + { ORDATA (DEL, sim_del_char, 8), REG_HRO }, - { BRDATA (PRL, dev_prl, 8, 32, 2), REG_HRO }, - { BRDATA (IRQ, dev_irq, 8, 32, 2), REG_HRO }, - { BRDATA (SRQ, dev_srq, 8, 32, 2), REG_HRO }, { NULL } }; @@ -1234,7 +1371,11 @@ static REG cpu_reg [] = { the "HP 1000 Series Naming History" on the back inside cover of the Technical Reference Handbook. - 2. Each CPU option requires three modifiers. The two regular modifiers + 2. The string descriptors are used by the "show_model" routine to print the + CPU model numbers prior to appending "loader enabled" or "loader + disabled" to the report. + + 3. Each CPU option requires three modifiers. The two regular modifiers control the setting and printing of the option, while the extended modifier controls clearing the option. The latter is necessary because the option must be checked before confirming the change, and so the @@ -1242,68 +1383,68 @@ static REG cpu_reg [] = { */ static MTAB cpu_mod [] = { -/* Mask Value Match Value Print String Match String Validation Display Descriptor */ -/* --------------- ----------- ------------ ------------ ------------- ----------- ----------------- */ - { UNIT_MODEL_MASK, UNIT_2116, "", "2116", &set_model, &show_model, (void *) "2116" }, - { UNIT_MODEL_MASK, UNIT_2115, "", "2115", &set_model, &show_model, (void *) "2115" }, - { UNIT_MODEL_MASK, UNIT_2114, "", "2114", &set_model, &show_model, (void *) "2114" }, - { UNIT_MODEL_MASK, UNIT_2100, "", "2100", &set_model, &show_model, (void *) "2100" }, - { UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &set_model, &show_model, (void *) "1000-E" }, - { UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &set_model, &show_model, (void *) "1000-M" }, +/* Mask Value Match Value Print String Match String Validation Display Descriptor */ +/* ---------------- ----------- ------------ ------------ ------------- ----------- ----------------- */ + { UNIT_MODEL_FIELD, UNIT_2116, "", "2116", &set_model, &show_model, (void *) "2116" }, + { UNIT_MODEL_FIELD, UNIT_2115, "", "2115", &set_model, &show_model, (void *) "2115" }, + { UNIT_MODEL_FIELD, UNIT_2114, "", "2114", &set_model, &show_model, (void *) "2114" }, + { UNIT_MODEL_FIELD, UNIT_2100, "", "2100", &set_model, &show_model, (void *) "2100" }, + { UNIT_MODEL_FIELD, UNIT_1000_E, "", "1000-E", &set_model, &show_model, (void *) "1000-E" }, + { UNIT_MODEL_FIELD, UNIT_1000_M, "", "1000-M", &set_model, &show_model, (void *) "1000-M" }, #if defined (HAVE_INT64) - { UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &set_model, &show_model, (void *) "1000-F" }, + { UNIT_MODEL_FIELD, UNIT_1000_F, "", "1000-F", &set_model, &show_model, (void *) "1000-F" }, #endif - { UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &set_model, &show_model, (void *) "1000-M" }, - { UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &set_model, &show_model, (void *) "1000-E" }, + { UNIT_MODEL_FIELD, UNIT_1000_M, NULL, "21MX-M", &set_model, &show_model, (void *) "1000-M" }, + { UNIT_MODEL_FIELD, UNIT_1000_E, NULL, "21MX-E", &set_model, &show_model, (void *) "1000-E" }, - { UNIT_EAU, UNIT_EAU, "EAU", "EAU", &set_option, NULL, NULL }, - { UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_EAU, NULL, "NOEAU", &clear_option, NULL, NULL }, + { UNIT_EAU, UNIT_EAU, "EAU", "EAU", &set_option, NULL, NULL }, + { UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_EAU, NULL, "NOEAU", &clear_option, NULL, NULL }, - { UNIT_FP, UNIT_FP, "FP", "FP", &set_option, NULL, NULL }, - { UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_FP, NULL, "NOFP", &clear_option, NULL, NULL }, + { UNIT_FP, UNIT_FP, "FP", "FP", &set_option, NULL, NULL }, + { UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_FP, NULL, "NOFP", &clear_option, NULL, NULL }, - { UNIT_IOP, UNIT_IOP, "IOP", "IOP", &set_option, NULL, NULL }, - { UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_IOP, NULL, "NOIOP", &clear_option, NULL, NULL }, + { UNIT_IOP, UNIT_IOP, "IOP", "IOP", &set_option, NULL, NULL }, + { UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_IOP, NULL, "NOIOP", &clear_option, NULL, NULL }, - { UNIT_DMS, UNIT_DMS, "DMS", "DMS", &set_option, NULL, NULL }, - { UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_DMS, NULL, "NODMS", &clear_option, NULL, NULL }, + { UNIT_DMS, UNIT_DMS, "DMS", "DMS", &set_option, NULL, NULL }, + { UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_DMS, NULL, "NODMS", &clear_option, NULL, NULL }, - { UNIT_FFP, UNIT_FFP, "FFP", "FFP", &set_option, NULL, NULL }, - { UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_FFP, NULL, "NOFFP", &clear_option, NULL, NULL }, + { UNIT_FFP, UNIT_FFP, "FFP", "FFP", &set_option, NULL, NULL }, + { UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_FFP, NULL, "NOFFP", &clear_option, NULL, NULL }, - { UNIT_DBI, UNIT_DBI, "DBI", "DBI", &set_option, NULL, NULL }, - { UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_DBI, NULL, "NODBI", &clear_option, NULL, NULL }, + { UNIT_DBI, UNIT_DBI, "DBI", "DBI", &set_option, NULL, NULL }, + { UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_DBI, NULL, "NODBI", &clear_option, NULL, NULL }, - { UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &set_option, NULL, NULL }, - { MTAB_XDV, UNIT_EMA, NULL, "NOEMA", &clear_option, NULL, NULL }, + { UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &set_option, NULL, NULL }, + { MTAB_XDV, UNIT_EMA, NULL, "NOEMA", &clear_option, NULL, NULL }, - { UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &set_option, NULL, NULL }, - { MTAB_XDV, UNIT_VMAOS, NULL, "NOVMA", &clear_option, NULL, NULL }, + { UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &set_option, NULL, NULL }, + { MTAB_XDV, UNIT_VMAOS, NULL, "NOVMA", &clear_option, NULL, NULL }, - { UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &set_option, NULL, NULL }, + { UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &set_option, NULL, NULL }, #if defined (HAVE_INT64) - { UNIT_VIS, UNIT_VIS, "VIS", "VIS", &set_option, NULL, NULL }, - { UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_VIS, NULL, "NOVIS", &clear_option, NULL, NULL }, + { UNIT_VIS, UNIT_VIS, "VIS", "VIS", &set_option, NULL, NULL }, + { UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_VIS, NULL, "NOVIS", &clear_option, NULL, NULL }, - { UNIT_SIGNAL, UNIT_SIGNAL, "SIGNAL", "SIGNAL", &set_option, NULL, NULL }, - { UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &clear_option, NULL, NULL }, + { UNIT_SIGNAL, UNIT_SIGNAL, "SIGNAL", "SIGNAL", &set_option, NULL, NULL }, + { UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &clear_option, NULL, NULL }, #endif /* Future microcode support. - { UNIT_DS, UNIT_DS, "DS", "DS", &set_option, NULL, NULL }, - { UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL }, - { MTAB_XDV, UNIT_DS, NULL, "NODS", &clear_option, NULL, NULL }, + { UNIT_DS, UNIT_DS, "DS", "DS", &set_option, NULL, NULL }, + { UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL }, + { MTAB_XDV, UNIT_DS, NULL, "NODS", &clear_option, NULL, NULL }, */ /* Entry Flags Value Print String Match String Validation Display Descriptor */ @@ -1327,6 +1468,7 @@ static MTAB cpu_mod [] = { { MTAB_XDV, 1024 * 1024, NULL, "1024K", &set_size, NULL, NULL }, { MTAB_XDV | MTAB_NMO, 0, "ROMS", "ROMS", &set_roms, &show_roms, NULL }, + { MTAB_XDV | MTAB_NMO, 0, "IOCAGE", NULL, NULL, &show_cage, NULL }, { MTAB_XDV | MTAB_NMO, 1, "STOPS", "STOP", &set_stops, &show_stops, NULL }, { MTAB_XDV, 0, NULL, "NOSTOP", &set_stops, NULL, NULL }, @@ -1341,7 +1483,7 @@ static MTAB cpu_mod [] = { }; -/* Debugging trace list */ +/* Trace list */ static DEBTAB cpu_deb [] = { { "INSTR", TRACE_INSTR }, /* trace instruction executions */ @@ -1410,12 +1552,12 @@ static STOPTAB 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 */ 8, /* address radix */ - PA_N_SIZE, /* address width */ + PA_WIDTH, /* address width */ 1, /* address increment */ 8, /* data radix */ 16, /* data width */ @@ -1435,427 +1577,174 @@ DEVICE cpu_dev = { -/* Memory program constants */ +/* I/O subsystem local data structures */ -static const char map_indicator [] = { /* MEU map indicator, indexed by map type */ - 'S', /* System */ - 'U', /* User */ - 'A', /* Port_A */ - 'B' /* Port_B */ - }; +/* Signal names */ -/* Memory global data structures */ - - -/* Memory access classification table */ - -typedef struct { - uint32 debug_flag; /* the debug flag for tracing */ - const char *name; /* the classification name */ - } ACCESS_PROPERTIES; - -static const ACCESS_PROPERTIES mem_access [] = { /* indexed by ACCESS_CLASS */ -/* debug_flag name */ -/* ------------ ------------------- */ - { TRACE_FETCH, "instruction fetch" }, /* instruction fetch */ - { TRACE_DATA, "data" }, /* data access */ - { TRACE_DATA, "data" }, /* data access, alternate map */ - { TRACE_DATA, "unprotected" }, /* data access, system map */ - { TRACE_DATA, "unprotected" }, /* data access, user map */ - { TRACE_DATA, "dma" }, /* DMA channel 1, port A map */ - { TRACE_DATA, "dma" } /* DMA channel 2, port B map */ - }; - - - -/* DMA program constants */ - -#define DMA_CHAN_COUNT 2 /* number of DMA channels */ - -#define DMA_OE 020000000000u /* byte packing odd/even flag */ -#define DMA1_STC 0100000u /* DMA - issue STC */ -#define DMA1_PB 0040000u /* DMA - pack bytes */ -#define DMA1_CLC 0020000u /* DMA - issue CLC */ -#define DMA2_OI 0100000u /* DMA - output/input */ - -typedef enum { ch1, ch2 } CHANNEL; /* channel number */ - -#define DMA_1_REQ (1 << ch1) /* channel 1 request */ -#define DMA_2_REQ (1 << ch2) /* channel 2 request */ - -typedef struct { - FLIP_FLOP control; /* control flip-flop */ - FLIP_FLOP flag; /* flag flip-flop */ - FLIP_FLOP flagbuf; /* flag buffer flip-flop */ - FLIP_FLOP xferen; /* transfer enable flip-flop */ - FLIP_FLOP select; /* register select flip-flop */ - - HP_WORD cw1; /* device select */ - HP_WORD cw2; /* direction, address */ - HP_WORD cw3; /* word count */ - uint32 packer; /* byte-packer holding reg */ - } DMA_STATE; - - -/* DMA global state */ - -DMA_STATE dma [DMA_CHAN_COUNT]; /* per-channel state */ - - -/* DMA local data structures */ - -static const BITSET_NAME dma_cw1_names [] = { /* DMA control word 1 names */ - "STC", /* bit 15 */ - "byte packing", /* bit 14 */ - "CLC" /* bit 13 */ - }; - -static const BITSET_FORMAT dma_cw1_format = /* names, offset, direction, alternates, bar */ - { FMT_INIT (dma_cw1_names, 13, msb_first, no_alt, append_bar) }; - - -/* DMA local SCP support routine declarations */ - -static IOHANDLER dmapio; -static IOHANDLER dmasio; -static t_stat dma_reset (DEVICE *dptr); - - -/* DMA local utility routine declarations */ - -static t_stat dma_cycle (CHANNEL chan, ACCESS_CLASS class); -static uint32 calc_dma (void); - - -/* DMA SCP data structures */ - - -/* Device information blocks */ - -static DIB dmap1_dib = { - &dmapio, /* device interface */ - DMA1, /* select code */ - ch1 /* card index */ - }; - -static DIB dmas1_dib = { - &dmasio, /* device interface */ - DMALT1, /* select code */ - ch1 /* card index */ - }; - -static DIB dmap2_dib = { - &dmapio, /* device interface */ - DMA2, /* select code */ - ch2 /* card index */ - }; - -static DIB dmas2_dib = { - &dmasio, /* device interface */ - DMALT2, /* select code */ - ch2 /* card index */ - }; - - -/* Unit lists */ - -static UNIT dma1_unit = { UDATA (NULL, 0, 0) }; - -static UNIT dma2_unit = { UDATA (NULL, 0, 0) }; - - -/* Register lists */ - -static REG dma1_reg [] = { -/* Macro Name Location Width Flags */ -/* ------ ------- ------------------ ----- ----- */ - { FLDATA (XFR, dma [ch1].xferen, 0) }, - { FLDATA (CTL, dma [ch1].control, 0) }, - { FLDATA (FLG, dma [ch1].flag, 0) }, - { FLDATA (FBF, dma [ch1].flagbuf, 0) }, - { FLDATA (CTL2, dma [ch1].select, 0) }, - { ORDATA (CW1, dma [ch1].cw1, 16) }, - { ORDATA (CW2, dma [ch1].cw2, 16) }, - { ORDATA (CW3, dma [ch1].cw3, 16) }, - { FLDATA (BYTE, dma [ch1].packer, 31) }, - { ORDATA (PACKER, dma [ch1].packer, 8), REG_A }, - { NULL } - }; - -static REG dma2_reg [] = { -/* Macro Name Location Width Flags */ -/* ------ ------- ------------------ ----- ----- */ - { FLDATA (XFR, dma [ch2].xferen, 0) }, - { FLDATA (CTL, dma [ch2].control, 0) }, - { FLDATA (FLG, dma [ch2].flag, 0) }, - { FLDATA (FBF, dma [ch2].flagbuf, 0) }, - { FLDATA (CTL2, dma [ch2].select, 0) }, - { ORDATA (CW1, dma [ch2].cw1, 16) }, - { ORDATA (CW2, dma [ch2].cw2, 16) }, - { ORDATA (CW3, dma [ch2].cw3, 16) }, - { FLDATA (BYTE, dma [ch2].packer, 31) }, - { ORDATA (PACKER, dma [ch2].packer, 8), REG_A }, - { NULL } - }; - - -/* Debugging trace list */ - -static DEBTAB dma_deb [] = { - { "CMD", TRACE_CMD }, /* trace interface or controller commands */ - { "CSRW", TRACE_CSRW }, /* trace interface control, status, read, and write actions */ - { "SR", TRACE_SR }, /* trace service requests received */ - { "DATA", TRACE_DATA }, /* trace memory data accesses */ - { "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */ - { NULL, 0 } - }; - - -/* Device descriptors */ - -DEVICE dma1_dev = { - "DMA1", /* device name */ - &dma1_unit, /* unit array */ - dma1_reg, /* register array */ - NULL, /* modifier array */ - 1, /* number of units */ - 8, /* address radix */ - 1, /* address width */ - 1, /* address increment */ - 8, /* data radix */ - 16, /* data width */ - NULL, /* examine routine */ - NULL, /* deposit routine */ - &dma_reset, /* reset routine */ - NULL, /* boot routine */ - NULL, /* attach routine */ - NULL, /* detach routine */ - &dmap1_dib, /* device information block pointer */ - DEV_DISABLE | DEV_DEBUG, /* device flags */ - 0, /* debug control flags */ - dma_deb, /* debug flag name table */ - NULL, /* memory size change routine */ - NULL /* logical device name */ - }; - -DEVICE dma2_dev = { - "DMA2", /* device name */ - &dma2_unit, /* unit array */ - dma2_reg, /* register array */ - NULL, /* modifier array */ - 1, /* number of units */ - 8, /* address radix */ - 1, /* address width */ - 1, /* address increment */ - 8, /* data radix */ - 16, /* data width */ - NULL, /* examine routine */ - NULL, /* deposit routine */ - &dma_reset, /* reset routine */ - NULL, /* boot routine */ - NULL, /* attach routine */ - NULL, /* detach routine */ - &dmap2_dib, /* device information block pointer */ - DEV_DISABLE | DEV_DEBUG, /* device flags */ - 0, /* debug control flags */ - dma_deb, /* debug flag name table */ - NULL, /* memory size change routine */ - NULL /* logical device name */ - }; - -static DEVICE *dma_dptrs [] = { - &dma1_dev, - &dma2_dev - }; - - - -/* Memory Protect program constants */ - -#define UNIT_V_MP_JSB (UNIT_V_UF + 0) /* MP jumper W5 */ -#define UNIT_V_MP_INT (UNIT_V_UF + 1) /* MP jumper W6 */ -#define UNIT_V_MP_SEL1 (UNIT_V_UF + 2) /* MP jumper W7 */ -#define UNIT_MP_JSB (1 << UNIT_V_MP_JSB) /* 1 = W5 is out */ -#define UNIT_MP_INT (1 << UNIT_V_MP_INT) /* 1 = W6 is out */ -#define UNIT_MP_SEL1 (1 << UNIT_V_MP_SEL1) /* 1 = W7 is out */ - -#define MP_TEST(va) (mp_control && ((va) >= 2) && ((va) < mp_fence)) - - -/* Memory Protect global state */ - -FLIP_FLOP mp_control = CLEAR; /* MP control flip-flop */ -FLIP_FLOP mp_mevff = CLEAR; /* memory expansion violation flip-flop */ -HP_WORD mp_fence = 0; /* MP fence register */ -HP_WORD mp_viol = 0; /* MP violation register */ -HP_WORD iop_sp = 0; /* iop stack reg */ -HP_WORD err_PC = 0; /* error PC */ - -jmp_buf save_env; /* MP abort handler */ -t_bool mp_mem_changed; /* TRUE if the MP or MEM registers have been altered */ - - -/* Memory Protect local state */ - -static FLIP_FLOP mp_flag = CLEAR; /* MP flag flip-flop */ -static FLIP_FLOP mp_flagbuf = CLEAR; /* MP flag buffer flip-flop */ -static FLIP_FLOP mp_evrff = SET; /* enable violation register flip-flop */ -static char meu_indicator; /* last map access indicator (S | U | A | B | -) */ -static uint32 meu_page; /* last physical page number accessed */ - - -/* Memory Protect local SCP support routine declarations */ - -static IOHANDLER protio; -static t_stat mp_reset (DEVICE *dptr); - - -/* Memory Protect SCP data structures */ - - -/* Device information block */ - -static DIB mp_dib = { - &protio, /* device interface */ - PRO, /* select code */ - 0 /* card index */ - }; - - -/* Unit list. - - - Implementation notes: - - 1. The default flags correspond to the following jumper settings: JSB in, - INT in, SEL1 out. -*/ - -static UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) }; - - -/* Register list */ - -static REG mp_reg [] = { -/* Macro Name Location Width */ -/* ------ ---- ----------- ----- */ - { FLDATA (CTL, mp_control, 0) }, - { FLDATA (FLG, mp_flag, 0) }, - { FLDATA (FBF, mp_flagbuf, 0) }, - { ORDATA (FR, mp_fence, 15) }, - { ORDATA (VR, mp_viol, 16) }, - { FLDATA (EVR, mp_evrff, 0) }, - { FLDATA (MEV, mp_mevff, 0) }, - { NULL } - }; - - -/* Modifier list */ - -static MTAB mp_mod [] = { -/* Mask Value Match Value Print String Match String Validation Display Descriptor */ -/* ------------- ------------ --------------- ------------ ---------- ------- ---------- */ - { UNIT_MP_JSB, UNIT_MP_JSB, "JSB (W5) out", "JSBOUT", NULL, NULL, NULL }, - { UNIT_MP_JSB, 0, "JSB (W5) in", "JSBIN", NULL, NULL, NULL }, - { UNIT_MP_INT, UNIT_MP_INT, "INT (W6) out", "INTOUT", NULL, NULL, NULL }, - { UNIT_MP_INT, 0, "INT (W6) in", "INTIN", NULL, NULL, NULL }, - { UNIT_MP_SEL1, UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT", NULL, NULL, NULL }, - { UNIT_MP_SEL1, 0, "SEL1 (W7) in", "SEL1IN", NULL, NULL, NULL }, - { 0 } - }; - - -/* Device descriptor */ - -DEVICE mp_dev = { - "MP", /* device name */ - &mp_unit, /* unit array */ - mp_reg, /* register array */ - mp_mod, /* modifier array */ - 1, /* number of units */ - 8, /* address radix */ - 1, /* address width */ - 1, /* address increment */ - 8, /* data radix */ - 16, /* data width */ - NULL, /* examine routine */ - NULL, /* deposit routine */ - &mp_reset, /* reset routine */ - NULL, /* boot routine */ - NULL, /* attach routine */ - NULL, /* detach routine */ - &mp_dib, /* device information block pointer */ - DEV_DISABLE | DEV_DIS, /* device flags */ - 0, /* debug control flags */ - NULL, /* debug flag name table */ - NULL, /* memory size change routine */ - NULL /* logical device name */ - }; - - - -/* I/O system program constants */ - -static const BITSET_NAME inbound_names [] = { /* Inbound signal names, in IOSIGNAL order */ +static const BITSET_NAME inbound_names [] = { /* Inbound signal names, in INBOUND_SIGNAL order */ "PON", /* 000000000001 */ - "ENF", /* 000000000002 */ - "IOI", /* 000000000004 */ - "IOO", /* 000000000010 */ - "SFS", /* 000000000020 */ - "SFC", /* 000000000040 */ - "STC", /* 000000000100 */ - "CLC", /* 000000000200 */ - "STF", /* 000000000400 */ - "CLF", /* 000000001000 */ - "EDT", /* 000000002000 */ - "CRS", /* 000000004000 */ - "POPIO", /* 000000010000 */ - "IAK", /* 000000020000 */ - "SIR" /* 000000040000 */ + "IOI", /* 000000000002 */ + "IOO", /* 000000000004 */ + "SFS", /* 000000000010 */ + "SFC", /* 000000000020 */ + "STC", /* 000000000040 */ + "CLC", /* 000000000100 */ + "STF", /* 000000000200 */ + "CLF", /* 000000000400 */ + "EDT", /* 000000001000 */ + "CRS", /* 000000002000 */ + "POPIO", /* 000000004000 */ + "IAK", /* 000000010000 */ + "ENF", /* 000000020000 */ + "SIR", /* 000000040000 */ + "IEN", /* 000000100000 */ + "PRH" /* 000000200000 */ }; static const BITSET_FORMAT inbound_format = /* names, offset, direction, alternates, bar */ { FMT_INIT (inbound_names, 0, lsb_first, no_alt, no_bar) }; -static const BITSET_NAME outbound_names [] = { /* Outbound signal names, in IOSIGNAL order */ - "SKF" /* 000000200000 */ +static const BITSET_NAME outbound_names [] = { /* Outbound signal names, in OUTBOUND_SIGNAL order */ + "SKF", /* 000000000001 */ + "PRL", /* 000000000002 */ + "FLG", /* 000000000004 */ + "IRQ", /* 000000000010 */ + "SRQ" /* 000000000020 */ }; static const BITSET_FORMAT outbound_format = /* names, offset, direction, alternates, bar */ - { FMT_INIT (outbound_names, 16, lsb_first, no_alt, no_bar) }; + { FMT_INIT (outbound_names, 0, lsb_first, no_alt, no_bar) }; -/* I/O instruction sub-opcodes */ +/* I/O signal tables. -#define soHLT 0 /* halt */ -#define soFLG 1 /* set/clear flag */ -#define soSFC 2 /* skip on flag clear */ -#define soSFS 3 /* skip on flag set */ -#define soMIX 4 /* merge into A/B */ -#define soLIX 5 /* load into A/B */ -#define soOTX 6 /* output from A/B */ -#define soCTL 7 /* set/clear control */ + These tables contain the set of I/O signals that are appropriate for each I/O + operation. Two tables are defined. + The first table defines the backplane signals generated by each I/O Group + operation. A signal set is sent to the device interface associated with the + select code specified in an IOG instruction to direct the operation of the + interface. Backplane signals map closely to IOG instructions. For example, + the SFS instruction asserts the SFS backplane signal to the interface card. -/* I/O system local data structures */ + The hardware ENF and SIR signals are periodic. In simulation, they are + asserted only when the flag buffer or the flag and control flip-flops are + changed, respectively. -static DIB *dibs [MAXDEV + 1] = { /* index by select code for I/O instruction dispatch */ - &cpu_dib, /* select code 00 = interrupt system */ - &ovfl_dib /* select code 01 = overflow register */ + The second table defines the signals generated in addition to the explicitly + asserted signal. Specifically, asserting PON also asserts POPIO and CRS, + and asserting POPIO also asserts CRS. The ENF and SIR signals are asserted + when necessary as described above. +*/ + +static const INBOUND_SET control_set [] = { /* indexed by IO_GROUP_OP */ + ioNONE, /* iog_HLT */ + ioSTF | ioENF | ioSIR, /* iog_STF */ + ioSFC, /* iog_SFC */ + ioSFS, /* iog_SFS */ + ioIOI, /* iog_MIx */ + ioIOI, /* iog_LIx */ + ioIOO, /* iog_OTx */ + ioSTC | ioSIR, /* iog_STC */ + + ioNONE | ioCLF | ioSIR, /* iog_HLT_C */ + ioCLF | ioSIR, /* iog_CLF */ + ioSFC | ioCLF | ioSIR, /* iog_SFC_C */ + ioSFS | ioCLF | ioSIR, /* iog_SFS_C */ + ioIOI | ioCLF | ioSIR, /* iog_MIx_C */ + ioIOI | ioCLF | ioSIR, /* iog_LIx_C */ + ioIOO | ioCLF | ioSIR, /* iog_OTx_C */ + ioSTC | ioCLF | ioSIR, /* iog_STC_C */ + + ioCLC | ioSIR, /* iog_CLC */ + ioCLC | ioCLF | ioSIR /* iog_CLC_C */ }; -static DEVICE *devs [MAXDEV + 1] = { /* index by select code for I/O dispatch tracing */ - &cpu_dev, /* select code 00 = interrupt system */ - &cpu_dev /* select code 01 = overflow register */ +static const INBOUND_SET assert_set [] = { /* indexed by IO_ASSERTION */ + ioENF | ioSIR, /* ioa_ENF */ + ioSIR, /* ioa_SIR */ + ioPON | ioPOPIO | ioCRS | ioENF | ioSIR, /* ioa_PON */ + ioPOPIO | ioCRS | ioENF | ioSIR, /* ioa_POPIO */ + ioCRS | ioENF | ioSIR, /* ioa_CRS */ + ioIAK | ioSIR /* ioa_IAK */ }; -/* I/O system local utility routine declarations */ +/* Interrupt enable table. -static void io_initialize (void); -static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD data); + I/O Group instructions that alter the interrupt priority chain must delay + recognition of interrupts until the following instruction completes. The HP + 1000 microcode does this by executing an IOFF micro-order to clear the + interrupt enable (INTEN) flip-flop. The table below gives the INTEN state + for each I/O instruction; CLEAR corresponds to an IOFF micro-order, while SET + corresponds to ION. The table is also indexed by the "is_1000" selector, as + the disable rules are different for the 21xx and 1000 machines. +*/ + +static const t_bool enable_map [2] [18] = { /* interrupt enable table, indexed by is_1000 and IO_GROUP_OP */ +/* HLT STF SFC SFS MIx LIx OTx STC */ +/* HLT_C CLF SFC_C SFS_C MIx_C LIx_C OTx_C STC_C */ +/* CLC CLC_C */ +/* ----- ----- ----- ----- ----- ----- ----- ----- */ + { CLEAR, CLEAR, SET, SET, SET, SET, SET, CLEAR, /* 21xx */ + CLEAR, CLEAR, SET, SET, SET, SET, SET, CLEAR, /* 21xx */ + CLEAR, CLEAR }, + + { CLEAR, CLEAR, CLEAR, CLEAR, SET, SET, SET, CLEAR, /* 1000 */ + CLEAR, CLEAR, CLEAR, CLEAR, SET, SET, SET, CLEAR, /* 1000 */ + CLEAR, CLEAR } + }; + + +/* I/O access table. + + I/O signals are directed to specific interface cards by specifying their + locations in the I/O card cage. Each location is assigned a number, called a + select code, that is specified by I/O Group instructions to indicate the + interface card to activate. + + In simulation, the select code corresponding to each interface is stored in + the corresponding Device Information Block (DIB). To avoid having to scan + the device list each time an I/O instruction is executed, an I/O access table + is filled in as part of I/O initialization in the instruction execution + prelude. The table is indexed by select code (00-77 octal) and contains + pointers to the device and DIB structures associated with each index. + + Initialization is performed during each "sim_instr" call, as the select code + assignments may have been changed by the user at the SCP prompt. + + + Implementation notes: + + 1. The entries for select codes 0 and 1 (the CPU and Overflow Register, + respectively) are initialized here, as they are always present (i.e., + cannot be disabled) and cannot change. + + 2. The table contains constant pointers, but "const" cannot be used here, as + "hp_trace" calls "sim_dname", which takes a variable device pointer even + though it does not change anything. + + 3. The "references" entries are used only during table initialization to + ensure that each select code is referenced by only one device. +*/ + +typedef struct { /* I/O access table entry */ + DEVICE *devptr; /* a pointer to the DEVICE structure */ + DIB *dibptr; /* a pointer to the DIB structure */ + uint32 references; /* a count of the references to this select code */ + } IO_TABLE; + +static IO_TABLE iot [SC_MAX + 1] = { /* index by select code for I/O instruction dispatch */ + { &cpu_dev, &cpu_dib, 0 }, /* select code 00 = interrupt system */ + { &cpu_dev, &ovfl_dib, 0 } /* select code 01 = overflow register */ + }; + + +/* I/O subsystem local utility routine declarations */ + +static t_bool initialize_io (t_bool is_executing); @@ -1870,7 +1759,7 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat the status to be returned is set to a value other than SCPE_OK. On entry, P points to the instruction to execute, and the "sim_switches" - global contains any command-line switches included with the run command. On + global contains any command-line switches included with the RUN command. On exit, P points at the next instruction to execute. Execution is divided into four phases. @@ -1879,16 +1768,16 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat execution. This involves verifying that there are no device conflicts (e.g., two devices with the same select code) and initializing the I/O state. These actions accommodate reconfiguration of the I/O device settings and program - counter while the simulator was stopped. The prelude also picks up the - time-base generator's select code for use in idle testing, and it checks for - one command-line switch: if "-B" is specified, the current set of simulation - stop conditions is bypassed for the first instruction executed. + counter while the simulator was stopped. The prelude also checks for one + command-line switch: if "-B" is specified, the current set of simulation stop + conditions is bypassed for the first instruction executed. - Second, the memory protect abort mechanism is set up. MP aborts utilize the + Second, the memory protect option is initialized. MP aborts utilize the "setjmp/longjmp" mechanism to transfer control out of the instruction executors without returning through the call stack. This allows an instruction to be aborted part-way through execution when continuation is - impossible due to a memory access violation. + impossible due to a memory access violation. An MP abort returns to the main + instruction loop at the "setjmp" routine. Third, the instruction execution loop decodes instructions and calls the individual executors in turn until a condition occurs that prevents further @@ -1911,37 +1800,20 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat points correctly at the next instruction to execute upon resumption. - In hardware, if the Memory Protect accessory is installed and enabled, I/O - operations to select codes other than 01 are prohibited. Also, in - combination with the MPCK micro-order, MP validates the M-register contents - (memory address) against the memory protect fence. If a violation occurs, an - I/O instruction or memory write is inhibited, and a memory read returns - invalid data. - - In simulation, an instruction executor that detects an MP violation calls the - MP_ABORT macro, passing the violation address as the parameter. This - executes a "longjmp" to the abort handler, which is outside of and precedes - the instruction execution loop. The value passed to "longjmp" is a 32-bit - integer containing the logical address of the instruction causing the - violation. MP_ABORT should only be called if "mp_control" is SET, as aborts - do not occur if MP is turned off. - - An MP interrupt (SC 05) is qualified by "ion" but not by "ion_defer". If the - interrupt system is off when an MP violation is detected, the violating - instruction will be aborted, even though no interrupt occurs. In this case, - neither the flag nor the flag buffer are set, and EVR is not cleared. - - The instruction execution loop starts by checking for event timer expiration. - If one occurs, the associated event service routine is called, and if it was - successful, the DMA service requests and interrupt requests are recalculated. + If one occurs, the associated device's service routine is called by the + "sim_process_event" routine. Then a check for DMA service requests is made. + If a request is active, the "dma_service" routine is called to process it. DMA cycles are requested by an I/O card asserting its SRQ signal. If a DMA - channel is programmed to respond to that card's select code, a DMA cycle will - be initiated. A DMA cycle consists of a memory cycle and an I/O cycle. - These cycles are synchronized with the control processor on the 21xx CPUs. On - the 1000s, memory cycles are asynchronous, while I/O cycles are synchronous. - Memory cycle time is about 40% of the I/O cycle time. + channel is programmed to respond to that card's select code, the channel's + service request flag is set in the "dma_request_set". On each pass through + the instruction execution loop, "dma_request_set" is checked; if it is + non-zero, a DMA cycle will be initiated. A DMA cycle consists of a memory + cycle and an I/O cycle. These cycles are synchronized with the control + processor on the 21xx CPUs. On the 1000s, memory cycles are asynchronous, + while I/O cycles are synchronous. Memory cycle time is about 40% of the I/O + cycle time. With properly designed interface cards, DMA is capable of taking consecutive I/O cycles. On all machines except the 1000 M-Series, a DMA cycle freezes @@ -1950,7 +1822,10 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat memory cycle. An interleaved memory cycle is allowed. Otherwise, the control processor is allowed to run. Therefore, during consecutive DMA cycles, the M-Series CPU will run until an IOG instruction is attempted, - whereas the other CPUs will freeze completely. + whereas the other CPUs will freeze completely. This is simulated by skipping + instruction execution if "dma_request_set" is still non-zero after servicing + the current request, i.e., if the device asserted SRQ again as a result of + the DMA cycle. All DMA cards except the 12607B provide two independent channels. If both channels are active simultaneously, channel 1 has priority for I/O cycles @@ -1962,50 +1837,62 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat channel 1 is asserted continuously when both channels are active, then no channel 2 cycles will occur until channel 1 completes. - Interrupt recognition is controlled by three state variables: "ion", - "ion_defer", and "intrq". "ion" corresponds to the INTSYS flip-flop in the - 1000 CPU, "ion_defer" corresponds to the INTEN flip-flop, and "intrq" - corresponds to the NRMINT flip-flop. STF 00 and CLF 00 set and clear INTSYS, - turning the interrupt system on and off. Micro-orders ION and IOFF set and - clear INTEN, deferring or allowing certain interrupts. An IRQ signal from a - device, qualified by the corresponding PRL signal, will set NRMINT to request - a normal interrupt; an IOFF or IAK will clear it. + After DMA servicing, a check for pending interrupt requests is made. - Under simulation, "ion" is controlled by STF/CLF 00. "ion_defer" is set or - cleared as appropriate by the individual instruction simulators. "intrq" is - set to the successfully interrupting device's select code, or to zero if - there is no qualifying interrupt request. + Interrupt recognition in the HP 1000 CPU is controlled by three state + variables: "interrupt_system", "cpu_interrupt_enable", and + "interrupt_request". "interrupt_system" corresponds to the INTSYS flip-flop + in the 1000 CPU, "cpu_interrupt_enable" corresponds to the INTEN flip-flop, + and "interrupt_request" corresponds to the NRMINT flip-flop. STF 00 and CLF + 00 set and clear INTSYS, turning the interrupt system on and off. Microcode + instructions ION and IOFF set and clear INTEN, enabling or disabling certain + interrupts. An IRQ signal from a device, qualified by the corresponding PRH + and IEN signals, will set NRMINT to request a normal interrupt; an IOFF or + IAK will clear it. - Presuming PRL is set to allow priority to an interrupting device: + Under simulation, "interrupt_system" is controlled by STF/CLF 00. + "cpu_interrupt_enable" is set or cleared as appropriate by the individual + instruction simulators. "interrupt_request" is set to the successfully + interrupting device's select code, or to zero if there is no qualifying + interrupt request. - 1. Power fail (SC 04) may interrupt if "ion_defer" is clear; this is not - conditional on "ion" being set. + The rules controlling interrupt recognition are: - 2. Memory protect (SC 05) may interrupt if "ion" is set; this is not - conditional on "ion_defer" being clear. + 1. Power fail (SC 04) may interrupt if "cpu_interrupt_enable" is set; this + is not conditional on "interrupt_system" being set. + + 2. Memory protect (SC 05) may interrupt if "interrupt_system" is set; this + is not conditional on "cpu_interrupt_enable" being set. 3. Parity error (SC 05) may interrupt always; this is not conditional on - "ion" being set or "ion_defer" being clear. + either "interrupt_system" or "cpu_interrupt_enable" being set. - 4. All other devices (SC 06 and up) may interrupt if "ion" is set and - "ion_defer" is clear. + 4. All other devices (SC 06 and up) may interrupt only if both + "interrupt_system" and "cpu_interrupt_enable" are set. - Qualification with "ion" is performed by "calc_int", except for case 2, which - is qualified by the MP abort handler above (because qualification occurs on - the MP card, rather than in the CPU). Therefore, we need only qualify by - "ion_defer" here. + Qualification with "interrupt_system" is performed by the I/O dispatcher, + which asserts IEN to the device interface if the interrupt system is on. All + interfaces other than Power Fail or Parity Error assert IRQ only if IEN is + asserted. If IEN is denied, i.e., the interrupt system is off, then only + Power Fail and Parity Error will assert IRQ and thereby set the + "interrupt_request" value to their respective select codes. Therefore, we + need only qualify by "cpu_interrupt_enable" here. At instruction fetch time, a pending interrupt request will be deferred if the previous instruction was a JMP indirect, JSB indirect, STC, CLC, STF, - CLF, or was executing from an interrupt trap cell. In addition, the following - instructions will cause deferral on the 1000 series: SFS, SFC, JRS, DJP, DJS, - SJP, SJS, UJP, and UJS. + CLF, or, for a 1000-series CPU, an SFS, SFC, JRS, DJP, DJS, SJP, SJS, UJP, or + UJS. The executors for these instructions clear the "cpu_interrupt_enable" + flag, which is then set unilaterally when each instruction is dispatched. + The flag is also cleared by an interrupt acknowledgement, deferring + additional interrupts until after the instruction in the trap cell is + executed. - On the HP 1000, the request is always deferred until after the current - instruction completes. On the 21xx, the request is deferred unless the - current instruction is an MRG instruction other than JMP or JMP,I or JSB,I. - Note that for the 21xx, SFS and SFC are not included in the deferral - criteria. + On the HP 1000, an interrupt request is always deferred until after the + current instruction completes. On the 21xx, the request is deferred unless + the current instruction is an MRG instruction other than JMP or JMP,I or + JSB,I. Note that for the 21xx, SFS and SFC are not included in the deferral + criteria. In simulation, the "reenable_interrupts" routine is called to + handle this case. When a status other than SCPE_OK is returned from an instruction executor or @@ -2014,7 +1901,7 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat been changed by an active execution trace or idle trace suppression. This ensures that the simulation stop does not exit with the flags set improperly. If the simulation stopped for a programmed halt, the 21xx binary loader area - is protected in case it had been unprotected to run the loader. The DMS + is protected in case it had been unprotected to run the loader. The MEU status and violation registers and the program counter queue pointer are updated to present the proper values to the user interface. The default breakpoint type is updated to reflect the current MEU state (disabled, system @@ -2044,260 +1931,190 @@ static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD dat even though in both cases the values are reestablished after an abort before they are used. - 2. The protected lower bound address for the JSB instruction depends on the - W5 jumper setting. If W5 is in, then the lower bound is 2, allowing JSBs - to the A and B registers. If W5 is out, then the lower bound is 0, just - as with JMP. The protected lower bound is set during the instruction - prelude and tested during JSB address validation. + 2. The C standard requires that the "setjmp" call be made from a frame that + is still active (i.e., still on the stack) when "longjmp" is called. + Consequently, we must call "setjmp" from this routine rather than a + subordinate routine that will have exited (to return to this routine) + when the "longjmp" call is made. 3. The -P switch is removed from the set of command line switches to ensure that internal calls to the device reset routines are not interpreted as "power-on" resets. - 4. The "longjmp" handler is used both for MP and MEM violations. The MEV - flip-flop will be clear for the former and set for the latter. The MEV - violation register will be updated by "dms_upd_vr" only if the call is - NOT for an MEM violation; if it is, then the register has already been - set and should not be disturbed. - - 5. For an MP/MEM abort, the violation address is passed via "longjmp" to - enable the MEM violation register to be updated. The "longjmp" routine - will not pass a value of 0; it is converted internally to 1. This is OK, - because only the page number of the address value is used, and locations - 0 and 1 are both on page 0. - - 6. A CPU freeze is simulated by skipping instruction execution during the + 4. A CPU freeze is simulated by skipping instruction execution during the current loop cycle. - 7. If both DMA channels have SRQ asserted, priority is simulated by skipping - the channel 2 cycle if channel 1's SRQ is still asserted at the end of - its cycle. If it is not, then channel 2 steals the next cycle from the - CPU. - - 8. The 1000 M-Series allows some CPU processing concurrently with + 5. The 1000 M-Series allows some CPU processing concurrently with continuous DMA cycles, whereas all other CPUs freeze. The processor freezes if an I/O cycle is attempted, including an interrupt acknowledgement. Because some microcode extensions (e.g., Access IOP, RTE-6/VM OS) perform I/O cycles, advance detection of I/O cycles is difficult. Therefore, we freeze all processing for the M-Series as well. - 9. EXEC tracing is active when exec_save is non-zero. "exec_save" saves the + 6. EXEC tracing is active when exec_save is non-zero. "exec_save" saves the current state of the trace flags when an EXEC trace match occurs. For this to happen, at least TRACE_EXEC must be set, so "exec_save" will be set non-zero when a match is active. - 10. The execution trace (TRACE_EXEC) match test is performed in two parts to + 7. The execution trace (TRACE_EXEC) match test is performed in two parts to display the register values both before and after the instruction execution. Consequently, the enable test is done before the register trace, and the disable test is done after. - 11. A simulation stop bypass is inactivated after the first instruction + 8. A simulation stop bypass is inactivated after the first instruction execution by the expedient of setting the stop inhibition mask to the execution status result. This must be SCPE_OK (i.e., zero) for execution to continue, which removes the stop inhibition. If a non-zero status value is returned, then the inhibition mask will be set improperly, but that is irrelevant, as execution will stop in this case. + + 9. In hardware, the IAK signal is asserted to all devices in the I/O card + cage, as well as to the Memory Protect card. Only the interrupting + device will respond to IAK. In simulation, IAK is dispatched to the + interrupting device and, if that device is not the MP card, then to the + MP device as well. + + 10. In hardware, execution of the instruction in the trap cell does not use + the FTCH micro-order to avoid changing the MP violation register during + an MP interrupt. In simulation, we use a Fetch classification to label + the access correctly in a trace listing. This is OK because the Enable + Violation Register flip-flop has already been reset if this is an MP + interrupt, so the Fetch will not alter the MP VR. + + 11. The "meu_assert_IAK" routine sets the "meu_indicator" and "meu_page" + values for the P register before switching to the system map. Therefore, + on return, they indicate the prior map and page in use when the interrupt + occurred. */ t_stat sim_instr (void) { -static const char *const register_values [] = { /* register values, indexed by EOI concatenation */ - "e o i", - "e o I", - "e O i", - "e O I", - "E o i", - "E o I", - "E O i", - "E O I" - }; - -static const char mp_value [] = { /* memory protection value, indexed by mp_control */ - '-', - 'P' - }; - -static const char *const register_formats [] = { /* CPU register formats, indexed by is_1000 */ - REGA_FORMAT " A %06o, B %06o, ", /* is_1000 = FALSE format */ - REGA_FORMAT " A %06o, B %06o, X %06o, Y %06o, " /* is_1000 = TRUE format */ - }; - -static const char *const mp_mem_formats [] = { /* MP/MEM register formats, indexed by is_1000 */ - REGB_FORMAT " MPF %06o, MPV %06o\n", /* is_1000 = FALSE format */ - REGB_FORMAT " MPF %06o, MPV %06o, MES %06o, MEV %06o\n" /* is_1000 = TRUE format */ - }; - static uint32 exec_save; /* the trace flag settings saved by an EXEC match */ static uint32 idle_save; /* the trace flag settings saved by an idle match */ -DEVICE *tbg_dptr; -int abortval; -uint32 intrq, dmarq; /* set after setjmp */ -t_bool exec_test; /* set after setjmp */ -t_bool iotrap; /* set after setjmp */ -t_stat status; /* set after setjmp */ +MICRO_ABORT abort_reason; +t_bool exec_test; /* set after setjmp */ +t_bool interrupt_acknowledge; /* set after setjmp */ +t_stat status; /* set after setjmp */ /* Instruction prelude */ if (sim_switches & SWMASK ('B')) /* if a simulation stop bypass was requested */ - cpu_ss_inhibit = SS_INHIBIT; /* then inhibit stops for the first instruction */ + cpu_ss_inhibit = SS_INHIBIT; /* then inhibit stops for the first instruction only */ else /* otherwise */ cpu_ss_inhibit = SCPE_OK; /* clear the inhibition mask */ sim_switches &= ~SWMASK ('P'); /* clear the power-on switch to prevent interference */ -if (hp_device_conflict ()) /* if device assignment is inconsistent */ +if (initialize_io (TRUE) == FALSE) /* set up the I/O table; if there's a select code conflict */ return SCPE_STOP; /* then inhibit execution */ -tbg_dptr = find_dev ("CLK"); /* get a pointer to the time-base generator device */ - -if (tbg_dptr == NULL) /* if the TBG device is not present */ - return SCPE_IERR; /* then something is seriously wrong */ -else /* otherwise */ - tbg_select_code = ((DIB *) tbg_dptr->ctxt)->select_code; /* get the select code from the device's DIB */ - -io_initialize (); /* set up the I/O data structures */ -cpu_ioerr_uptr = NULL; /* and clear the I/O error unit pointer */ +mp_is_present = mp_initialize (); /* set up memory protect */ exec_save = 0; /* clear the EXEC match */ idle_save = 0; /* and idle match trace flags */ -jsb_plb = (mp_unit.flags & UNIT_MP_JSB) ? 0 : 2; /* set the protected lower bound for JSB */ - -mp_mem_changed = TRUE; /* request an initial MP/MEM trace */ +cpu_ioerr_uptr = NULL; /* clear the I/O error unit pointer */ -/* Memory Protect abort processor */ +/* Microcode abort processor */ -abortval = setjmp (save_env); /* set abort hdlr */ +abort_reason = (MICRO_ABORT) setjmp (abort_environment); /* set the microcode abort handler */ -if (abortval) { /* memory protect abort? */ - dms_upd_vr (abortval); /* update violation register (if not MEV) */ +switch (abort_reason) { /* dispatch on the abort reason */ - if (ion) /* interrupt system on? */ - protio (dibs [PRO], ioENF, 0); /* set flag */ + case Memory_Protect: /* a memory protect abort */ + status = SCPE_OK; /* continues execution with FLG 5 asserted */ + break; + + + case Interrupt: /* an interrupt in an indirect chain */ + PR = err_PR; /* backs out of the instruction */ + status = SCPE_OK; /* and then continues execution to service the interrupt */ + break; + + + case Indirect_Loop: /* an indirect chain length exceeding the limit */ + status = STOP_INDIR; /* causes a simulator stop */ + break; + + + default: /* anything else */ + status = SCPE_OK; /* continues execution */ + break; } -dmarq = calc_dma (); /* initial recalc of DMA masks */ -intrq = calc_int (); /* initial recalc of interrupts */ - -status = SCPE_OK; /* clear the status */ -exec_test = FALSE; /* and the execution test flag */ +exec_test = FALSE; /* clear the execution test flag */ /* Instruction execution loop */ -do { /* execute instructions until halted */ - err_PC = PR; /* save P for error recovery */ +while (status == SCPE_OK) { /* execute until simulator status prevents continuation */ - if (sim_interval <= 0) { /* event timeout? */ - status = sim_process_event (); /* process event service */ + err_PR = PR; /* save P for error recovery */ - if (status != SCPE_OK) /* service failed? */ - break; /* stop execution */ + if (sim_interval <= 0) { /* if an event is pending */ + status = sim_process_event (); /* then call the event service */ - dmarq = calc_dma (); /* recalc DMA reqs */ - intrq = calc_int (); /* recalc interrupts */ + if (status != SCPE_OK) /* if the service failed */ + break; /* then stop execution */ } + if (dma_request_set) { /* if a DMA service request is pending */ + dma_service (); /* then service the active channel(s) */ - if (dmarq) { /* if a DMA service request is pending */ - if (dmarq & DMA_1_REQ) { /* then if the request is for channel 1 */ - status = dma_cycle (ch1, DMA_Channel_1); /* then do one DMA cycle using the port A map */ - - if (status == SCPE_OK) /* cycle OK? */ - dmarq = calc_dma (); /* recalc DMA requests */ - else - break; /* cycle failed, so stop */ - } - - if ((dmarq & (DMA_1_REQ | DMA_2_REQ)) == DMA_2_REQ) { /* DMA channel 1 idle and channel 2 request? */ - status = dma_cycle (ch2, DMA_Channel_2); /* do one DMA cycle using port B map */ - - if (status == SCPE_OK) /* cycle OK? */ - dmarq = calc_dma (); /* recalc DMA requests */ - else - break; /* cycle failed, so stop */ - } - - if (dmarq) /* DMA request still pending? */ - continue; /* service it before instruction execution */ - - intrq = calc_int (); /* recalc interrupts */ + if (dma_request_set) /* if a DMA request is still pending */ + continue; /* then service it before instruction execution */ } - if (intrq && ion_defer) /* if an interrupt is pending but deferred */ - ion_defer = check_deferral (intrq); /* then check that the deferral is applicable */ - - - if (intrq && !ion_defer) { /* if an interrupt request is pending and not deferred */ - if (sim_brk_summ && /* any breakpoints? */ - sim_brk_test (intrq, SWMASK ('E') | /* unconditional or right type for DMS? */ - (dms_enb ? SWMASK ('S') : SWMASK ('N')))) { - status = STOP_BRKPNT; /* stop simulation */ + if (interrupt_request /* if an interrupt request is pending */ + && (cpu_interrupt_enable || reenable_interrupts ())) { /* and is enabled or reenabled */ + if (sim_brk_summ /* then if any breakpoints are defined */ + && sim_brk_test (interrupt_request, /* and an unconditional breakpoint */ + SWMASK ('E') /* or a breakpoint matching */ + | meu_breakpoint_type (TRUE))) { /* the current MEM map is set */ + status = STOP_BRKPNT; /* then stop simulation */ break; } - CIR = (HP_WORD) intrq; /* save int addr in CIR */ - intrq = 0; /* clear request */ - ion_defer = TRUE; /* defer interrupts */ - iotrap = TRUE; /* mark as I/O trap cell instr */ + CIR = (HP_WORD) interrupt_request; /* set the CIR to the select code of the interrupting device */ + interrupt_request = 0; /* and then clear the request */ + + cpu_interrupt_enable = CLEAR; /* inhibit interrupts */ + interrupt_acknowledge = TRUE; /* while in an interrupt acknowledge cycle */ if (idle_save != 0) { /* if idle loop tracing is suppressed */ cpu_dev.dctrl = idle_save; /* then restore the saved trace flag set */ idle_save = 0; /* and indicate that we are out of the idle loop */ } - if (TRACING (cpu_dev, TRACE_INSTR)) { - meu_map (PR, dms_ump, NOPROT); /* reset the indicator and page */ + meu_assert_IAK (); /* assert IAK to the MEM to switch to the system map */ - tprintf (cpu_dev, cpu_dev.dctrl, - DMS_FORMAT "interrupt\n", - meu_indicator, meu_page, - PR, CIR); - } + tprintf (cpu_dev, TRACE_INSTR, DMS_FORMAT "interrupt\n", + meu_indicator, meu_page, PR, CIR); - if (dms_enb) /* dms enabled? */ - dms_sr = dms_sr | MST_ENBI; /* set in status */ - else /* not enabled */ - dms_sr = dms_sr & ~MST_ENBI; /* clear in status */ + io_assert (iot [CIR].devptr, ioa_IAK); /* acknowledge the interrupt */ - if (dms_ump) { /* user map enabled at interrupt? */ - dms_sr = dms_sr | MST_UMPI; /* set in status */ - dms_ump = SMAP; /* switch to system map */ - } - else /* system map enabled at interrupt */ - dms_sr = dms_sr & ~MST_UMPI; /* clear in status */ + if (CIR != MPPE) /* if the MP is not interrupting */ + io_assert (iot [MPPE].devptr, ioa_IAK); /* then notify MP of the IAK too */ - mp_mem_changed = TRUE; /* set the MP/MEM registers changed flag */ - - IR = ReadF (CIR); /* get trap cell instruction */ - - io_dispatch (CIR, ioIAK, IR); /* acknowledge interrupt */ - - if (CIR != PRO) /* not MP interrupt? */ - protio (dibs [CIR], ioIAK, IR); /* send IAK for device to MP too */ + IR = ReadF (CIR); /* fetch the trap cell instruction */ } - else { /* normal instruction */ - iotrap = FALSE; /* not a trap cell instruction */ + else { /* otherwise this is a normal instruction execution */ + interrupt_acknowledge = FALSE; /* so clear the interrupt acknowledgement status */ - if (sim_brk_summ && /* any breakpoints? */ - sim_brk_test (PR, SWMASK ('E') | /* unconditional or */ - (dms_enb ? /* correct type for DMS state? */ - (dms_ump ? - SWMASK ('U') : SWMASK ('S')) : - SWMASK ('N')))) { - status = STOP_BRKPNT; /* stop simulation */ + if (sim_brk_summ /* if any breakpoints are defined */ + && sim_brk_test (PR, SWMASK ('E') /* and an unconditional breakpoint or a */ + | meu_breakpoint_type (FALSE))) { /* breakpoint matching the current map is set */ + status = STOP_BRKPNT; /* then stop simulation */ break; } - if (mp_evrff) /* violation register enabled */ - mp_viol = PR; /* update with current P */ + IR = ReadF (PR); /* fetch the instruction */ + PR = PR + 1 & LA_MASK; /* and point at the next memory location */ - IR = ReadF (PR); /* fetch instr */ - PR = (PR + 1) & VAMASK; - ion_defer = FALSE; + cpu_interrupt_enable = SET; /* enable interrupts */ } @@ -2313,31 +2130,14 @@ do { /* execute instructions cpu_dev.dctrl |= TRACE_ALL; /* and turn on full tracing */ } - if (cpu_dev.dctrl & TRACE_REG) { /* if register tracing is enabled */ - hp_trace (&cpu_dev, TRACE_REG, /* then output the working registers */ - register_formats [is_1000], - mp_value [mp_control], - dms_sr & MST_FENCE, - SR, AR, BR, XR, YR); + if (cpu_dev.dctrl & TRACE_REG) /* if register tracing is enabled */ + mem_trace_registers (interrupt_system); /* then output the working and MP/MEM registers */ - fputs (register_values [E << 2 | O << 1 | ion], sim_deb); - fputc ('\n', sim_deb); - - if (mp_mem_changed) { /* if the MP/MEM registers have been altered */ - hp_trace (&cpu_dev, TRACE_REG, /* then output the register values */ - mp_mem_formats [is_1000], - mp_value [mp_control], - mp_fence, mp_viol, dms_sr, dms_vr); - - mp_mem_changed = FALSE; /* clear the MP/MEM registers changed flag */ - } - } - - if (cpu_dev.dctrl & TRACE_EXEC /* if execution tracing is enabled */ - && exec_save != 0 /* and is currently active */ - && ! exec_test) { /* and the matching test fails */ - cpu_dev.dctrl = exec_save; /* then restore the saved debug flag set */ - exec_save = 0; /* and indicate that tracing is disabled */ + if (cpu_dev.dctrl & TRACE_EXEC /* if execution tracing is enabled */ + && exec_save != 0 /* and is currently active */ + && ! exec_test) { /* and the matching test fails */ + cpu_dev.dctrl = exec_save; /* then restore the saved debug flag set */ + exec_save = 0; /* and indicate that tracing is disabled */ hp_trace (&cpu_dev, TRACE_EXEC, EXEC_FORMAT "\n"); /* add a separator to the trace log */ } @@ -2361,29 +2161,25 @@ do { /* execute instructions sim_interval = sim_interval - 1; /* count the instruction */ - status = machine_instruction (IR, iotrap, intrq, /* execute one machine instruction */ + status = machine_instruction (interrupt_acknowledge, /* execute one machine instruction */ &idle_save); - if (status == NOTE_IOG) { /* I/O instr exec? */ - dmarq = calc_dma (); /* recalc DMA masks */ - intrq = calc_int (); /* recalc interrupts */ - status = SCPE_OK; /* continue */ - } - - else if (status == NOTE_INDINT) { /* intr pend during indir? */ - PR = err_PC; /* back out of inst */ - status = SCPE_OK; /* continue */ + if (status == NOTE_INDINT) { /* if an interrupt was recognized while resolving indirects */ + PR = err_PR; /* then back out of the instruction */ + status = SCPE_OK; /* to service the interrupt */ } cpu_ss_inhibit = status; /* clear the simulation stop inhibition mask */ - } -while (status == SCPE_OK); /* loop until halted */ + } /* and continue the instruction execution loop */ /* Instruction postlude */ -if (intrq && ion_defer) /* if an interrupt is pending but deferred */ - ion_defer = check_deferral (intrq); /* then check that the deferral is applicable */ +if (interrupt_request /* if an interrupt request is pending */ + && (cpu_interrupt_enable || reenable_interrupts ())) /* and is enabled or reenabled */ + cpu_pending_interrupt = interrupt_request; /* then report the pending interrupt select code */ +else /* otherwise */ + cpu_pending_interrupt = 0; /* report that no interrupt is pending */ if (exec_save != 0) { /* if EXEC tracing is active */ cpu_dev.dctrl = exec_save; /* then restore the saved trace flag set */ @@ -2393,37 +2189,37 @@ if (exec_save != 0) { /* if EXEC tracing is ac else if (idle_save != 0) /* otherwise if idle tracing is suppressed */ cpu_dev.dctrl = idle_save; /* then restore the saved trace flag set */ -saved_MR = MR; /* save for T cmd update */ +saved_MR = MR; /* save the current M value to detect a user change */ -if (status == STOP_HALT) /* programmed halt? */ - set_loader (NULL, FALSE, NULL, NULL); /* disable loader (after T is read) */ -else if (status <= STOP_RERUN) /* simulation stop */ - PR = err_PC; /* back out instruction */ +if (status == STOP_HALT) /* if this is a programmed halt */ + set_loader (NULL, FALSE, NULL, NULL); /* then disable the 21xx loader */ -dms_upd_sr (); /* update dms_sr */ -dms_upd_vr (MR); /* update dms_vr */ -pcq_r->qptr = pcq_p; /* update pc q ptr */ +else if (status <= STOP_RERUN) /* otherwise if this is a simulation stop */ + PR = err_PR; /* then restore P to reexecute the instruction */ -if (dms_enb) /* DMS enabled? */ - if (dms_ump) /* set default */ - sim_brk_dflt = SWMASK ('U'); /* breakpoint type */ - else /* to current */ - sim_brk_dflt = SWMASK ('S'); /* map mode */ -else /* DMS disabled */ - sim_brk_dflt = SWMASK ('N'); /* set breakpoint type to non-DMS */ +meu_update_status (); /* update the MEM status register */ +meu_update_violation (); /* and the violation register */ + +pcq_r->qptr = pcq_p; /* update the PC queue pointer */ + +sim_brk_dflt = meu_breakpoint_type (FALSE); /* base the default breakpoint type on the current MEM state */ tprintf (cpu_dev, cpu_dev.dctrl, DMS_FORMAT "simulation stop: %s\n", - meu_indicator, meu_page, - MR, TR, sim_error_text (status)); + meu_indicator, meu_page, MR, TR, + status >= SCPE_BASE ? sim_error_text (status) + : sim_stop_messages [status]); -return status; /* return status code */ +return status; /* return the status code */ } -/* VM command post-processor +/* VM command post-processor. - Update T register to contents of memory addressed by M register. + This routine is called from SCP after every command before returning to the + command prompt. We use it to update the T (memory data) register whenever + the M (memory address) register is changed to follow the 21xx/1000 CPU + hardware action. Implementation notes: @@ -2436,10 +2232,11 @@ return status; /* return status code */ void cpu_post_cmd (t_bool from_scp) { -if (MR != saved_MR) { /* M changed since last update? */ - saved_MR = MR; - TR = mem_fast_read (MR, dms_ump); /* sync T with new M */ +if (MR != saved_MR) { /* if M has changed since the last update */ + saved_MR = MR; /* then save the new M value */ + TR = mem_fast_read (MR, Current_Map); /* and set T to the contents of the addressed location */ } + return; } @@ -2448,368 +2245,100 @@ return; /* CPU global utility routines */ -/* Install a bootstrap loader into memory. - - This routine copies the bootstrap loader specified by "boot" into the last 64 - words of main memory, limited by a 32K memory size. If "sc" contains the - select code of an I/O interface (i.e., select code 10 or above), this routine - will configure the I/O instructions in the loader to the supplied select - code. On exit, P will be set to point at the loader starting program - address, and S will be altered as directed by the "sr_clear" and "sr_set" - masks if the current CPU is a 1000. - - The currently configured CPU family (21xx or 1000) determines which of two - BOOT_LOADER structures is accessed from the "boot" array. Each structure - contains the 64-word loader array and three indicies into the loader - array that specify the start of program execution, the element containing the - DMA control word, and the element containing the (negative) address of the - first loader word in memory. - - 21xx-series loaders consist of subsections handling one or two devices. A - two-part loader is indicated by a starting program index other than 0, i.e., - other than the beginning of the loader. An example is the Basic Moving-Head - Disc Loader (BMDL), which consists of a paper tape loader section starting at - index 0 and a disc loader section starting at index 50 octal. For these - loaders, I/O configuration depends on the "start_index" field of the selected - BOOTSTRAP structure: I/O instructions before the starting index are - configured to the current paper-tape reader select code, and instructions at - or after the starting index are configured to the device select code - specified by "sc". Single-part loaders specify a starting index of 0, and - all I/O instructions are configured to the "sc" select code. - - 1000-series loaders are always single part and always start at index 0, so - they are always configured to use the "sc" select code. - - If a given device does not have both a 21xx-series and a 1000-series loader, - the "start_index" field of the undefined loader will be set to the "IBL_NA" - value. If this routine is called to copy an undefined loader, it will reject - the call with a "Command not allowed" error. - - If I/O configuration is requested, each instruction in the loader array is - examined as it is copied to memory. If the instruction is a non-HLT I/O - instruction referencing a select code >= 10, the select code will be reset by - subtracting 10 and adding the value of the select code supplied by the "sc" - parameter (or the paper-tape reader select code, as above). This permits - configuration of loaders that address two- or three-card interfaces. Passing - an "sc" value of 0 will inhibit configuration, and the loader array will be - copied verbatim. - - As an example, passing an "sc" value of 24 octal will alter these I/O-group - instructions as follows: - - Loader Configured - Instruction Instruction Note - ----------- ----------- ------------------------------ - OTA 10 OTA 24 Normal configuration - LIA 11 LIA 25 Second card configuration - STC 6 STC 6 DCPC configuration not changed - HLT 11 HLT 11 Halt instruction not changed - - If configuration is performed, two additional operations may be performed. - First, the routine will alter the word at the index specified by the - "dma_index" field of the selected BOOTSTRAP structure unconditionally as - above. This word is assumed to contain a DMA control word; it is configured - to reference the supplied select code. Second, it will set the word at the - index specified by the "fwa_index" field to the two's-complement of the - starting address of the loader in memory. This value may be used by the - loader to check that it will not be overwritten by loaded data. - - If either field is set to the IBL_NA value, then the corresponding - modification is not made. For example, the 21xx Basic Binary Loader (BBL) - does not use DMA, so its "dma_index" field is set to IBL_NA, and so no DMA - control word modification is done. - - This routine also unconditionally sets the P register to the starting - address for loader execution. This is derived from the "start_index" field - and the starting memory address to which the loader is copied. - - Finally, if the current CPU is a 1000-series machine, the S register bits - corresponding to those set in the "sr_clear" value are masked off, and the - bits corresponding to those in the "sr_set" value are set. In addition, the - select code from the "sc" value is shifted left and ORed into the value. - This action presets the S-register to the correct value for the selected - loader. - - - Implementation notes: - - 1. The paper-tape reader's select code is determined on each entry to the - routine to accommodate select code reassignment by the user. -*/ - -t_stat cpu_copy_loader (const LOADER_ARRAY boot, uint32 sc, HP_WORD sr_clear, HP_WORD sr_set) -{ -uint32 index, base, ptr_sc; -MEMORY_WORD word; -DEVICE *ptr_dptr; - -if (boot [is_1000].start_index == IBL_NA) /* if the bootstrap is not defined for the current CPU */ - return SCPE_NOFNC; /* then reject the command */ - -else if (boot [is_1000].start_index > 0 && sc > 0) { /* if this is a two-part loader with I/O reconfiguration */ - ptr_dptr = find_dev ("PTR"); /* then get a pointer to the paper tape reader device */ - - if (ptr_dptr == NULL) /* if the PTR device is not present */ - return SCPE_IERR; /* then something is seriously wrong */ - else /* otherwise */ - ptr_sc = ((DIB *) ptr_dptr->ctxt)->select_code; /* get the select code from the device's DIB */ - } - -else /* otherwise this is a single-part loader */ - ptr_sc = 0; /* or I/O reconfiguration is not requested */ - -base = MEMSIZE - 1 & ~IBL_MASK & LA_MASK; /* get the base memory address of the loader */ -PR = base + boot [is_1000].start_index & R_MASK; /* and store the starting program address in P */ - -set_loader (NULL, TRUE, NULL, NULL); /* enable the loader (ignore errors if not 21xx) */ - -for (index = 0; index < IBL_SIZE; index++) { /* copy the bootstrap loader to memory */ - word = boot [is_1000].loader [index]; /* get the next word */ - - if (sc == 0) /* if reconfiguration is not requested */ - M [base + index] = word; /* then copy the instruction verbatim */ - - else if ((word & I_NMRMASK) == I_IO /* otherwise if this is an I/O instruction */ - && (word & I_DEVMASK) >= VARDEV /* and the referenced select code is >= 10B */ - && I_GETIOOP (word) != soHLT) /* and it's not a halt instruction */ - if (index < boot [is_1000].start_index) /* then if this is a split loader */ - M [base + index] = word + (ptr_sc - VARDEV) & DV_MASK; /* then reconfigure the paper tape reader */ - else /* otherwise */ - M [base + index] = word + (sc - VARDEV) & DV_MASK; /* reconfigure the target device */ - - else if (index == boot [is_1000].dma_index) /* otherwise if this is the DMA configuration word */ - M [base + index] = word + (sc - VARDEV) & DV_MASK; /* then reconfigure the target device */ - - else if (index == boot [is_1000].fwa_index) /* otherwise if this is the starting address word */ - M [base + index] = NEG16 (base); /* then set the negative starting address of the bootstrap */ - - else /* otherwise the word is not a special one */ - M [base + index] = word; /* so simply copy it */ - } - -if (is_1000) /* if the CPU is a 1000 */ - SR = SR & sr_clear | sr_set | IBL_TO_SC (sc); /* then modify the S register as indicated */ - -return SCPE_OK; /* return success with the loader copied to memory */ -} - - /* Execute an I/O instruction. - If memory protect is enabled, and the instruction is not in a trap cell, then - HLT instructions are illegal and will cause a memory protect violation. If - jumper W7 (SEL1) is in, then all other I/O instructions are legal; if W7 is - out, then only I/O instructions to select code 1 are legal, and I/O to other - select codes will cause a violation. - - If the instruction is allowed, then the I/O signal corresponding to the - instruction is determined, and the state of the interrupt deferral flag is - set. The signal is then dispatched to the device simulator indicated by the - target select code. The return value is split into status and data values, - with the latter containing the SKF signal state or data to be returned in the - A or B registers. + This routine executes the I/O Group instruction that is passed in the + "instruction" parameter. The instruction is examined to obtain the I/O + function desired. A memory protect check is then made to determine if it is + legal to execute the instruction. If it is, the state of the interrupt + enable flip-flop is set, the set of I/O backplane signals generated by the + instruction are picked up from the "control_set" array, and the signals and + inbound data value are dispatched to the device interface indicated by the + select code contained in the instruction. The data value and signals + returned from the interface are used as directed by the I/O operation, and + the status of the operation is returned. Implementation notes: - 1. If the H/C (hold/clear flag) bit is set, then the ioCLF signal is added - to the base signal set derived from the I/O instruction. + 1. The STC and CLC instructions share the same pattern in bits 9-6 that are + used to decode the rest of the IOG instructions. These instructions are + differentiated by the A/B selector (bit 11). - 2. ioNONE is dispatched for HLT instructions because although HLT does not - assert any backplane signals, the H/C bit may be set. If it is, then the - result will be to dispatch ioCLF. - - 3. Device simulators return either ioSKF or ioNONE in response to an SFC or - SFS signal. ioSKF means that the instruction should skip. Because - device simulators return the "data" parameter value by default, we - initialize that parameter to ioNONE to ensure that a simulator that does - not implement SFC or SFS does not skip, which is the correct action for - an interface that does not drive the SKF signal. - - 4. STF/CLF and STC/CLC share sub-opcode values and must be further decoded - by the state of instruction register bits 9 and 11, respectively. - - 5. We return NOTE_IOG for normal status instead of SCPE_OK to request that - interrupts be recalculated at the end of the instruction (execution of - the I/O group instructions can change the interrupt priority chain). We - do this in preference to calling the recalculation routines directly, as - some extended firmware instructions call this routine multiple times, and - there is no point in recalculating until all calls are complete. - - 6. The I/O dispatcher returns NOTE_SKIP if the interface asserted the SKF - signal. We must recalculate interrupts if the originating SFS or SFC - instruction included the CLF signal (e.g., SFS 0,C). + 2. If a memory protect violation occurs, the IOG signal from the CPU to the + I/O backplane is denied to disable all I/O signals. For a LIA/B or MIA/B + instruction, this will load or merge the value of the floating I/O bus + into the A or B registers. This value is zero on all machines. Merging + zero doesn't change the register value, so the only the LIA/B case must + be explicitly checked. */ -t_stat cpu_iog (HP_WORD IR, t_bool iotrap) +t_stat cpu_iog (HP_WORD instruction) { -/* Translation for I/O subopcodes: soHLT, soFLG, soSFC, soSFS, soMIX, soLIX, soOTX, soCTL */ -static const IOSIGNAL generate_signal [] = { ioNONE, ioSTF, ioSFC, ioSFS, ioIOI, ioIOI, ioIOO, ioSTC }; +const uint32 select_code = instruction & SC_MASK; /* device select code */ +const uint32 ab_selector = AB_SELECT (instruction); /* A/B register selector */ +IO_GROUP_OP micro_op; +HP_WORD inbound_value; +INBOUND_SET inbound_signals; +SKPF_DATA outbound; -const uint32 dev = IR & I_DEVMASK; /* device select code */ -const uint32 sop = I_GETIOOP (IR); /* I/O subopcode */ -const uint32 ab = (IR & I_AB ? 1 : 0); /* A/B register selector */ -uint32 ioreturn; -t_stat iostat; -IOCYCLE signal_set; -HP_WORD iodata = (HP_WORD) ioNONE; /* initialize for SKF test */ +if ((instruction & IR_CLC_MASK) == IR_CLC) /* if the instruction is CLC or CLC,C */ + if (instruction & IR_HCF) /* then if the H/C flag bit is set */ + micro_op = iog_CLC_C; /* then it's a CLC,C operation */ + else /* otherwise */ + micro_op = iog_CLC; /* it's a CLC operation */ +else /* otherwise */ + micro_op = IOG_OP (instruction); /* the operation is decoded directly */ -if (mp_control && !iotrap /* if MP is enabled and the instruction is not in trap cell */ - && (sop == soHLT /* and it is a HLT */ - || dev != OVF && (mp_unit.flags & UNIT_MP_SEL1))) { /* or does not address SC 01 and SEL1 is out */ - if (sop == soLIX) /* then an MP violation occurs; if it is an LIA/B */ - ABREG [ab] = 0; /* then the register is written before the abort */ +if (micro_op == iog_LIx || micro_op == iog_LIx_C) /* if executing an LIA or LIB instruction */ + ABREG [ab_selector] = 0; /* then clear the register value in case MP aborts */ - MP_ABORT (err_PC); /* MP abort */ - } +mp_check_io (select_code, micro_op); /* check for a memory protect violation */ -signal_set = generate_signal [sop]; /* generate I/O signal from instruction */ -ion_defer = defer_tab [sop]; /* defer depending on instruction */ +cpu_interrupt_enable = enable_map [is_1000] [micro_op]; /* disable interrupts depending on the instruction */ -if (sop == soOTX) /* OTA/B instruction? */ - iodata = ABREG [ab]; /* pass A/B register value */ +inbound_signals = control_set [micro_op]; /* get the set of signals to assert to the interface */ -else if (sop == soCTL && IR & I_CTL) /* CLC instruction? */ - signal_set = ioCLC; /* change STC to CLC signal */ +if (micro_op == iog_OTx || micro_op == iog_OTx_C) /* if the instruction is OTA/B or OTA/B,C */ + inbound_value = ABREG [ab_selector]; /* then send the register value to the interface */ +else /* otherwise */ + inbound_value = 0; /* the interface won't use the inbound value */ -if (IR & I_HC) /* if the H/C bit is set */ - if (sop == soFLG) /* then if the instruction is STF or CLF */ - signal_set = ioCLF; /* then change the ioSTF signal to ioCLF */ - else /* otherwise it's a non-flag instruction */ - signal_set |= ioCLF; /* so add ioCLF to the instruction-specific signal */ +outbound = io_dispatch (select_code, inbound_signals, /* dispatch the I/O action to the interface */ + inbound_value); -ioreturn = io_dispatch (dev, signal_set, iodata); /* dispatch the I/O signals */ +if (micro_op == iog_LIx || micro_op == iog_LIx_C) /* if the instruction is LIA/B or LIA/B,C */ + ABREG [ab_selector] = outbound.data; /* then store the I/O bus data into the register */ -iostat = IOSTATUS (ioreturn); /* extract status */ -iodata = IODATA (ioreturn); /* extract return data value */ +else if (micro_op == iog_MIx || micro_op == iog_MIx_C) /* otherwise if the instruction is MIA/B or MIA/B,C */ + ABREG [ab_selector] |= outbound.data; /* then merge the I/O bus data into the register */ -if (iostat == NOTE_SKIP) { /* if the interface asserted SKF */ - PR = PR + 1 & LA_MASK; /* then bump P to skip then next instruction */ - return (IR & I_HC ? NOTE_IOG : SCPE_OK); /* and request recalculation of interrupts if needed */ +else if (outbound.skip) { /* otherwise if the interface asserted SKF */ + PR = PR + 1 & R_MASK; /* then bump P to skip then next instruction */ + return SCPE_OK; /* and return success */ } -else if (iostat == SCPE_OK) { /* otherwise if instruction execution succeeded */ - if (sop == soLIX) /* then if is it an LIA or LIB */ - ABREG [ab] = iodata; /* then load the returned data */ +else if (micro_op == iog_HLT || micro_op == iog_HLT_C) /* otherwise if the instruction is HLT or HLT,C */ + return STOP_HALT; /* then stop the simulator */ - else if (sop == soMIX) /* otherwise if it is an MIA or MIB */ - ABREG [ab] = ABREG [ab] | iodata; /* then merge the returned data */ - - else if (sop == soHLT) /* otherwise if it is a HLT */ - return STOP_HALT; /* then stop the simulator */ - - return NOTE_IOG; /* request recalculation of interrupts */ - } - -else /* otherwise the execution failed */ - return iostat; /* so return the failure status */ +if (iot [select_code].devptr == NULL) /* if the I/O slot is empty */ + return STOP (cpu_ss_unsc); /* then return stop status if enabled */ +else /* otherwise */ + return SCPE_OK; /* the instruction executed successfully */ } -/* Calculate interrupt requests. +/* Resolve an indirect address. - The interrupt request (IRQ) of the highest-priority device for which all - higher-priority PRL bits are set is granted. That is, there must be an - unbroken chain of priority to a device requesting an interrupt for that - request to be granted. - - A device sets its IRQ bit to request an interrupt, and it clears its PRL bit - to prevent lower-priority devices from interrupting. IRQ is cleared by an - interrupt acknowledge (IAK) signal. PRL generally remains low while a - device's interrupt service routine is executing to prevent preemption. - - IRQ and PRL indicate one of four possible states for a device: - - IRQ PRL Device state - --- --- ---------------------- - 0 1 Not interrupting - 1 0 Interrupt requested - 0 0 Interrupt acknowledged - 1 1 (not allowed) - - Note that PRL must be dropped when requesting an interrupt (IRQ set). This - is a hardware requirement of the 1000 series. The IRQ lines from the - backplane are not priority encoded. Instead, the PRL chain expresses the - priority by allowing only one IRQ line to be active at a time. This allows a - simple pull-down encoding of the CIR inputs. - - The end of priority chain is marked by the highest-priority (lowest-order) - bit that is clear. The device corresponding to that bit is the only device - that may interrupt (a higher priority device that had IRQ set would also have - had PRL set, which is a state violation). We calculate a priority mask by - ANDing the complement of the PRL bits with an increment of the PRL bits. - Only the lowest-order bit will differ. For example: - - dev_prl : ...1 1 0 1 1 0 1 1 1 1 1 1 (PRL denied for SC 06 and 11) - - dev_prl + 1 : ...1 1 0 1 1 1 0 0 0 0 0 0 - ~dev_prl : ...0 0 1 0 0 1 0 0 0 0 0 0 - ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (break is at SC 06) - - The interrupt requests are then ANDed with the priority mask to determine if - a request is pending: - - pri mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (allowed interrupt source) - dev_irq : ...0 0 1 0 0 1 0 0 0 0 0 0 (devices requesting interrupts) - ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (request to grant) - - The select code corresponding to the granted request is then returned to the - caller. - - If ION is clear, only power fail (SC 04) and parity error (SC 05) are - eligible to interrupt (memory protect shares SC 05, but qualification occurs - in the MP abort handler, so if SC 05 is interrupting when ION is clear, it - must be a parity error interrupt). -*/ - -uint32 calc_int (void) -{ -uint32 sc, pri_mask [2], req_grant [2]; - -pri_mask [0] = ~dev_prl [0] & (dev_prl [0] + 1); /* calculate lower priority mask */ -req_grant [0] = pri_mask [0] & dev_irq [0]; /* calculate lower request to grant */ - -if (ion) /* interrupt system on? */ - if ((req_grant [0] == 0) && (pri_mask [0] == 0)) { /* no requests in lower set and PRL unbroken? */ - pri_mask [1] = ~dev_prl [1] & (dev_prl [1] + 1); /* calculate upper priority mask */ - req_grant [1] = pri_mask [1] & dev_irq [1]; /* calculate upper request to grant */ - } - else /* lower set has request */ - req_grant [1] = 0; /* no grants to upper set */ - -else { /* interrupt system off */ - req_grant [0] = req_grant [0] & /* only PF and PE can interrupt */ - (BIT_M (PWR) | BIT_M (PRO)); - req_grant [1] = 0; - } - -if (req_grant [0]) /* device in lower half? */ - for (sc = 0; sc <= 31; sc++) /* determine interrupting select code */ - if (req_grant [0] & LSB) /* grant this request? */ - return sc; /* return this select code */ - else /* not this one */ - req_grant [0] = req_grant [0] >> 1; /* position next request */ - -else if (req_grant [1]) /* device in upper half */ - for (sc = 32; sc <= 63; sc++) /* determine interrupting select code */ - if (req_grant [1] & LSB) /* grant this request? */ - return sc; /* return this select code */ - else /* not this one */ - req_grant [1] = req_grant [1] >> 1; /* position next request */ - -return 0; /* no interrupt granted */ -} - - -/* Resolve a indirect address. - - This routine resolves a supplied memory address into a direct address by - following an indirect chain, if any. On entry, "MA" contains the address to - resolve, and "irq" is non-zero if an interrupt is currently pending. On - exit, the variable pointed to by "addr" is set to the direct address, and - SCPE_OK is returned. If an interrupt is pending and permitted, NOTE_INDINT - is returned to abort the instruction, and the variable indicated by "addr" is - unchanged. + This routine resolves a possibly indirect memory address into a direct + address by following an indirect chain, if any. On entry, the M register + contains the address to resolve, and the "interruptible" parameter is set to + TRUE if the instruction is interruptible or FALSE if it is not. On exit, the + M register contains the direct address, and SCPE_OK is returned. If an + interrupt is pending and permitted, NOTE_INDINT is returned to abort the + instruction. If the indirect chain length is greater than the chain limit, + STOP_INDIR is returned to abort execution. In both abort cases, the M + register content is undefined. Logical memory addresses are 15 bits wide, providing direct access to a 32K logical address space. Addresses may also be indirect, with bit 15 (the MSB) @@ -2823,8 +2352,10 @@ return 0; /* no interrupt granted interrupt. On return from the interrupt handler, the instruction will be restarted. - However, the JMP indirect and JSB indirect instructions hold off interrupts - until completion of the instruction, including complete resolution of the + Detection of interrupts is dependent on the Interrupt Enable flip-flop being + set. Certain instructions, such as JMP indirect, JSB indirect, and most IOG + instructions, clear the enable flag to hold off interrupts until the current + and following instructions complete, including complete resolution of the indirect chain. If the chain is unresolvable (i.e., it points to itself, as in the instruction sequence JMP *+1,I and DEF *,I), then interrupts are held off forever. @@ -2852,18 +2383,19 @@ return 0; /* no interrupt granted calling this routine only for indirect addresses. 2. The 12892B Memory Protect accessory jumper W6 ("INT") controls whether - held off pending interrupts are serviced immediately (jumper removed) or - after three levels of indirection (jumper installed). If the jumper is - removed, MP must be enabled (control flip-flop set) for the interrupt - hold off to be overridden. + pending interrupts with the Interrupt Enable flip-flop clear are serviced + immediately (jumper removed) or after three levels of indirection (jumper + installed). If the jumper is removed, MP must be enabled (control + flip-flop set) for the interrupt disable to be overridden. The jumper state need not be checked here, however, because this routine - can be entered with an interrupt pending ("irq" non-zero) only if - "ion_defer" and "check_deferral" are both true. If either is false, the - pending interrupt would have been serviced before calling the instruction - executor that is calling this routine to resolve its address. For - "check_deferral" to return TRUE, then the INT jumper must be installed or - the MP control flip-flop must be clear. + can be entered with an interrupt pending, i.e., "interrupt_request" is + non-zero, only if "cpu_interrupt_enable" and "mp_reenable_interrupts" are + both false. If either is true, the pending interrupt would have been + serviced before calling the instruction executor that caleld this routine + to resolve its address. For "mp_reenable_interrupts" to return false, + the INT jumper must be installed or the MP control flip-flop must be + clear. 3. When employing the indirect counter, the hardware clears a pending interrupt deferral after the third level of indirection and aborts the @@ -2883,567 +2415,666 @@ return 0; /* no interrupt granted Modelling the hardware CPU freeze would be difficult, as the simulation console would have to be polled locally to watch for CTRL+E (the simulation equivalent of the CPU front panel HALT button). + + 6. In hardware, all instructions that resolve indirects are interruptible. + In simulation, some instruction executors are not written to handle an + instruction abort (e.g., the P register is not backed up properly to + rerun the instruction); these will pass FALSE for the "interruptible" + parameter. */ -t_stat resolve (HP_WORD MA, HP_WORD *address, uint32 irq) +t_stat cpu_resolve_indirects (t_bool interruptible) { uint32 level; t_bool pending; -if (MA & I_IA) { /* if the address is indirect */ - MA = ReadW (MA & LA_MASK); /* then follow the chain (first level) */ +if (MR & IR_IND) { /* if the address is indirect */ + MR = ReadW (MR & LA_MASK); /* then follow the chain (first level) */ - if (MA & I_IA) { /* if the address is still indirect */ - pending = (irq && !(mp_unit.flags & DEV_DIS)); /* then permit a pending interrupt if MP is enabled */ + if (MR & IR_IND) { /* if the address is still indirect */ + pending = interruptible && interrupt_request; /* then see if an interrupt request is pending */ - for (level = 2; MA & I_IA; level++) { /* follow the chain from level 2 until the address resolves */ + if (pending && cpu_interrupt_enable) /* if it's pending and enabled */ + return NOTE_INDINT; /* then service the interrupt now */ + else /* otherwise */ + pending = pending && mp_is_present; /* a pending interrupt is recognized only if MP is present */ + + for (level = 2; MR & IR_IND; level++) { /* follow the chain from level 2 until the address is direct */ if (level > indirect_limit) /* if the limit is exceeded */ return STOP_INDIR; /* then stop the simulator */ else if (pending) /* otherwise if an interrupt is pending */ if (level == 3) /* then if this is the third level */ - ion_defer = FALSE; /* then reenable interrupts */ + cpu_interrupt_enable = SET; /* then reenable interrupts */ else if (level == 4) /* otherwise if this is the fourth level */ return NOTE_INDINT; /* then service the interrupt now */ - MA = ReadW (MA & LA_MASK); /* follow the address chain */ + MR = ReadW (MR & LA_MASK); /* follow the address chain */ } } } -*address = MA; /* return the direct address */ -return SCPE_OK; /* and success status */ +return SCPE_OK; } +/* Abort an instruction. -/* Memory global utility routines */ + This routine performs a microcode abort for the reason passed in the + "abort_reason" parameter. In hardware, microcode aborts are implemented by + direct jumps to the FETCH label in the base set (microcode address 0). This + is typically done when an interrupt is detected while executing a lengthy + instruction or during resolution of an indirect chain. In simulation, this + is performed by a "longjmp" back to the start of the instruction execution + loop. It is the caller's responsibility to restore the machine state, e.g., + to back up the P register to rerun the instruction. +*/ + +void cpu_microcode_abort (MICRO_ABORT abort_reason) +{ +longjmp (abort_environment, (int) abort_reason); /* jump back to the instruction execution loop */ +} -/* Read a word from memory. +/* Install a bootstrap loader into memory. - Read and return a word from memory at the indicated logical address. On - entry, "dptr" points to the DEVICE structure of the device requesting access, - "classification" is the type of access requested, and "address" is the offset - into the 32K logical address space implied by the classification. + This routine copies the bootstrap loader specified by "boot" into the last 64 + words of main memory, limited by a 32K memory size. If "sc" contains the + select code of an I/O interface (i.e., select code 10 or above), this routine + will configure the I/O instructions in the loader to the supplied select + code. On exit, the P register will be set to point at the loader starting + program address, and the S register will be altered as directed by the + "sr_clear" and "sr_set" masks if the current CPU is a 1000. The updated P + register value is returned to the caller. - If memory expansion is enabled, the logical address is mapped into a physical - memory location; the map used is determined by the access classification. - The current map (user or system), alternate map (the map not currently - selected), or an explicit map (system, user, DCPC port A, or port B) may be - requested. Read protection is enabled for current or alternate map access - and disabled for the others. If memory expansion is disabled or not present, - the logical address directly accesses the first 32K of memory. + The currently configured CPU family (21xx or 1000) determines which of two + BOOT_LOADER structures is accessed from the "boot" array. Each structure + contains the 64-word loader array and three indicies into the loader + array that specify the start of program execution, the element containing the + DMA control word, and the element containing the (negative) address of the + first loader word in memory. - The memory protect (MP) and memory expansion module (MEM) accessories provide - a protected mode that guards against improper accesses by user programs. - They may be enabled or disabled independently, although protection requires - that both be enabled. MEM checks that read protection rules on the target - page are compatible with the access desired. If the check fails, and MP is - enabled, then the request is aborted. + 21xx-series loaders consist of subsections handling one or two devices. A + two-part loader is indicated by a starting program index other than 0, i.e., + other than the beginning of the loader. An example is the Basic Moving-Head + Disc Loader (BMDL), which consists of a paper tape loader section starting at + index 0 and a disc loader section starting at index 50 octal. For these + loaders, I/O configuration depends on the "start_index" field of the selected + BOOTSTRAP structure: I/O instructions before the starting index are + configured to the current paper-tape reader select code, and instructions at + or after the starting index are configured to the device select code + specified by "sc". Single-part loaders specify a starting index of 0, and + all I/O instructions are configured to the "sc" select code. - The 1000 family maps memory location 0 to the A-register and location 1 to - the B-register. CPU reads of these locations return the A- or B-register - values, while DCPC reads access physical memory locations 0 and 1 instead. + 1000-series loaders are always single part and always start at index 0, so + they are always configured to use the "sc" select code. + + If a given device does not have both a 21xx-series and a 1000-series loader, + the "start_index" field of the undefined loader will be set to the "IBL_NA" + value. If this routine is called to copy an undefined loader, it will reject + the call by returning a starting address of zero to the caller. In this + case, neither P nor S are changed. + + If I/O configuration is requested, each instruction in the loader array is + examined as it is copied. If the instruction is a non-HLT I/O instruction + referencing a select code >= 10, the select code will be reset by subtracting + 10 and adding the value of the select code supplied by the "sc" parameter (or + the paper-tape reader select code, as above). This permits configuration of + loaders that address two- or three-card interfaces. Passing an "sc" value of + 0 will inhibit configuration, and the loader array will be copied verbatim. + + As an example, passing an "sc" value of 24 octal will alter these I/O-group + instructions as follows: + + Loader Configured + Instruction Instruction Note + ----------- ----------- ------------------------------ + OTA 10 OTA 24 Normal configuration + LIA 11 LIA 25 Second card configuration + STC 6 STC 6 DCPC configuration not changed + HLT 11 HLT 11 Halt instruction not changed + + If configuration is performed, two additional operations may be performed. + First, the routine will alter the word at the index specified by the + "dma_index" field of the selected BOOTSTRAP structure unconditionally as + above. This word is assumed to contain a DMA control word; it is configured + to reference the supplied select code. Second, it will set the word at the + index specified by the "fwa_index" field to the two's-complement of the + starting address of the loader in memory. This value may be used by the + loader to check that it will not be overwritten by loaded data. + + If either field is set to the IBL_NA value, then the corresponding + modification is not made. For example, the 21xx Basic Binary Loader (BBL) + does not use DMA, so its "dma_index" field is set to IBL_NA, and so no DMA + control word modification is done. + + The starting address for loader execution is derived from the "start_index" + field and the starting memory address to which the loader is copied. + + Finally, if the current CPU is a 1000-series machine, the S register bits + corresponding to those set in the "sr_clear" value are masked off, and the + bits corresponding to those in the "sr_set" value are set. In addition, the + select code from the "sc" value is shifted left and ORed into the value. + This action presets the S-register to the correct value for the selected + loader. Implementation notes: - 1. A read beyond the limit of physical memory returns 0. This is handled by - allocating the maximum memory array and initializing memory beyond the - defined limit to zero, so no special handling is needed here.. - - 2. A MEM read protection violation with MP enabled causes an MP abort - instead of a normal return. + 1. The paper-tape reader's select code is determined on each entry to the + routine to accommodate select code reassignment by the user. */ -HP_WORD mem_read (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address) +uint32 cpu_copy_loader (const LOADER_ARRAY boot, uint32 sc, HP_WORD sr_clear, HP_WORD sr_set) { -uint32 index, map; -HP_WORD protection; +uint32 index, loader_start, ptr_sc; +MEMORY_WORD loader [IBL_SIZE]; +MEMORY_WORD word; +DEVICE *ptr_dptr; -switch (classification) { /* dispatch on the access classification */ +if (boot [is_1000].start_index == IBL_NA) /* if the bootstrap is not defined for the current CPU */ + return 0; /* then reject the command */ - case Fetch: - case Data: - default: /* needed to quiet the compiler's anxiety */ - map = dms_ump; /* use the currently selected map (user or system) */ - protection = RDPROT; /* and enable read protection */ - break; +else if (boot [is_1000].start_index > 0 && sc > 0) { /* if this is a two-part loader with I/O reconfiguration */ + ptr_dptr = find_dev ("PTR"); /* then get a pointer to the paper tape reader device */ - case Data_Alternate: - map = dms_ump ^ MAP_LNT; /* use the alternate map (user or system) */ - protection = RDPROT; /* and enable read protection */ - break; - - case Data_System: - map = SMAP; /* use the system map explicitly */ - protection = NOPROT; /* without protection */ - break; - - case Data_User: - map = UMAP; /* use the user map explicitly */ - protection = NOPROT; /* without protection */ - break; - - case DMA_Channel_1: - map = PAMAP; /* use the DCPC port A map */ - protection = NOPROT; /* without protection */ - break; - - case DMA_Channel_2: - map = PBMAP; /* use the DCPC port B map */ - protection = NOPROT; /* without protection */ - break; - } /* all cases are handled */ - -MR = address; /* save the logical memory address */ -index = meu_map (address, map, protection); /* and translate to a physical address */ - -if (index <= 1 && map < PAMAP) /* if the A/B register is referenced */ - TR = ABREG [index]; /* then return the selected register value */ -else /* otherwise */ - TR = (HP_WORD) M [index]; /* return the physical memory value */ - -tpprintf (dptr, mem_access [classification].debug_flag, - DMS_FORMAT " %s%s\n", - meu_indicator, meu_page, MR, TR, - mem_access [classification].name, - mem_access [classification].debug_flag == TRACE_FETCH ? "" : " read"); - -return TR; -} - - -/* Write a word to memory. - - Write a word to memory at the indicated logical address. On entry, "dptr" - points to the DEVICE structure of the device requesting access, - "classification" is the type of access requested, "address" is the offset - into the 32K logical address space implied by the classification, and - "value" is the value to write. - - If memory expansion is enabled, the logical address is mapped into a physical - memory location; the map used is determined by the access classification. - The current map (user or system), alternate map (the map not currently - selected), or an explicit map (system, user, DCPC port A, or port B) may be - requested. Write protection is enabled for current or alternate map access - and disabled for the others. If memory expansion is disabled or not present, - the logical address directly accesses the first 32K of memory. - - The memory protect (MP) and memory expansion module (MEM) accessories provide - a protected mode that guards against improper accesses by user programs. - They may be enabled or disabled independently, although protection requires - that both be enabled. MP checks that memory writes do not fall below the - Memory Protect Fence Register (MPFR) value, and MEM checks that write - protection rules on the target page are compatible with the access desired. - If either check fails, and MP is enabled, then the request is aborted (so, to - pass, a page must be writable AND the target must be above the MP fence). In - addition, a MEM write violation will occur if MP is enabled and the alternate - map is selected, regardless of the page protection. - - The 1000 family maps memory location 0 to the A-register and location 1 to - the B-register. CPU writes to these locations store the values into the A or - B register, while DCPC writes access physical memory locations 0 and 1 - instead. MP uses a lower bound of 2 for memory writes, allowing unrestricted - access to the A and B registers. - - - Implementation notes: - - 1. A write beyond the limit of physical memory is a no-operation. - - 2. When the alternate map is enabled, writes are permitted only in the - unprotected mode, regardless of page protections or the MP fence setting. - This behavior is not mentioned in the MEM documentation, but it is tested by - the MEM diagnostic and is evident from the MEM schematic. Referring to - Sheet 2 in the ERD, gates U125 and U127 provide this logic: - - WTV = MPCNDB * MAPON * (WPRO + ALTMAP) - - The ALTMAP signal is generated by the not-Q output of flip-flop U117, - which toggles on control signal -CL3 assertion (generated by the MESP - microorder) to select the alternate map. Therefore, a write violation is - indicated whenever a memory protect check occurs while the MEM is enabled - and either the page is write-protected or the alternate map is selected. - - The hardware reference manuals that contain descriptions of those DMS - instructions that write to the alternate map (e.g., MBI) say, "This - instruction will always cause a MEM violation when executed in the - protected mode and no bytes [or words] will be transferred." However, - they do not state that a write violation will be indicated, nor does the - description of the write violation state that this is a potential cause. -*/ - -void mem_write (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address, HP_WORD value) -{ -uint32 index, map; -HP_WORD protection; - -switch (classification) { /* dispatch on the access classification */ - - case Data: - default: /* needed to quiet the compiler's anxiety */ - map = dms_ump; /* use the currently selected map (user or system) */ - protection = WRPROT; /* and enable write protection */ - break; - - case Data_Alternate: - map = dms_ump ^ MAP_LNT; /* use the alternate map (user or system) */ - protection = WRPROT; /* and enable write protection */ - - if (dms_enb) /* if the MEM is enabled */ - dms_viol (address, MVI_WPR); /* then a violation always occurs if in protected mode */ - break; - - case Data_System: - map = SMAP; /* use the system map explicitly */ - protection = NOPROT; /* without protection */ - break; - - case Data_User: - map = UMAP; /* use the user map explicitly */ - protection = NOPROT; /* without protection */ - break; - - case DMA_Channel_1: - map = PAMAP; /* use the DCPC port A map */ - protection = NOPROT; /* without protection */ - break; - - case DMA_Channel_2: - map = PBMAP; /* use the DCPC port B map */ - protection = NOPROT; /* without protection */ - break; - - case Fetch: /* instruction fetches */ - return; /* do not cause writes */ - - } /* all cases are handled */ - -MR = address; /* save the logical memory address */ -index = meu_map (address, map, protection); /* and translate to a physical address */ - -if (protection != NOPROT && MP_TEST (address)) /* if protected and the MP check fails */ - MP_ABORT (address); /* then abort with an MP violation */ - -if (index <= 1 && map < PAMAP) /* if the A/B register is referenced */ - ABREG [index] = value; /* then write the value to the selected register */ - -else if (index < fwanxm) /* otherwise if the location is within defined memory */ - M [index] = (MEMORY_WORD) value; /* then write the value to memory */ - -TR = value; /* save the value */ - -tpprintf (dptr, mem_access [classification].debug_flag, - DMS_FORMAT " %s write\n", - meu_indicator, meu_page, MR, TR, - mem_access [classification].name); - -return; -} - - -/* Read a byte from memory. - - Read and return a byte from memory at the indicated logical address. On - entry, "dptr" points to the DEVICE structure of the device requesting access, - "classification" is the type of access requested, and "byte_address" is the - byte offset into the 32K logical address space implied by the classification. - - The 1000 is a word-oriented machine. To permit byte accesses, a logical byte - address is defined as two times the associated word address. The LSB of the - byte address designates the byte to access: 0 for the upper byte, and 1 for - the lower byte. As all 16 bits are used, byte addresses cannot be indirect. - - - Implementation notes: - - 1. Word buffering is not used to minimize memory reads, as the HP 1000 - microcode does a full word read for each byte accessed. -*/ - -uint8 mem_read_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address) -{ -const HP_WORD word_address = byte_address >> 1; /* the address of the word containing the byte */ -HP_WORD word; - -word = mem_read (dptr, classification, word_address); /* read the addressed word */ - -if (byte_address & LSB) /* if the byte address is odd */ - return LOWER_BYTE (word); /* then return the right-hand byte */ -else /* otherwise */ - return UPPER_BYTE (word); /* return the left-hand byte */ -} - - -/* Write a byte to memory. - - Write a byte to memory at the indicated logical address. On entry, "dptr" - points to the DEVICE structure of the device requesting access, - "classification" is the type of access requested, "byte_address" is the - byte offset into the 32K logical address space implied by the classification, - and "value" is the value to write. - - The 1000 is a word-oriented machine. To permit byte accesses, a logical byte - address is defined as two times the associated word address. The LSB of the - byte address designates the byte to access: 0 for the upper byte, and 1 for - the lower byte. As all 16 bits are used, byte addresses cannot be indirect. - - - Implementation notes: - - 1. Word buffering is not used to minimize memory writes, as the HP 1000 - base-set microcode does a full word write for each byte accessed. (The - DMS byte instructions, e.g., MBI, do full-word accesses for each pair of - bytes, but that is to minimize the number of map switches.) -*/ - -void mem_write_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address, uint8 value) -{ -const HP_WORD word_address = byte_address >> 1; /* the address of the word containing the byte */ -HP_WORD word; - -word = mem_read (dptr, classification, word_address); /* read the addressed word */ - -if (byte_address & LSB) /* if the byte address is odd */ - word = REPLACE_LOWER (word, value); /* then replace the right-hand byte */ -else /* otherwise */ - word = REPLACE_UPPER (word, value); /* replace the left-hand byte */ - -mem_write (dptr, classification, word_address, word); /* write the updated word back */ - -return; -} - - -/* Fast read from memory. - - This routine reads and returns a word from memory at the indicated logical - address using the specified map. Memory protection is not used, and tracing - is not available. - - This routine is used when fast, unchecked access to mapped memory is - required. -*/ - -HP_WORD mem_fast_read (HP_WORD address, uint32 map) -{ -return mem_examine (meu_map (address, map, NOPROT)); /* return the value at the translated address */ -} - - -/* Examine a physical memory address. - - This routine reads and returns a word from memory at the indicated physical - address. If the address lies outside of allocated memory, a zero value is - returned. There are no protections or error indications. -*/ - -HP_WORD mem_examine (uint32 address) -{ -if (address <= 1) /* if the address is 0 or 1 */ - return ABREG [address]; /* then return the A or B register value */ - -else if (address < PASIZE) /* otherwise if the address is within allocated memory */ - return (HP_WORD) M [address]; /* then return the memory value */ - -else /* otherwise the access is outside of memory */ - return 0; /* which reads as zero */ -} - - -/* Deposit into a physical memory address. - - This routine writes a word into memory at the indicated physical address. If - the address lies outside of defined memory, the write is ignored. There are - no protections or error indications. -*/ - -void mem_deposit (uint32 address, HP_WORD value) -{ -if (address <= 1) /* if the address is 0 or 1 */ - ABREG [address] = value & DV_MASK; /* then store into the A or B register */ - -else if (address < fwanxm) /* otherwise if the address is within defined memory */ - M [address] = (MEMORY_WORD) value & DV_MASK; /* then store the value */ - -return; -} - - - -/* Memory Expansion Unit global utility routines */ - - -/* DMS read and write map registers */ - -uint16 dms_rmap (uint32 mapi) -{ -return dms_map [mapi & MAP_MASK] & ~MAP_RSVD; -} - -void dms_wmap (uint32 mapi, uint32 dat) -{ -dms_map [mapi & MAP_MASK] = (uint16) (dat & ~MAP_RSVD); -return; -} - - -/* Process a MEM violation. - - A MEM violation will report the cause in the violation register. This occurs - even if the MEM is not in the protected mode (i.e., MP is not enabled). If - MP is enabled, an MP abort is taken with the MEV flip-flop set. Otherwise, - we return to the caller. -*/ - -void dms_viol (uint32 va, HP_WORD st) -{ -dms_vr = st | dms_upd_vr (va); /* set violation cause in register */ - -if (mp_control) { /* memory protect on? */ - mp_mem_changed = TRUE; /* set the MP/MEM registers changed flag */ - - mp_mevff = SET; /* record memory expansion violation */ - MP_ABORT (va); /* abort */ - } -return; -} - - -/* Update the MEM violation register. - - In hardware, the MEM violation register (VR) is clocked on every memory read, - every memory write above the lower bound of protected memory, and every - execution of a privileged DMS instruction. The register is not clocked when - MP is disabled by an MP or MEM error (i.e., when MEVFF sets or CTL5FF - clears), in order to capture the state of the MEM. In other words, the VR - continually tracks the memory map register accessed plus the MEM state - (MEBEN, MAPON, and USR) until a violation occurs, and then it's "frozen." - - Under simulation, we do not have to update the VR on every memory access, - because the visible state is only available via a programmed RVA/B - instruction or via the SCP interface. Therefore, it is sufficient if the - register is updated: - - - at a MEM violation (when freezing) - - at an MP violation (when freezing) - - during RVA/B execution (if not frozen) - - before returning to SCP after a simulator stop (if not frozen) -*/ - -HP_WORD dms_upd_vr (uint32 va) -{ -if (mp_control && (mp_mevff == CLEAR)) { /* violation register unfrozen? */ - dms_vr = VA_GETPAG (va) | /* set map address */ - (dms_enb ? MVI_MEM : 0) | /* and MEM enabled */ - (dms_ump ? MVI_UMP : 0); /* and user map enabled */ - - if (is_mapped (va)) /* is addressed mapped? */ - dms_vr = dms_vr | MVI_MEB; /* ME bus is enabled */ - - mp_mem_changed = TRUE; /* set the MP/MEM registers changed flag */ + if (ptr_dptr == NULL) /* if the PTR device is not present */ + return 0; /* then something is seriously wrong */ + else /* otherwise */ + ptr_sc = ((DIB *) ptr_dptr->ctxt)->select_code; /* get the select code from the device's DIB */ } -return dms_vr; +else /* otherwise this is a single-part loader */ + ptr_sc = 0; /* or I/O reconfiguration is not requested */ + +loader_start = mem_size - 1 & ~IBL_MASK & LA_MASK; /* get the base memory address of the loader */ +PR = loader_start + boot [is_1000].start_index & R_MASK; /* and store the starting program address in P */ + +set_loader (NULL, TRUE, NULL, NULL); /* enable the loader (ignore errors if not 21xx) */ + +for (index = 0; index < IBL_SIZE; index++) { /* copy the bootstrap loader to memory */ + word = boot [is_1000].loader [index]; /* get the next word */ + + if (sc == 0) /* if reconfiguration is not requested */ + loader [index] = word; /* then copy the instruction verbatim */ + + else if (IOGOP (word) /* otherwise if this is an I/O instruction */ + && (word & SC_MASK) >= SC_VAR /* and the referenced select code is >= 10B */ + && (word & IR_HLT_MASK) != IR_HLT) /* and it's not a halt instruction */ + if (index < boot [is_1000].start_index) /* then if this is a split loader */ + loader [index] = word + (ptr_sc - SC_VAR) & DV_MASK; /* then reconfigure the paper tape reader */ + else /* otherwise */ + loader [index] = word + (sc - SC_VAR) & DV_MASK; /* reconfigure the target device */ + + else if (index == boot [is_1000].dma_index) /* otherwise if this is the DMA configuration word */ + loader [index] = word + (sc - SC_VAR) & DV_MASK; /* then reconfigure the target device */ + + else if (index == boot [is_1000].fwa_index) /* otherwise if this is the starting address word */ + loader [index] = NEG16 (loader_start); /* then set the negative starting address of the bootstrap */ + + else /* otherwise the word is not a special one */ + loader [index] = word; /* so simply copy it */ + } + +mem_copy_loader (loader, loader_start, To_Memory); /* copy the loader to memory */ + +if (cpu_configuration & CPU_1000) /* if the CPU is a 1000 */ + SR = SR & sr_clear | sr_set | IBL_TO_SC (sc); /* then modify the S register as indicated */ + +return PR; /* return the starting execution address of the loader */ } -/* Update the MEM status register */ +/* Check for an I/O stop condition. -HP_WORD dms_upd_sr (void) -{ -dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO); + Entering the SET CPU STOP=IOERR command at the SCP command prompt stops the + simulator if an I/O error occurs on a device that does not return error + status to the CPU. For example, while the paper tape punch returns low- or + out-of-tape status, the paper tape reader gives no indication that a tape is + loaded. If the reader is commanded to read when no tape is mounted, the + interface hangs while waiting for the handshake with the device to complete. -if (dms_enb) - dms_sr = dms_sr | MST_ENB; + In hardware, the CPU can detect this condition only by timing the operation + and concluding that the tape is missing if the timeout is exceeded. However, + if an IOERR stop has been set, then the simulator will stop with an error + message to permit the condition to be fixed. For instance, attempting to + read from the paper tape reader with no paper tape image file attached will + print "No tape loaded in the PTR device" and will stop the simulator. -if (dms_ump) - dms_sr = dms_sr | MST_UMP; - -if (mp_control) - dms_sr = dms_sr | MST_PRO; - -return dms_sr; -} - - - -/* Memory Protect global utility routines */ - - -/* Memory protect and DMS validation for jumps. - - Jumps are a special case of write validation. The target address is treated - as a write, even when no physical write takes place, so jumping to a - write-protected page causes a MEM violation. In addition, a MEM violation is - indicated if the jump is to the unmapped portion of the base page. Finally, - jumping to a location under the memory-protect fence causes an MP violation. - - Because the MP and MEM hardware works in parallel, all three violations may - exist concurrently. For example, a JMP to the unmapped portion of the base - page that is write protected and under the MP fence will indicate a - base-page, write, and MP violation, whereas a JMP to the mapped portion will - indicate a write and MP violation (BPV is inhibited by the MEBEN signal). If - MEM and MP violations occur concurrently, the MEM violation takes precedence, - as the SFS and SFC instructions test the MEV flip-flop. - - The lower bound of protected memory is passed in the "plb" argument. This - must be either 0 or 2. All violations are qualified by the MPCND signal, - which responds to the lower bound. Therefore, if the lower bound is 2, and - if the part below the base-page fence is unmapped, or if the base page is - write-protected, then a MEM violation will occur only if the access is not to - locations 0 or 1. The instruction set firmware uses a lower bound of 0 for - JMP, JLY, and JPY (and for JSB with W5 out), and of 2 for DJP, SJP, UJP, JRS, - and .GOTO (and JSB with W5 in). - - Finally, all violations are inhibited if MP is off (mp_control is CLEAR), and - MEM violations are inhibited if the MEM is disabled. + Such devices will call this routine and pass a pointer to the unit that + encountered the error condition. The routine returns TRUE and saves the + pointer to the failing unit if the IOERR stop is enabled, and FALSE + otherwise. */ -void mp_dms_jmp (uint32 va, uint32 plb) +t_bool cpu_io_stop (UNIT *uptr) { -HP_WORD violation = 0; -uint32 pgn = VA_GETPAG (va); /* get page number */ +if (cpu_ss_ioerr != SCPE_OK) { /* if the I/O error stop is enabled */ + cpu_ioerr_uptr = uptr; /* then save the failing unit */ + return TRUE; /* and return TRUE to indicate that the stop is enabled */ + } -if (mp_control) { /* MP on? */ - if (dms_enb) { /* MEM on? */ - if (dms_map [dms_ump + pgn] & WRPROT) /* page write protected? */ - violation = MVI_WPR; /* write violation occurred */ +else /* otherwise */ + return FALSE; /* return FALSE to indicate that the stop is disabled */ +} - if (!is_mapped (va) && (va >= plb)) /* base page target? */ - violation = violation | MVI_BPG; /* base page violation occurred */ - if (violation) /* any violation? */ - dms_viol (va, violation); /* signal MEM violation */ + +/* I/O subsystem global utility routines */ + + +/* Device I/O signal dispatcher. + + This routine calls the I/O interface handler of the device corresponding to + the supplied "select_code" value, passing the "inbound_signals" and + "inbound_value" to the interface. The combined skip status and outbound data + value from the handler is returned to the caller. + + The 21xx/1000 I/O structure requires that no empty slots exist between + interface cards. This is due to the hardware priority chaining (PRH/PRL) + that is passed from card-to-card. If it is necessary to leave unused I/O + slots, HP 12777A Priority Jumper Cards must be installed in them to maintain + priority continuity. + + Under simulation, every unassigned I/O slot behaves as though a 12777A were + resident. In this configuration, I/O instructions addressed to one of these + slots read the floating bus for LIA/B and MIA/B instructions or do nothing + for all other instructions. + + If the slot is occupied, then the routine first determines the rank (0 or 1) + and bit (0 to 31) corresponding to the select code of the interface. These + will be used to access the interrupt, priority, and service request bit + vectors. Then it augments the supplied inbound signals set with IEN if the + interrupt system flip-flop is set, and PRH if no higher priority interface is + denying PRL. + + In hardware, PRH of each interface is connected to PRL of the next higher + priority (lower select code) interface, so that a PRH-to-PRL chain is formed. + An interface receives PRH if every higher-priority interface is asserting + PRL. When an interface denies PRL, each lower-priority interface has PRH + denied and so denies PRL to the next interface in the chain. + + To avoid checking calling each device simulator's higher-priority interface + routine to ascertain its PRL status, the priority holdoff bit vector is + checked. This vector has a bit set for each interface currently denying PRL. + The check is made as in the following example: + + sc bit : ...0 0 1 0 0 0 0 0 0 0 0 0 (dispatching to SC 11) + sc bit - 1 : ...0 0 0 1 1 1 1 1 1 1 1 1 (PRH required thru SC 10) + pri holdoff : ...0 0 1 0 0 1 0 0 0 0 0 0 (PRL denied for SC 06 and 11) + ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (PRL blocked at SC 06) + + If the ANDed value is 0, then there are no higher priority devices in this + rank that are denying PRL. If the rank is 0, then PRH is asserted to the + current select code. If the rank is 1, then PRH is asserted only if all rank + 0 priority holdoff bits are zero, i.e., if no rank 0 interfaces are denying + PRL. + + The device interface handler is obtained from the "dibs" array, and the + interface is called to process the inbound signals. + + On return, the outbound signals from the interface are examined. The + interrupt vector bit is set to the state of the IRQ signal, and the priority + vector bit is set to the state of the PRL signal. + + If the IRQ signal is present, then the interface is the highest priority + device requesting an interrupt (otherwise the interface would not have + received PRH, which is a term in asserting IRQ), so the select code of the + interrupting device is set. Otherwise, if the interface asserted its PRL + condition from a prior denied state, then lower priority devices are checked + to see if any now have a valid interrupt request. + + If the SRQ signal is asserted, a DMA request is made; if the device is under + DMA control, a DMA cycle will be initiated at the start of the next pass + through the instruction execution loop. Finally, the skip condition is set + if the SKF signal is asserted, and the skip state and outbound value from the + interface are returned to the caller. + + + Implementation notes: + + 1. For select codes < 10 octal, an IOI signal reads the floating S-bus + (high on the 1000, low on the 21xx). For select codes >= 10 octal, an + IOI reads the floating I/O bus (low on all machines). + + 2. The last select code used is saved for use by the CPU I/O handler in + detecting consecutive CLC 0 executions. + + 3. The IRQ and FLG signals always assert together on HP 21xx/1000 machines. + They are physically tied together on all interface cards, and the CPUs + depend on their concurrent assertion for correct operation. In + simulation, we check only IRQ from the interface, although FLG will be + asserted as well. + + 4. The IRQ and SRQ signals usually assert together, which means that an + interrupt is pending before the DMA request is serviced. However, DMA + cycles always assert CLF, which clears the interrupt request before + interrupts are checked. In hardware, an SRQ to an active channel asserts + DMALO (DMA lock out), which inhibits IRQ from setting the interrupt + request flip-flop until the DMA cycle completes. In simulation, IRQ is + never asserted because the CLF is processed by the interface simulator + before IRQ is determined. + + 5. An interface will assert "cnVALID" if the conditional PRL and IRQ were + determined. If "cnVALID" is not asserted by the interface, then the + states of the "cnPRL" and "cnIRQ" signals cannot be inferred from their + presence or absence in the outbound signal set. + + 6. The "cnVALID" pseudo-signal is required because although most interfaces + determine the PRL and IRQ states in response to an SIR assertion, not all + do. In particular, the 12936A Privileged Interrupt Fence determines PRL + in response to an IOO signal. +*/ + +SKPF_DATA io_dispatch (uint32 select_code, INBOUND_SET inbound_signals, HP_WORD inbound_value) +{ +SKPF_DATA result; +SIGNALS_VALUE outbound; +uint32 sc_rank, sc_bit, previous_holdoff; + +if (iot [select_code].dibptr == NULL) { /* if the I/O slot is empty */ + result.skip = FALSE; /* then SKF cannot be asserted */ + + if (inbound_signals & ioIOI && select_code < SC_VAR /* if this is an input request for an internal device */ + && cpu_configuration & CPU_1000) /* of a 1000 CPU */ + result.data = D16_UMAX; /* then the empty slot reads as all ones */ + else /* otherwise */ + result.data = 0; /* the empty slot reads as all zeros */ + } + +else { /* otherwise the slot is occupied */ + sc_rank = select_code / 32; /* so set the rank */ + sc_bit = 1u << select_code % 32; /* and bit of the interface */ + + if (interrupt_system == SET) { /* if the interrupt system is on */ + inbound_signals |= ioIEN; /* then assert IEN to the interface */ + + if ((sc_bit - 1 & priority_holdoff_set [sc_rank]) == 0 /* if no higher priority device */ + && (sc_rank == 0 || priority_holdoff_set [0] == 0)) /* is denying PRL */ + inbound_signals |= ioPRH; /* then assert PRH to our interface */ } - if ((va >= plb) && (va < mp_fence)) /* jump under fence? */ - MP_ABORT (va); /* signal MP violation */ + tpprintf (iot [select_code].devptr, TRACE_IOBUS, "Received data %06o with signals %s\n", + inbound_value, fmt_bitset (inbound_signals, inbound_format)); + + outbound = /* call the device interface */ + iot [select_code].dibptr->io_interface (iot [select_code].dibptr, /* with the device select code */ + inbound_signals, /* and inbound signal set */ + inbound_value); /* and data value */ + + tpprintf (iot [select_code].devptr, TRACE_IOBUS, "Returned data %06o with signals %s\n", + outbound.value, fmt_bitset (outbound.signals, outbound_format)); + + last_select_code = select_code; /* save the select code for CLC 0 detection */ + previous_holdoff = priority_holdoff_set [sc_rank]; /* and the current priority holdoff for this rank */ + + if (outbound.signals & cnVALID) { /* if the IRQ and PRL signals are valid */ + if (outbound.signals & cnIRQ) /* then if the conditional interrupt request signal is present */ + interrupt_request_set [sc_rank] |= sc_bit; /* then set the interrupt request bit */ + else /* otherwise */ + interrupt_request_set [sc_rank] &= ~sc_bit; /* clear the request bit */ + + if (outbound.signals & cnPRL) /* if the conditional priority low signal is present */ + priority_holdoff_set [sc_rank] &= ~sc_bit; /* then clear the priority inhibit bit */ + else /* otherwise */ + priority_holdoff_set [sc_rank] |= sc_bit; /* set the inhibit bit */ + } + + if (outbound.signals & ioIRQ) /* if the interrupt request signal is present */ + interrupt_request = select_code; /* then indicate that the select code is interrupting */ + + else if (previous_holdoff & ~priority_holdoff_set [sc_rank] & sc_bit) /* otherwise if priority is newly asserted */ + interrupt_request = io_poll_interrupts (interrupt_system); /* then check interrupt requests */ + + if (outbound.signals & ioSRQ) /* if SRQ is asserted */ + dma_assert_SRQ (select_code); /* then check if DMA is controlling this interface */ + + result.skip = (outbound.signals & ioSKF) != 0; /* return TRUE if the skip-on-flag signal is present */ + result.data = outbound.value; /* and return the outbound data from the interface */ + } + +return result; /* return the result of the I/O operation */ +} + + +/* Execute an I/O control operation. + + This routine performs an I/O control operation on the interface specified by + the "select_code" parameter. I/O control operations are all those that do + not pass data to or from the interface. The routine returns TRUE if the + interface asserted the SKF signal as a result of the operation and FALSE if + it did not. + + Certain microcode extension instructions perform I/O operations as part of + their execution. In hardware, this is done by setting bits 11-6 of the + Instruction Register to a code describing the operation and then issuing the + IOG micro-order to generate the sppropriate backplane signals. In + simulation, this relatively lightweight routine performs the same action, + avoiding much of the overhead of the "cpu_iog" routine. + + The routine performs a memory protect check and then dispatches the operation + to the interface indicated by the supplied select code. +*/ + +t_bool io_control (uint32 select_code, IO_GROUP_OP micro_op) +{ +SKPF_DATA result; + +mp_check_io (select_code, micro_op); /* check that the I/O operation is permitted */ + +result = io_dispatch (select_code, /* send the signal set */ + control_set [micro_op], 0); /* to the indicated interface */ + +return result.skip; /* return TRUE if the interface asserted SKF */ +} + + +/* Assert an I/O backplane signal. + + This routine is called by a device interface simulator to assert a specific + I/O backplane signal. The supported signal assertions are ENF, SIR, PON, + POPIO, CRS, and IAK. In hardware, these signals would be asserted by logic + gates on the interface; in simulation, they are asserted by calls to this + routine. + + The operation is dispatched via the I/O access table by the "io_dispatch" + routine, so we first ensure that the table entry is set correctly. During + instruction execution, this is redundant, as the table has been set up during + the execution prelude. However, this routine is also called by each + interface simulator's device reset routine in response to a RESET ALL or + RESET SCP command. At that point, the table may not have been + intitialized or may contain incorrect entries (if the device has been enabled + or reassigned since the last time the table was set up). +*/ + +void io_assert (DEVICE *dptr, IO_ASSERTION assertion) +{ +DIB *dibptr; +uint32 select_code; + +if (dptr != NULL && dptr->ctxt != NULL) { /* if the device points to a valid DIB */ + dibptr = (DIB *) dptr->ctxt; /* then get the DIB pointer */ + select_code = dibptr->select_code; /* and associated select code */ + + iot [select_code].devptr = dptr; /* set the current device and DIB pointer assignments */ + iot [select_code].dibptr = dibptr; /* into the table */ + + io_dispatch (select_code, assert_set [assertion], 0); /* assert the signal set to the indicated interface */ } return; } +/* Poll for a new interrupt request. -/* CPU local SCP support routine declarations */ + This routine is called when an interface asserts a previously denied PRL + signal, i.e., when PRL goes from low to high on the I/O backplane. It + determines if any interfaces lower in the priority chain are now ready to + interrupt. If so, the select code of the highest priority interrupting + interface is returned; otherwise, 0 is returned. + + In hardware, PRH and PRL form a priority chain that extends from interface to + interface on the backplane. For an interface to generate an interrupt, PRH + must be asserted by the next-higher-priority interface. An interface + receiving PRH asserts IRQ to request an interrupt, and it denies PRL to + prevent lower-priority devices from interrupting. IRQ is cleared by an + interrupt acknowledge (IAK) signal. PRL generally remains low while a + device's interrupt service routine is executing to prevent preemption. + + IRQ and PRL indicate one of four possible states for a device: + + IRQ PRL Device state + --- --- ---------------------- + 0 1 Not interrupting + 1 0 Interrupt requested + 0 0 Interrupt acknowledged + 1 1 (not allowed) + + PRL must be denied when requesting an interrupt (IRQ asserted). This is a + hardware requirement of the 21xx/1000 series. The IRQ lines from the + backplane are not priority encoded. Instead, the PRL chain expresses the + priority by allowing only one IRQ line to be active at a time. This allows a + simple pull-down encoding of the CIR inputs. + + When a given interface denies PRL, the PRH-to-PRL chain through all + lower-priority interfaces also denies. When that interface reasserts PRL, + the chain reasserts through intervening interfaces until it reaches one that + is ready to request an interrupt or through all interfaces if none are ready. + An interface that is ready to interrupt except for a denied PRH will assert + IRQ when the higher-priority PRH-to-PRL chain reasserts. + + A direct simulation of this hardware behavior would require polling all + lower-priority interfaces in order by calling their interface routines, + asserting PRH to each, and checking each for an asserted IRQ or a denied PRL + in response. Two avoid this inefficiency, two bit vectors are kept that + reflect the states of each interface's IRQ and PRL signals conditional on PRH + being asserted. The "interrupt_request_set" bit vector reflects each + interface's readiness to interrupt if its associated PRH signal is asserted, + and the "priority_holdoff_set" reflects the (inverted) state of each + interface's PRL signal, i.e., a zero-bit indicates that the interface is + ready to assert PRL if its associated PRH signal is asserted. Each vector is + represented as a two-element array of 32-bit unsigned integers, forming a + 64-bit vector in which bits 0-31 of the first element correspond to select + codes 00-37 octal, and bits 0-31 of the second element correspond to select + codes 40-77 octal. + + The routine begins by seeing if an interrupt is pending. If not, it returns + 0. Otherwise, the PRH-to-PRL chain is checked to see if PRL is denied + anywhere upstream of the highest priority interrupting interface. If it is, + then no interface can interrupt. If all higher-priority PRL signals are + asserted, then the interrupt request of that interface is granted. + + Recognition of interrupts depends on the current state of the interrupt + system and interrupt enable flip-flops, as follows: + + INTSYS INTEN Interrupts Recognized + ------ ------ ---------------------------- + CLEAR CLEAR Parity error + CLEAR SET Parity error, power fail + SET CLEAR Parity error, memory protect + SET SET All sources + + Memory protect and parity error share select code 05, but the interrupt + sources are differentiated on the MP card -- setting the flag buffer for a + memory protect violation is qualified by the IEN signal that reflects + the INTSYS state, whereas no qualification is used for a parity error. + Therefore, an interrupt on select code 05 with the interrupt system off must + be a parity error interrupt. + + The end of the priority chain is marked by the highest-priority + (lowest-order) bit that is set. We calculate a priority mask by ANDing the + the PRL bits with its two's complement using the IOPRIORITY macro. Only the + lowest-order bit will differ. For example: + + pri holdoff : ...0 0 1 0 0 1 0 0 0 0 0 0 (PRL denied for SC 06 and 11) + one's compl : ...1 1 0 1 1 0 1 1 1 1 1 1 + two's compl : ...1 1 0 1 1 1 0 0 0 0 0 0 + ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (chain is broken at SC 06) + + The interrupt requests are then ANDed with the priority masks to determine if + a request is pending: + + pri mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (allowed interrupt source) + int request : ...0 0 1 0 0 1 0 0 0 0 0 0 (interfaces asserting IRQ) + ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (request to grant) + + The select code corresponding to the granted request is then returned to the + caller. -/* CPU (SC 0) I/O signal handler. + Implementation notes: - I/O instructions for select code 0 manipulate the interrupt system. STF and - CLF turn the interrupt system on and off, and SFS and SFC test the state of - the interrupt system. When the interrupt system is off, only power fail and - parity error interrupts are allowed. + 1. Recognition of an interrupt in the second half of the request set (i.e., + for select codes 40-77 octal) occurs only if the priority set for the + first half is 0, i.e., if the PRH-to-PRL chain is unbroken through select + code 37. + + 2. If an interrupt for select codes 00-37 is pending but is inhibited, then + all higher select code interrupts are inhibited as well, so we only need + to check requests for the lower half or the upper half, but not both. + + 3. Splitting the interrupt request and priority holdoff sets into two 32-bit + parts is actually more efficient on 32-bit host CPUs than using a single + 64-bit set, especially when right-shifting to obtain the bit number. + Efficiency is also aided by the fact that interface select codes are + typically below 40 octal and therefore are represented in the first bit + set(s). +*/ + +uint32 io_poll_interrupts (FLIP_FLOP interrupt_system) +{ +const uint32 unmaskable = 1u << PWR | 1u << MPPE; /* these interrupts are not inhibited by INTSYS clear */ +uint32 sc, rank, priority_mask, request_granted; + +if (interrupt_request_set [0]) { /* if a lower pending interrupt exists */ + priority_mask = IOPRIORITY (priority_holdoff_set [0]); /* then calculate the first priority mask */ + request_granted = priority_mask & interrupt_request_set [0]; /* and the request to grant */ + rank = 0; /* for interrupt sources SC 00-37 */ + + if (interrupt_system == CLEAR) /* if the interrupt system is off */ + request_granted &= unmaskable; /* then only Power Fail and Parity Error may interrupt */ + } + +else if (interrupt_request_set [1] /* otherwise if an upper pending interrupt exists */ + && priority_holdoff_set [0] == 0) { /* and the priority chain is intact */ + priority_mask = IOPRIORITY (priority_holdoff_set [1]); /* then calculate the second priority mask */ + request_granted = priority_mask & interrupt_request_set [1]; /* and the request to grant */ + rank = 32; /* for interrupt sources SC 40-77 */ + } + +else /* otherwise no interrupts are pending */ + return 0; /* so return */ + +if (request_granted == 0) /* if no request was granted */ + return 0; /* then return */ + +else { /* otherwise */ + for (sc = rank; !(request_granted & 1); sc++) /* determine the interrupting select code */ + request_granted = request_granted >> 1; /* by counting the bits until the set bit is reached */ + + return sc; /* return the interrupting select code */ + } +} + + + +/* CPU local SCP support routines */ + + +/* CPU interface (select code 00). + + I/O operations directed to select code 0 manipulate the interrupt system. + STF and CLF turn the interrupt system on and off, and SFS and SFC test the + state of the interrupt system. When the interrupt system is off, only Power + Fail and Parity Error interrupts are allowed. The PRL signal follows the + state of the interrupt system. + + CLC asserts CRS to all interfaces from select code 6 upward. A PON reset initializes certain CPU registers. The 1000 series does a microcoded memory clear and leaves the T and P registers set as a result. - Front-panel PRESET performs additional initialization. We also handle MEM - preset here. - Implementation notes: @@ -3466,12 +3097,12 @@ return; "Because parity error interrupts can occur even when the interrupt system is off, the code at $CIC must be able to save the complete - system status. The major hole in being able to save the complete state - is in saving the interrupt system state. In order to do this in both + system status. The major hole in being able to save the complete state + is in saving the interrupt system state. In order to do this in both the 21MX and the 21XE the instruction 103300 was used to both test the interrupt system and turn it off." - 4. Select code 0 cannot interrupt, so there is no SIR handler. + 4. Select code 0 cannot interrupt, so the SIR handler does not assert IRQ. 5. To guarantee proper initialization, the 12920A terminal multiplexer requires that the Control Reset (CRS) I/O signal be asserted for a @@ -3484,195 +3115,267 @@ return; operation. */ -static uint32 cpuio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) +static SIGNALS_VALUE cpu_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) { -static IOCYCLE last_signal_set = ioNONE; /* the last set of I/O signals processed */ -uint32 sc; -IOSIGNAL signal; -IOCYCLE working_set = signal_set; /* no SIR handler needed */ +static INBOUND_SET last_signal_set = ioNONE; /* the last set of I/O signals processed */ +INBOUND_SIGNAL signal; +INBOUND_SET working_set = inbound_signals; +SIGNALS_VALUE outbound = { ioNONE, 0 }; +uint32 sc; -while (working_set) { - signal = IONEXT (working_set); /* isolate next signal */ +while (working_set) { /* while signals remain */ + signal = IONEXTSIG (working_set); /* isolate the next signal */ - switch (signal) { /* dispatch I/O signal */ + switch (signal) { /* dispatch the I/O signal */ - case ioCLF: /* clear flag flip-flop */ - ion = CLEAR; /* turn interrupt system off */ + case ioCLF: /* Clear Flag flip-flop */ + interrupt_system = CLEAR; /* turn the interrupt system off */ + + if (interrupt_request > MPPE) /* if any interrupt other than power fail or parity error */ + interrupt_request = 0; /* is pending, then clear it */ break; - case ioSTF: /* set flag flip-flop */ - ion = SET; /* turn interrupt system on */ + + case ioSTF: /* Set Flag flip-flop */ + interrupt_system = SET; /* turn the interrupt system on */ break; - case ioSFC: /* skip if flag is clear */ - setSKF (!ion); /* skip if interrupt system is off */ + + case ioSFC: /* Skip if Flag is Clear */ + if (interrupt_system == CLEAR) /* if the interrupt system is off */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ break; - case ioSFS: /* skip if flag is set */ - setSKF (ion); /* skip if interrupt system is on */ + + case ioSFS: /* Skip if Flag is Set */ + if (interrupt_system == SET) /* if the interrupt system is on */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ break; + case ioIOI: /* I/O input */ - stat_data = IORETURN (SCPE_OK, 0); /* returns 0 */ + outbound.value = 0; /* returns 0 */ break; - case ioPON: /* power on normal */ + + case ioPON: /* Power On Normal */ AR = 0; /* clear A register */ BR = 0; /* clear B register */ SR = 0; /* clear S register */ TR = 0; /* clear T register */ E = 1; /* set E register */ - if (is_1000) { /* 1000 series? */ - memset (M, 0, (uint32) MEMSIZE * 2); /* zero allocated memory */ - MR = 0077777; /* set M register */ - PR = 0100000; /* set P register */ + if (cpu_configuration & CPU_1000) { /* if the CPU is a 1000-series machine */ + mem_zero (0, mem_size); /* then power on clears memory */ + + MR = LA_MAX; /* set the M register to the maximum logical address */ + PR = MR + 1; /* and the P register to one more than that */ } - else { /* 21xx series */ - MR = 0; /* clear M register */ - PR = 0; /* clear P register */ + else { /* otherwise is a 21xx-series machine */ + MR = 0; /* which clears the M register */ + PR = 0; /* and the P register */ } break; - case ioPOPIO: /* power-on preset to I/O */ - O = 0; /* clear O register */ - ion = CLEAR; /* turn off interrupt system */ - ion_defer = FALSE; /* clear interrupt deferral */ - dms_enb = 0; /* turn DMS off */ - dms_ump = 0; /* init to system map */ - dms_sr = 0; /* clear status register and BP fence */ - dms_vr = 0; /* clear violation register */ + case ioPOPIO: /* Power-On Preset to I/O */ + O = 0; /* clear the overflow register */ - mp_mem_changed = TRUE; /* set the MP/MEM registers changed flag */ + interrupt_system = CLEAR; /* turn off the interrupt system */ + cpu_interrupt_enable = SET; /* and enable interrupts */ break; - case ioCLC: /* clear control flip-flop */ - if (last_select_code != 0 /* if the last I/O instruction */ - || (last_signal_set & ioCLC) == 0) /* was not a CLC 0 */ - for (sc = CRSDEV; sc <= MAXDEV; sc++) /* then assert the CRS signal */ - if (devs [sc] != NULL) /* to all occupied I/O slots */ - io_dispatch (sc, ioCRS, 0); /* from select code 6 and up */ + + case ioCLC: /* Clear Control flip-flop */ + if (last_select_code != 0 /* if the last I/O instruction */ + || (last_signal_set & ioCLC) == 0) /* was not a CLC 0 */ + for (sc = SC_CRS; sc <= SC_MAX; sc++) /* then assert the CRS signal */ + if (iot [sc].devptr != NULL) /* to all occupied I/O slots */ + io_assert (iot [sc].devptr, ioa_CRS); /* from select code 6 and up */ break; - default: /* all other signals */ - break; /* are ignored */ + + case ioSIR: /* Set Interrupt Request */ + if (interrupt_system) /* if the interrupt system is on */ + outbound.signals |= ioPRL | cnPRL | cnVALID; /* then assert PRL */ + else /* otherwise */ + outbound.signals |= cnVALID; /* deny PRL */ + break; + + + case ioIOO: /* not used by this interface */ + case ioSTC: /* not used by this interface */ + case ioEDT: /* not used by this interface */ + case ioCRS: /* not used by this interface */ + case ioIAK: /* not used by this interface */ + case ioENF: /* not used by this interface */ + case ioIEN: /* not used by this interface */ + case ioPRH: /* not used by this interface */ + break; } - working_set = working_set & ~signal; /* remove current signal from set */ - } + IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ + } /* and continue until all signals are processed */ -last_signal_set = signal_set; /* save the current signal set for the next call */ +last_signal_set = inbound_signals; /* save the current signal set for the next call */ -return stat_data; +return outbound; /* return the outbound signals and value */ } -/* Overflow/S-register (SC 1) I/O signal handler. +/* Overflow and S-register interface (select code 01). - Flag instructions directed to select code 1 manipulate the overflow (O) - register. Input and output instructions access the switch (S) register. On - the 2115 and 2116, there is no S-register indicator, so it is effectively - read-only. On the other machines, a front-panel display of the S-register is - provided. On all machines, front-panel switches are provided to set the - contents of the S register. + I/O operations directed to select code 1 manipulate the overflow (O) and + switch (S) registers. On the 2115 and 2116, there is no S-register + indicator, so it is effectively read-only. On the other machines, a + front-panel display of the S-register is provided. On all machines, + front-panel switches are provided to set the contents of the S register. Implementation notes: - 1. Select code 1 cannot interrupt, so there is no SIR handler. + 1. Select code 1 cannot interrupt, so there is no SIR handler, and PRH is + passed through to PRL. */ -static uint32 ovflio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) +static SIGNALS_VALUE ovf_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) { -IOSIGNAL signal; -IOCYCLE working_set = signal_set; /* no SIR handler needed */ +INBOUND_SIGNAL signal; +INBOUND_SET working_set = inbound_signals; +SIGNALS_VALUE outbound = { ioNONE, 0 }; -while (working_set) { - signal = IONEXT (working_set); /* isolate next signal */ +while (working_set) { /* while signals remain */ + signal = IONEXTSIG (working_set); /* isolate the next signal */ - switch (signal) { /* dispatch I/O signal */ + switch (signal) { /* dispatch the I/O signal */ - case ioCLF: /* clear flag flip-flop */ - O = 0; /* clear overflow */ + case ioCLF: /* Clear Flag flip-flop */ + O = 0; /* clear the overflow register */ break; - case ioSTF: /* set flag flip-flop */ - O = 1; /* set overflow */ + + case ioSTF: /* Set Flag flip-flop */ + O = 1; /* set the overflow register */ break; - case ioSFC: /* skip if flag is clear */ - setSKF (!O); /* skip if overflow is clear */ + + case ioSFC: /* Skip if Flag is Clear */ + if (O == 0) /* if the overflow register is clear */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ break; - case ioSFS: /* skip if flag is set */ - setSKF (O); /* skip if overflow is set */ + + case ioSFS: /* Skip if Flag is Set */ + if (O != 0) /* if the overflow register is set */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ break; - case ioIOI: /* I/O input */ - stat_data = IORETURN (SCPE_OK, SR); /* read switch register value */ + + case ioIOI: /* I/O data input */ + outbound.value = SR; /* read switch register value */ break; - case ioIOO: /* I/O output */ - if ((UNIT_CPU_MODEL != UNIT_2116) && /* no S register display on */ - (UNIT_CPU_MODEL != UNIT_2115)) /* 2116 and 2115 machines */ - SR = IODATA (stat_data); /* write S register value */ + + case ioIOO: /* I/O data output */ + if (!(cpu_configuration & (CPU_2115 | CPU_2116))) /* on all machines except the 2115 and 2116 */ + SR = inbound_value & R_MASK; /* write the value to the S-register display */ break; - default: /* all other signals */ - break; /* are ignored */ + + case ioPRH: /* Priority High */ + outbound.signals |= ioPRL; /* assert PRL */ + break; + + + case ioSTC: /* not used by this interface */ + case ioCLC: /* not used by this interface */ + case ioEDT: /* not used by this interface */ + case ioCRS: /* not used by this interface */ + case ioPOPIO: /* not used by this interface */ + case ioPON: /* not used by this interface */ + case ioIAK: /* not used by this interface */ + case ioENF: /* not used by this interface */ + case ioIEN: /* not used by this interface */ + case ioSIR: /* not used by this interface */ + break; } - working_set = working_set & ~signal; /* remove current signal from set */ - } + IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ + } /* and continue until all signals are processed */ -return stat_data; +return outbound; /* return the outbound signals and value */ } -/* Power fail (SC 4) I/O signal handler. +/* Power fail interface (select code 04). Power fail detection is standard on 2100 and 1000 systems and is optional on 21xx systems. Power fail recovery is standard on the 2100 and optional on the others. Power failure or restoration will cause an interrupt on select - code 4. The direction of power change (down or up) can be tested by SFC. + code 4 and will deny PRL, preventing all other devices from interrupting + while the power fail routine is executing. Asserting either STC or CLC + clears the interrupt and reasserts PRL. The direction of power change (down + or up) can be tested by SFC, which will skip if power is failing. - We do not implement power fail under simulation. However, the central - interrupt register (CIR) is always read by an IOI directed to select code 4. + Asserting IOI to select code 4 reads the Central Interrupt Register value. + + + Implementation notes: + + 1. Currently, power fail is not simulated, and the interface is provided + solely to read the CIR. As a result, PRL is always asserted. */ -static uint32 pwrfio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) +static SIGNALS_VALUE pwr_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) { -IOSIGNAL signal; -IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ +INBOUND_SIGNAL signal; +INBOUND_SET working_set = inbound_signals; +SIGNALS_VALUE outbound = { ioNONE, 0 }; -while (working_set) { - signal = IONEXT (working_set); /* isolate next signal */ +while (working_set) { /* while signals remain */ + signal = IONEXTSIG (working_set); /* isolate the next signal */ - switch (signal) { /* dispatch I/O signal */ + switch (signal) { /* dispatch the I/O signal */ - case ioSTC: /* set control flip-flop */ + case ioSTC: /* Set Control flip-flop */ + case ioCLC: /* Clear Control flip-flop */ break; /* reinitializes power fail */ - case ioCLC: /* clear control flip-flop */ - break; /* reinitializes power fail */ - case ioSFC: /* skip if flag is clear */ - break; /* skips if power fail occurred */ + case ioSFC: /* Skip if Flag is Clear */ + break; /* skips if power is failing */ - case ioIOI: /* I/O input */ - stat_data = IORETURN (SCPE_OK, CIR); /* input CIR value */ + + case ioIOI: /* I/O data input */ + outbound.value = CIR; /* return the select code of the interrupting interface */ break; - default: /* all other signals */ - break; /* are ignored */ + + case ioPRH: /* Priority High */ + outbound.signals |= ioPRL; /* assert PRL */ + break; + + + case ioSTF: /* not used by this interface */ + case ioCLF: /* not used by this interface */ + case ioSFS: /* not used by this interface */ + case ioIOO: /* not used by this interface */ + case ioEDT: /* not used by this interface */ + case ioCRS: /* not used by this interface */ + case ioPOPIO: /* not used by this interface */ + case ioPON: /* not used by this interface */ + case ioIAK: /* not used by this interface */ + case ioENF: /* not used by this interface */ + case ioIEN: /* not used by this interface */ + case ioSIR: /* not used by this interface */ + break; } - working_set = working_set & ~signal; /* remove current signal from set */ - } + IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ + } /* and continue until all signals are processed */ -return stat_data; +return outbound; /* return the outbound signals and value */ } @@ -3681,11 +3384,12 @@ return stat_data; This routine is called by the SCP to examine memory. The routine retrieves the memory location indicated by "address" as modified by any "switches" that were specified on the command line and returns the value in the first element - of "eval_array". + of "eval_array". The "uptr" parameter is not used. - On entry, the "map_address" routine is called to translate a logical address - to a physical address. If "switches" includes SIM_SW_REST or "-N", then the - address is a physical address, and the routine returns the address unaltered. + On entry, the "meu_map_address" routine is called to translate a logical + address to a physical address. If "switches" includes SIM_SW_REST or "-N", + then the address is a physical address, and the routine returns the address + unaltered. Otherwise, the address is a logical address interpreted in the context of the translation map implied by the specified switch and is mapped to a physical @@ -3703,21 +3407,19 @@ static t_stat cpu_examine (t_value *eval_array, t_addr address, UNIT *uptr, int3 { uint32 index; -index = map_address ((HP_WORD) address, switches); /* map the supplied address as directed by the switches */ +index = meu_map_address ((HP_WORD) address, switches); /* map the supplied address as directed by the switches */ -if (dms_enb == 0 && switches & ALL_MAPMODES) /* if the MEM is disabled but a mapping mode was given */ +if (index == D32_UMAX) /* if the MEM is disabled but a mapping mode was given */ return SCPE_NOFNC; /* then the command is not allowed */ -else if (index >= MEMSIZE) /* otherwise if the address is beyond the memory limit */ +else if (index >= mem_size) /* otherwise if the address is beyond the memory limit */ return SCPE_NXM; /* then return non-existent memory status */ else if (eval_array == NULL) /* otherwise if the value pointer was not supplied */ return SCPE_IERR; /* then return internal error status */ -else if (switches & SIM_SW_REST || index >= 2) /* otherwise if restoring or memory is being accessed */ - *eval_array = (t_value) M [index]; /* then return the memory value */ else /* otherwise */ - *eval_array = (t_value) ABREG [index]; /* return the A or B register value */ + *eval_array = (t_value) mem_examine (index); /* then return the memory or A/B register value */ return SCPE_OK; /* return success status */ } @@ -3729,9 +3431,10 @@ return SCPE_OK; /* return success status the supplied "value" into memory at the "address" location as modified by any "switches" that were specified on the command line. - On entry, the "map_address" routine is called to translate a logical address - to a physical address. If "switches" includes SIM_SW_REST or "-N", then the - address is a physical address, and the routine returns the address unaltered. + On entry, the "meu_map_address" routine is called to translate a logical + address to a physical address. If "switches" includes SIM_SW_REST or "-N", + then the address is a physical address, and the routine returns the address + unaltered. Otherwise, the address is a logical address interpreted in the context of the translation map implied by the specified switch and is mapped to a physical @@ -3748,19 +3451,16 @@ static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 swit { uint32 index; -index = map_address ((HP_WORD) address, switches); /* map the supplied address as directed by the switches */ +index = meu_map_address ((HP_WORD) address, switches); /* map the supplied address as directed by the switches */ -if (dms_enb == 0 && switches & ALL_MAPMODES) /* if the MEM is disabled but a mapping mode was given */ +if (index == D32_UMAX) /* if the MEM is disabled but a mapping mode was given */ return SCPE_NOFNC; /* then the command is not allowed */ -else if (index >= MEMSIZE) /* otherwise if the address is beyond the memory limit */ +else if (index >= mem_size) /* otherwise if the address is beyond the memory limit */ return SCPE_NXM; /* then return non-existent memory status */ -else if (switches & SIM_SW_REST || index >= 2) /* otherwise if restoring or memory is being accessed */ - M [index] = (MEMORY_WORD) value & DV_MASK; /* then write the memory value */ - else /* otherwise */ - ABREG [index] = (HP_WORD) value & DV_MASK; /* write the A or B register value */ + mem_deposit (index, (HP_WORD) value); /* write the memory or A/B register value */ return SCPE_OK; /* return success status */ } @@ -3775,12 +3475,12 @@ return SCPE_OK; /* return success status power-on reset to all devices when the simulator is started. If this is the first call after simulator startup, the initial memory array - is allocated, the default CPU and memory size configuration is set, and the - SCP-required program counter pointer is set to point to the REG array element - corresponding to the P register. In addition, the loader ROM sockets of the + is allocated, the SCP-required program counter pointer is set to point to the + REG array element corresponding to the P register, and the default CPU and + memory size configuration is set. In addition, the loader ROM sockets of the 1000-series CPUs are populated with the initial ROM set, and the Basic Binary - Loader (BBL) is installed in protected memory (the upper 64 words of the - defined memory size). + Loader (BBL) is installed in the protected memory (the upper 64 words of the + defined memory size) of the 2116. Implementation notes: @@ -3801,29 +3501,27 @@ return SCPE_OK; /* return success status static t_stat cpu_reset (DEVICE *dptr) { -if (M == NULL) { /* if this is the initial call after simulator startup */ - pcq_r = find_reg ("PCQ", NULL, dptr); /* then get the PC queue pointer */ +t_stat status; - if (pcq_r == NULL) /* if the PCQ register is not present */ - return SCPE_IERR; /* then something is seriously wrong */ - else /* otherwise */ - pcq_r->qptr = 0; /* initialize the register's queue pointer */ +if (sim_PC == NULL) { /* if this is the first call after simulator start */ + status = mem_initialize (PA_MAX); /* then allocate main memory */ - M = (MEMORY_WORD *) calloc (PASIZE, /* allocate and zero the main memory array */ - sizeof (MEMORY_WORD)); /* to the maximum configurable size */ - - if (M == NULL) /* if the allocation failed */ - return SCPE_MEM; /* then report a "Memory exhausted" error */ - - else { /* otherwise perform one-time initialization */ - for (sim_PC = dptr->registers; /* find the P register entry */ - sim_PC->loc != &PR && sim_PC->loc != NULL; /* in the register array */ - sim_PC++); /* for the SCP interface */ + if (status == SCPE_OK) { /* if memory initialization succeeds */ + for (sim_PC = dptr->registers; /* then find the P register entry */ + sim_PC->loc != &PR && sim_PC->loc != NULL; /* in the register array */ + sim_PC++); /* for the SCP interface */ if (sim_PC == NULL) /* if the P register entry is not present */ return SCPE_NXREG; /* then there is a serious problem! */ - MEMSIZE = 32768; /* set the initial memory size */ + pcq_r = find_reg ("PCQ", NULL, dptr); /* get the PC queue pointer */ + + if (pcq_r == NULL) /* if the PCQ register is not present */ + return SCPE_IERR; /* then something is seriously wrong */ + else /* otherwise */ + pcq_r->qptr = 0; /* initialize the register's queue pointer */ + + mem_size = 32768; /* set the initial memory size to 32K */ set_model (NULL, UNIT_2116, NULL, NULL); /* and the initial CPU model */ loader_rom [0] = find_dev ("PTR"); /* install the 12992K ROM in socket 0 */ @@ -3832,15 +3530,17 @@ if (M == NULL) { /* if this is the initia loader_rom [3] = find_dev ("DS"); /* and the 12992B ROM in socket 3 */ loader_rom [0]->boot (0, loader_rom [0]); /* install the BBL via the paper tape reader boot routine */ - set_loader (NULL, FALSE, NULL, NULL); /* and then disable the loader, which was enabled */ + set_loader (NULL, FALSE, NULL, NULL); /* and then disable the loader, which had been enabled */ } + + else /* otherwise memory initialization failed */ + return status; /* so report the error and abort the simulator */ } - if (sim_switches & SWMASK ('P')) /* if this is a power-on reset */ - IOPOWERON (&cpu_dib); /* then issue the PON signal to the CPU */ + io_assert (&cpu_dev, ioa_PON); /* then issue the PON signal to the CPU */ else /* otherwise */ - IOPRESET (&cpu_dib); /* issue a PRESET */ + io_assert (&cpu_dev, ioa_POPIO); /* issue a PRESET */ sim_brk_dflt = SWMASK ('N'); /* the default breakpoint type is "nomap" as MEM is disabled */ @@ -3886,7 +3586,7 @@ return SCPE_OK; SC - 000010, where SC is the configured select code, followed by a word set to the negative starting address of the loader. This is not simulated; instead, an attempt to boot from an empty socket is rejected - with "Command not allowed." + with "Non-existent device." */ static t_stat cpu_boot (int32 unitno, DEVICE *dptr) @@ -3894,8 +3594,8 @@ static t_stat cpu_boot (int32 unitno, DEVICE *dptr) const int32 select_code = IBL_SC (SR); /* the select code from S register bits 11-6 */ const int32 rom_socket = IBL_ROM (SR); /* the ROM socket number from S register bits 15-14 */ -if (is_1000) /* if this is a 1000-series CPU */ - if (select_code < VARDEV) { /* then if the select code is invalid */ +if (cpu_configuration & CPU_1000) /* if this is a 1000-series CPU */ + if (select_code < SC_VAR) { /* then if the select code is invalid */ O = 1; /* then set the overflow register */ return SCPE_ARG; /* and reject the IBL with "Invalid argument" */ } @@ -4035,10 +3735,8 @@ return SCPE_OK; /* the stops were succes static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc) { static CONST char confirm [] = "Really truncate memory [N]?"; -uint32 i; -uint32 old_size = (uint32) MEMSIZE; /* current memory size */ - -const uint32 model = CPU_MODEL_INDEX; /* the current CPU model index */ +const uint32 model = UNIT_MODEL (cpu_unit [0].flags); /* the current CPU model index */ +int32 old_size = (int32) mem_size; /* current memory size */ if ((uint32) new_size > cpu_features [model].maxmem) /* if the new memory size is not supported on current model */ return SCPE_NOFNC; /* then report the error */ @@ -4048,33 +3746,45 @@ if (!(sim_switches & SWMASK ('F')) /* if truncation is not && get_yn (confirm, FALSE) == FALSE) /* and the user denies confirmation */ return SCPE_INCOMP; /* then abort the command */ -if (is_1000) /* loader unsupported */ - MEMSIZE = fwanxm = new_size; /* set new memory size */ +if (cpu_configuration & CPU_1000) /* if the CPU is a 1000-series machine */ + cpu_unit [0].capac = mem_size = mem_end = new_size; /* then memory is not reserved for the loader */ -else { /* 21xx CPU? */ +else { /* otherwise */ set_loader (uptr, FALSE, NULL, NULL); /* save loader to shadow RAM */ - MEMSIZE = new_size; /* set new memory size */ - fwanxm = (uint32) MEMSIZE - IBL_SIZE; /* reserve memory for loader */ + cpu_unit [0].capac = mem_size = new_size; /* set new memory size */ + mem_end = mem_size - IBL_SIZE; /* reserve memory for loader */ } -for (i = fwanxm; i < old_size; i++) /* zero non-existent memory */ - M [i] = 0; +if (old_size > new_size) /* if the new size is smaller than the prior size */ + mem_zero (mem_end, old_size - new_size); /* then zero the newly non-existent memory area */ return SCPE_OK; } -/* Change CPU models. +/* Change the CPU model. - For convenience, MP and DMA are typically enabled if available; they may be - disabled subsequently if desired. Note that the 2114 supports only one DMA - channel (channel 1). All other models support two channels. + This validation routine is called to configure the CPU model. The + "new_model" parameter is set to the unit flag corresponding to the model + desired. The "uptr" parameter points to the CPU unit. The other parameters + are not used. - Validation: - - Sets standard equipment and convenience features. - - Changes DMA device name to DCPC if 1000 is selected. - - Enforces maximum memory allowed (doesn't change otherwise). - - Disables loader on 21xx machines. + Validation starts by setting the new memory size. If the current memory size + is within the range of memory sizes permitted by the new CPU model, it is + kept; otherwise, it is reduced to the maximum size permitted. If memory is + to be truncated, the "set_size" routine verifies either that it is blank + (i.e., filled with zero values) or that the user confirms that truncation is + allowable. + + If the new memory size is accepted, the CPU options are set to the typical + configuration supported by the new model. Memory Protect and DMA are then + configured as specified by the CPU feature table. Memory expansion is + enabled if the DMS instruction set is present. Finally, the "is_1000" flag + and memory reserved for the binary loader are set as directed by the new + model. + + On return, the "cpu_configuration" bit set is updated to indicate the new + model configuration. Implementation notes: @@ -4087,114 +3797,71 @@ return SCPE_OK; applicability, saving the multiple masks and comparisons that would otherwise be required. - Additionally, the configuration word has the unit CPU model bits set on - permanently to permit a base-set feature test for those CPUs that have no - options currently enabled (at least one non-option bit must be on for the - test to succeed, and the model bits are not otherwise used). + 2. The 'is_1000" variable is used to index into tables where the row + selected depends on whether or not the CPU is a 1000 M/E/F-series model. + For logical tests that depend on this, it is faster (by one x86 machine + instruction) to test the "cpu_configuration" variable for the presence of + one of the three 1000 model flags. */ static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc) { -const uint32 old_family = UNIT_CPU_FAMILY; /* current CPU type */ -const uint32 new_family = new_model & UNIT_FAMILY_MASK; /* new CPU family */ -const uint32 new_index = new_model >> UNIT_V_CPU; /* new CPU model index */ +const FEATURE_TABLE new_cpu = cpu_features [UNIT_MODEL (new_model)]; /* get the features describing the new model */ uint32 new_memsize; t_stat result; -if (MEMSIZE > cpu_features [new_index].maxmem) /* if the current memory size is too large for the new model */ - new_memsize = cpu_features [new_index].maxmem; /* then set it to the maximum size supported */ +if (mem_size > new_cpu.maxmem) /* if the current memory size is too large for the new model */ + new_memsize = new_cpu.maxmem; /* then set it to the maximum size supported */ else /* otherwise */ - new_memsize = (uint32) MEMSIZE; /* leave it unchanged */ + new_memsize = mem_size; /* leave it unchanged */ result = set_size (uptr, new_memsize, NULL, NULL); /* set the new memory size */ -if (result == SCPE_OK) { /* if the change succeeded */ - cpu_configuration = cpu_features [new_index].typ & UNIT_OPTS /* then set the typical options */ - | UNIT_MODEL_MASK /* and the base model bits */ - | 1u << new_index; /* and the new CPU model flag */ +if (result == SCPE_OK) { /* if the change succeeded */ + cpu_configuration = TO_CPU_OPTION (new_cpu.typ) /* then set the typical options */ + | CPU_BASE /* and the base instruction set bit */ + | TO_CPU_MODEL (new_model); /* and the new CPU model flag */ - cpu_unit.flags = cpu_unit.flags & ~UNIT_OPTS /* enable the typical features */ - | cpu_features [new_index].typ & UNIT_OPTS; /* for the new model */ + cpu_unit [0].flags = cpu_unit [0].flags & ~UNIT_OPTION_FIELD /* enable the typical features */ + | new_cpu.typ & UNIT_OPTION_FIELD; /* for the new model */ - if (cpu_features [new_index].typ & UNIT_MP) /* MP in typ config? */ - mp_dev.flags &= ~DEV_DIS; /* enable it */ - else - mp_dev.flags |= DEV_DIS; /* disable it */ + mp_configure ((new_cpu.typ & UNIT_MP) != 0, /* configure MP, specifying whether */ + (new_cpu.opt & UNIT_MP) != 0); /* it is enabled and optional */ - if (cpu_features[new_index].opt & UNIT_MP) /* MP an option? */ - mp_dev.flags |= DEV_DISABLE; /* make it alterable */ - else - mp_dev.flags &= ~DEV_DISABLE; /* make it unalterable */ + dma_configure (); /* configure DMA for the new model */ + if (new_cpu.typ & UNIT_DMS) /* if DMS instructions are present */ + meu_configure (ME_Enabled); /* then enable the MEM device */ + else /* otherwise */ + meu_configure (ME_Disabled); /* disable the MEM and mapping */ - if (cpu_features [new_index].typ & UNIT_DMA) { /* DMA in typ config? */ - dma1_dev.flags &= ~DEV_DIS; /* enable DMA channel 1 */ - - if (new_model == UNIT_2114) /* 2114 has only one channel */ - dma2_dev.flags |= DEV_DIS; /* disable channel 2 */ - else /* all others have two channels */ - dma2_dev.flags &= ~DEV_DIS; /* enable it */ + if (cpu_configuration & CPU_1000) { /* if the CPU is a 1000-series machine */ + is_1000 = TRUE; /* then set the model index */ + mem_end = mem_size; /* memory is not reserved for the loader */ } - else { - dma1_dev.flags |= DEV_DIS; /* disable channel 1 */ - dma2_dev.flags |= DEV_DIS; /* disable channel 2 */ + else { /* otherwise this is a 2100 or 211x */ + is_1000 = FALSE; /* so set the model index */ + mem_end = mem_size - IBL_SIZE; /* and reserve memory for the loader */ } - - if (cpu_features [new_index].opt & UNIT_DMA) { /* DMA an option? */ - dma1_dev.flags |= DEV_DISABLE; /* make it alterable */ - - if (new_model == UNIT_2114) /* 2114 has only one channel */ - dma2_dev.flags &= ~DEV_DISABLE; /* make it unalterable */ - else /* all others have two channels */ - dma2_dev.flags |= DEV_DISABLE; /* make it alterable */ - } - - else { /* otherwise DMA is not available */ - dma1_dev.flags &= ~DEV_DISABLE; /* make it unalterable */ - dma2_dev.flags &= ~DEV_DISABLE; /* make it unalterable */ - } - - if ((old_family == UNIT_FAMILY_1000) && /* if current family is 1000 */ - (new_family == UNIT_FAMILY_21XX)) { /* and new family is 21xx */ - deassign_device (&dma1_dev); /* delete DCPC names */ - deassign_device (&dma2_dev); - } - - else if ((old_family == UNIT_FAMILY_21XX) && /* otherwise if current family is 21xx */ - (new_family == UNIT_FAMILY_1000)) { /* and new family is 1000 */ - assign_device (&dma1_dev, "DCPC1"); /* change DMA device name */ - assign_device (&dma2_dev, "DCPC2"); /* to DCPC for familiarity */ - } - - - if (!(cpu_features [new_index].typ & UNIT_DMS)) /* if DMS is not being enabled */ - dms_enb = 0; /* then disable MEM mapping */ - - is_1000 = (new_family == UNIT_FAMILY_1000); /* set model */ - - if (is_1000) - fwanxm = (uint32) MEMSIZE; /* loader reserved only for 21xx */ - else /* 2100 or 211x */ - fwanxm = (uint32) MEMSIZE - IBL_SIZE; /* reserve memory for loader */ } return result; } -/* Change a CPU option. +/* Set a CPU option. - This validation routine is called to configure the option set for the current - CPU model. The "option" parameter is set to the option desired and will be - one of the unit option flags. The "uptr" parameter points to the CPU unit - and is used to obtain the CPU model. The other parameters are not used. + This validation routine is called to add an option to the current CPU + configuration. The "option" parameter is set to the unit flag corresponding + to the option desired. The "uptr" parameter points to the CPU unit and is + used to obtain the CPU model. The other parameters are not used. The routine processes commands of the form: SET CPU