diff --git a/HP2100/hp2100_baci.c b/HP2100/hp2100_baci.c new file mode 100644 index 00000000..e4063a6f --- /dev/null +++ b/HP2100/hp2100_baci.c @@ -0,0 +1,1748 @@ +/* hp2100_baci.c: HP 12966A Buffered Asynchronous Communications Interface simulator + + 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"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + BACI 12966A Buffered Asynchronous Communications Interface + + 19-Jun-19 JDB Now detaches the line when entering DIAGNOSTIC mode + 28-Mar-19 JDB Revised for serial port support + 18-Mar-19 JDB Reordered SCP includes + 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 + 10-Mar-17 JDB Added IOBUS to the debug table + 17-Jan-17 JDB Changed "hp_---sc" and "hp_---dev" to "hp_---_dib" + 02-Aug-16 JDB "baci_poll_svc" now calls "tmxr_poll_conn" unilaterally + 13-May-16 JDB Modified for revised SCP API function parameter types + 24-Dec-14 JDB Added casts for explicit downward conversions + 10-Jan-13 MP Added DEV_MUX and additional DEVICE field values + 10-Feb-12 JDB Deprecated DEVNO in favor of SC + Removed DEV_NET to allow restoration of listening port + 28-Mar-11 JDB Tidied up signal handling + 26-Oct-10 JDB Changed I/O signal handler for revised signal model + 25-Nov-08 JDB Revised for new multiplexer library SHOW routines + 11-Sep-08 JDB Fixed STC,C losing interrupt request on BREAK + 07-Sep-08 JDB Fixed IN_LOOPBACK conflict with netinet/in.h + Changed Telnet poll to connect immediately after reset or attach + 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size + 26-Jun-08 JDB Rewrote device I/O to model backplane signals + 17-Jun-08 JDB Moved fmt_char() function to hp2100_sys.c + 13-Jun-08 JDB Cleaned up debug reporting for sim_activate calls + 16-Apr-08 JDB Separated terminal I/O and Telnet poll for idle compatibility + 07-Dec-07 JDB Created BACI device + + References: + - HP 12966A Buffered Asynchronous Data Communications Interface Installation + and Reference Manual (12966-90001, Jul-1982) + - Western Digital Communications Products Handbook (Jun-1984) + + + The 12966A BACI card supplanted the 12531C Teletype and 12880A CRT interfaces + as the primary terminal connection for HP 1000 systems. The main advantage + of this card over the others was its 128-character FIFO memory. While this + allowed more efficient I/O than its interrupt-per-character predecessors, the + most significant advantage was that block input from the 264x-series of CRT + terminals was supported. The 264x were the first HP-supported terminals to + provide local editing and character storage, as well as mass storage via dual + DC-100 minicartridge drives. This support meant that input from the terminal + could come in bursts at the full baud rate, which would overrun the older + cards that needed a small intercharacter handling time. Also, the older + cards placed a substantial load on the CPU in high-baud-rate output + applications. Indeed, block output under RTE on a 1000 M-Series with a + 12880A CRT card would saturate the CPU at about 5700 baud. + + For a while, the BACI and the earlier cards were both supported as the system + console interface, and RTE primary systems were generated with drivers for + both cards. The boot-time I/O reconfigurator would detect the presence of + the BACI card and would dynamically select the correct driver (DVR05 vs. + DVR00). However, the 12880A card faded quickly as the 264x and later 262x + terminals gained in popularity, and support for the 12880A was dropped in + favor of the BACI. This meant that later RTE primary systems could only be + run on CPUs containing a BACI card. + + The simulation supports terminal and diagnostic modes. The latter simulates + the installation of the 12966-60003 diagnostic loopback connector on the + card. + + Fifteen programmable baud rates were supported by the BACI. We simulate + these "realistic" rates by scheduling I/O service based on the appropriate + number of 1000 E-Series instructions for the rate selected. We also provide + an "external rate" that is equivalent to 9600 baud, as most terminals were + set to their maximum speeds. + + We support the 12966A connected to an HP terminal emulator via Telnet or a + serial port. Internally, we model the BACI as a terminal multiplexer with + one line. The simulation is complicated by the half-duplex nature of the + card (there is only one FIFO, used selectively either for transmission or + reception) and the double-buffered UART (a Western Digital TR1863A), which + has holding registers as well as a shift registers for transmission and + reception. We model both sets of device registers. + + During an output operation, the first character output to the card passes + through the FIFO and into the transmitter holding register. Subsequent + characters remain in the FIFO. If the FIFO is then turned around by a mode + switch from transmission to reception, the second character output becomes + the first character input to the CPU, as the first character output remains + in the THR. Also, the FIFO counter reflects the combined state of the FIFO + and the THR: it is incremented by a "shift in" to the FIFO and decremented by + the "transmit complete" signal from the UART. This has two implications: + + 1. If the FIFO is turned around before the character in the THR is + transmitted, the counter will not decrement when transmission is + complete, so the FIFO will show as "empty" when the counter reads "1". + + 2. The FIFO counter will indicate "half full" and "full" one character + before the FIFO itself reaches those stages. + + The diagnostic hood connects the UART clock to a spare output register. This + allows the diagnostic to supply programmed clock pulses to the UART. The + serial transmit and receive lines from the UART are also available to the + diagnostic. Functional operation is checked by supplying or testing serial + data while clocking the UART sixteen times for each bit. This meant that we + had to model the UART shift registers for faithful hardware simulation. + + The simulation provides both the "realistic timing" described above, as well + as an "optimized (fast) timing" option. Optimization makes three + improvements: + + 1. On output, characters in the FIFO are emptied into the line buffer as a + block, rather than one character per service call, and on input, all of + the characters available in the line buffer are loaded into the FIFO as a + block. + + 2. The ENQ/ACK handshake is done locally, without involving the terminal + client. + + 3. Input occurring during an output operation is delayed until the second or + third consecutive ENQ/ACK handshake. + + During development, it was noted that a comparatively long time elapsed + (approximately 30 milliseconds on a 3 GHz system) between the transmission of + an ENQ and the reception of the ACK. As the RTE BACI driver, DVR05, does + three ENQ/ACKs at the end of each line, plus an additional ENQ/ACK every 33 + characters within a line, maximum throughput was about ten lines per second. + The source of this delay is not understood but apparently lies within the + terminal emulator, as it was observed with two emulators from two different + companies. Absorbing the ENQ and generating the ACK locally provided a + dramatic improvement in output speed. + + However, as a result, RTE break-mode became effectively impossible, i.e., + striking a key during output no longer produced the break-mode prompt. This + was traced to the RTE driver. DVR05 only checks for an input character + during ENQ/ACK processing, and then only during the second and third + end-of-line handshakes. When the ENQ/ACKs were eliminated, break-mode also + disappeared. + + The workaround is to save a character received during output and supply it + during the second or third consecutive handshake. This ensures that + break-mode is recognized. Because the driver tries to "cheat" the card by + selecting receive mode before the ENQ has actually been transmitted (in order + to save an interrupt), the FIFO counter becomes "off by one" and is reset + with a master clear at the end of each handshake. This would normally clear + the UART receiving register, thereby losing the deferred character. We work + around this by skipping the register clear in "fast timing" mode. +*/ + + + +#include + +#include "sim_defs.h" +#include "sim_tmxr.h" + +#include "hp2100_defs.h" +#include "hp2100_io.h" + + + +/* Program limits */ + +#define FIFO_SIZE 128 /* read/write buffer size */ + + +/* Character constants */ + +#define ENQ '\005' +#define ACK '\006' + + +/* Unit flags */ + +#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ +#define UNIT_V_FASTTIME (UNIT_V_UF + 1) /* fast timing mode */ +#define UNIT_V_CAPSLOCK (UNIT_V_UF + 2) /* caps lock mode */ + +#define UNIT_DIAG (1 << UNIT_V_DIAG) +#define UNIT_FASTTIME (1 << UNIT_V_FASTTIME) +#define UNIT_CAPSLOCK (1 << UNIT_V_CAPSLOCK) + + +/* Bit flags */ + +#define OUT_MR 0100000 /* common master reset */ + +#define OUT_ENCM 0000040 /* ID1: enable character mode */ +#define OUT_ENCB 0000020 /* ID1: enable CB */ +#define OUT_ENCC 0000010 /* ID1: enable CC */ +#define OUT_ENCE 0000004 /* ID1: enable CE */ +#define OUT_ENCF 0000002 /* ID1: enable CF */ +#define OUT_ENSXX 0000001 /* ID1: enable SBB/SCF */ + +#define OUT_DIAG 0000040 /* ID2: diagnostic output */ +#define OUT_REFCB 0000020 /* ID2: reference CB */ +#define OUT_REFCC 0000010 /* ID2: reference CC */ +#define OUT_REFCE 0000004 /* ID2: reference CE */ +#define OUT_REFCF 0000002 /* ID2: reference CF */ +#define OUT_REFSXX 0000001 /* ID2: reference SBB/SCF */ + +#define OUT_STBITS 0000040 /* ID3: number of stop bits */ +#define OUT_ECHO 0000020 /* ID3: enable echo */ +#define OUT_PARITY 0000010 /* ID3: enable parity */ +#define OUT_PAREVEN 0000004 /* ID3: even parity or odd */ + +#define OUT_XMIT 0000400 /* ID4: transmit or receive */ +#define OUT_CA 0000200 /* ID4: CA on */ +#define OUT_CD 0000100 /* ID4: CD on */ +#define OUT_SXX 0000040 /* ID4: SBA/SCA on */ +#define OUT_DCPC 0000020 /* ID4: DCPC on */ + +#define OUT_CSC 0000040 /* ID5: clear special char interrupt */ +#define OUT_CBH 0000020 /* ID5: clear buffer half-full interrupt */ +#define OUT_CBF 0000010 /* ID5: clear buffer full interrupt */ +#define OUT_CBE 0000004 /* ID5: clear buffer empty interrupt */ +#define OUT_CBRK 0000002 /* ID5: clear break interrupt */ +#define OUT_COVR 0000001 /* ID5: clear overrun/parity interrupt */ + +#define OUT_SPFLAG 0000400 /* ID6: special character */ + +#define OUT_IRQCLR (OUT_CBH | OUT_CBF | OUT_CBE | OUT_CBRK | OUT_COVR) + + +#define IN_VALID 0100000 /* received data: character valid */ +#define IN_SPFLAG 0040000 /* received data: is special character */ + +#define IN_DEVINT 0100000 /* status: device interrupt */ +#define IN_SPCHAR 0040000 /* status: special char has been recd */ +#define IN_SPARE 0010000 /* status: spare receiver state */ +#define IN_TEST 0004000 /* status: unprocessed serial data line */ +#define IN_BUFHALF 0001000 /* status: buffer is half full */ +#define IN_BUFFULL 0000400 /* status: buffer is full */ +#define IN_BUFEMPTY 0000200 /* status: buffer is empty */ +#define IN_BREAK 0000100 /* status: break detected */ +#define IN_OVRUNPE 0000040 /* status: overrun or parity error */ +#define IN_CB 0000020 /* status: CB is on */ +#define IN_CC 0000010 /* status: CC is on */ +#define IN_CE 0000004 /* status: CE is on */ +#define IN_CF 0000002 /* status: CF is on */ +#define IN_SXX 0000001 /* status: SBB/SCF is on */ + +#define IN_MODEM (IN_CB | IN_CC | IN_CE | IN_CF | IN_SXX) +#define IN_DIAG (IN_DEVINT | IN_SPARE | IN_TEST | IN_MODEM) +#define IN_STDIRQ (IN_DEVINT | IN_SPCHAR | IN_BREAK | IN_OVRUNPE) +#define IN_FIFOIRQ (IN_BUFEMPTY | IN_BUFHALF | IN_BUFFULL) + + +/* Packed starting bit numbers */ + +#define OUT_V_ID 12 /* common output word ID */ +#define OUT_V_DATA 0 /* ID 0: output data character */ +#define OUT_V_CHARSIZE 0 /* ID 3: character size */ +#define OUT_V_BAUDRATE 0 /* ID 4: baud rate */ +#define OUT_V_SPCHAR 0 /* ID 6: special character */ + +#define IN_V_CHARCNT 8 /* data: char count in buffer */ +#define IN_V_DATA 0 /* data: input character */ +#define IN_V_IRQCLR 5 /* status: interrupt status clear */ + + +/* Packed bit widths */ + +#define OUT_W_ID 3 +#define OUT_W_DATA 8 +#define OUT_W_CHARSIZE 2 +#define OUT_W_BAUDRATE 4 +#define OUT_W_SPCHAR 8 + +#define IN_W_CHARCNT 6 +#define IN_W_DATA 8 + +/* Packed bit masks */ + +#define OUT_M_ID ((1 << OUT_W_ID) - 1) +#define OUT_M_DATA ((1 << OUT_W_DATA) - 1) +#define OUT_M_CHARSIZE ((1 << OUT_W_CHARSIZE) - 1) +#define OUT_M_BAUDRATE ((1 << OUT_W_BAUDRATE) - 1) +#define OUT_M_SPCHAR ((1 << OUT_W_SPCHAR) - 1) + +#define IN_M_CHARCNT ((1 << IN_W_CHARCNT) - 1) +#define IN_M_DATA ((1 << IN_W_DATA) - 1) + +/* Packed field masks */ + +#define OUT_ID (OUT_M_ID << OUT_V_ID) +#define OUT_DATA (OUT_M_DATA << OUT_V_DATA) +#define OUT_CHARSIZE (OUT_M_CHARSIZE << OUT_V_CHARSIZE) +#define OUT_BAUDRATE (OUT_M_BAUDRATE << OUT_V_BAUDRATE) +#define OUT_SPCHAR (OUT_M_SPCHAR << OUT_V_SPCHAR) + +#define IN_CHARCNT (IN_M_CHARCNT << IN_V_CHARCNT) +#define IN_DATA (IN_M_DATA << IN_V_DATA) + + +/* Command helpers */ + +#define TO_CHARCNT(c) (((c) << IN_V_CHARCNT) & IN_CHARCNT) + +#define GET_ID(i) (((i) & OUT_ID) >> OUT_V_ID) +#define GET_BAUDRATE(b) (((b) & OUT_BAUDRATE) >> OUT_V_BAUDRATE) + +#define IO_MODE (baci_icw & OUT_XMIT) +#define XMIT OUT_XMIT +#define RECV 0 + +#define CLEAR_HR 0 /* UART holding register clear value */ +#define CLEAR_R -1 /* UART register clear value */ + + +/* Unit references */ + +#define baci_term baci_unit [0] /* terminal I/O unit */ +#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 */ + +static uint16 baci_ibuf = 0; /* status/data in */ +static uint16 baci_obuf = 0; /* command/data out */ +static 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 */ + +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 */ + +static uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */ +static 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 */ + +static t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ +static uint32 baci_enq_cntr = 0; /* ENQ seen counter */ + + +/* BACI local SCP support routines */ + +static INTERFACE baci_interface; + +/* BACI local routines */ + +static int32 service_time (uint32 control_word); +static void update_status (void); +static void master_reset (void); + +static uint16 fifo_get (void); +static void fifo_put (uint8 ch); +static void clock_uart (void); + +/* BACI local SCP support routines */ + +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, char *cptr); +static t_stat baci_detach (UNIT *uptr); +static t_stat baci_set_mode (UNIT *uptr, int32 value, char *cptr, void *desc); + + +/* BACI SCP data structures */ + +DEVICE baci_dev; /* incomplete device structure */ + + +/* 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 */ + &baci_dev /* multiplexer device */ + }; + + +/* 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 + routine runs only when there are characters to read or write. It operates at + the approximate baud rate of the terminal (in CPU instructions per second) in + order to be compatible with the OS drivers. The line poll must run + continuously, but it can operate much more slowly, as the only requirement is + that it must not present a perceptible lag to human input. To be compatible + with CPU idling, it is co-scheduled with the master poll timer, which uses a + ten millisecond period. + + + Implementation notes: + + 1. Normally, the poll unit carries the UNIT_ATTABLE designation while the + line unit does not. This directs the listening socket connection to the + former and serial port connections to the latter, permitting simultaneous + Telnet and serial port connections. In our case, there is only one line, + which can be connected via either Telnet or serial, and all connections + are made to the line unit. This ensures that attaching a serial port + first detaches a Telnet port, and vice versa. The poll unit is used only + to schedule poll checks. +*/ + +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 */ + }; + + +/* 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 }, + { ORDATA (OBUF, baci_obuf, 16), REG_FIT | REG_X }, + { GRDATA (STATUS, baci_status, 2, 16, 0), REG_FIT }, + + { ORDATA (EDSIW, baci_edsiw, 16), REG_FIT }, + { ORDATA (DSRW, baci_dsrw, 16), REG_FIT }, + { ORDATA (CFCW, baci_cfcw, 16), REG_FIT }, + { ORDATA (ICW, baci_icw, 16), REG_FIT }, + { ORDATA (ISRW, baci_isrw, 16), REG_FIT }, + + { DRDATA (FIFOPUT, baci_fput, 8) }, + { DRDATA (FIFOGET, baci_fget, 8) }, + { DRDATA (FIFOCNTR, baci_fcount, 8) }, + { DRDATA (BRKCNTR, baci_bcount, 16) }, + + { BRDATA (FIFO, baci_fifo, 8, 8, FIFO_SIZE), REG_A }, + { BRDATA (SPCHAR, baci_spchar, 8, 1, 256) }, + + { ORDATA (UARTTHR, baci_uart_thr, 16), REG_FIT | REG_X }, + { ORDATA (UARTTR, baci_uart_tr, 16), REG_NZ | REG_X }, + { ORDATA (UARTRHR, baci_uart_rhr, 16), REG_FIT | REG_X }, + { ORDATA (UARTRR, baci_uart_rr, 16), REG_NZ | REG_X }, + { DRDATA (UARTCLK, baci_uart_clk, 16) }, + + { DRDATA (CTIME, baci_term.wait, 19) }, + + { FLDATA (ENQFLAG, baci_enq_seen, 0), REG_HRO }, + { DRDATA (ENQCNTR, baci_enq_cntr, 16), REG_HRO }, + + { FLDATA (LKO, baci.lockout, 0) }, + { FLDATA (CTL, baci.control, 0) }, + { FLDATA (FLG, baci.flag, 0) }, + { FLDATA (FBF, baci.flag_buffer, 0) }, + { FLDATA (SRQ, baci.srq, 0) }, + + DIB_REGS (baci_dib), + + { NULL } + }; + + +/* Modifier list */ + +static MTAB baci_mod[] = { +/* Mask Value Match Value Print String Match String Validation Display Descriptor */ +/* ------------- ------------- ------------------ ------------ -------------- ------- ---------- */ + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", &baci_set_mode, NULL, NULL }, + { UNIT_DIAG, 0, "terminal mode", "TERMINAL", &baci_set_mode, NULL, NULL }, + + { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, + { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, + + { 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 }, + + { 0 } + }; + + +/* Debugging trace list */ + +static DEBTAB baci_deb [] = { + { "CMDS", DEB_CMDS }, + { "CPU", DEB_CPU }, + { "BUF", DEB_BUF }, + { "XFER", DEB_XFER }, + { "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */ + { 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_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*/ + NULL /* help context */ + }; + + + +/* 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. + Input word type is determined by the state of the control flip-flop. + + The card has the usual control, flag buffer, flag, and SRQ flip-flops. + However, they have the following unusual characteristics: + + - STC is not required to transfer a character. + - Flag is not set after character transfer completes. + - FLAG and SRQ are decoupled and are set independently. + + An interrupt lockout flip-flop is used to prevent the generation of multiple + 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. +*/ + +static SIGNALS_VALUE baci_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) +{ +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) { /* while signals remain */ + signal = IONEXTSIG (working_set); /* isolate the next signal */ + + switch (signal) { /* dispatch the I/O signal */ + + 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"); + + update_status (); /* FLG might set when SRQ clears */ + break; + + + 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 */ + + tprintf (baci_dev, DEB_CMDS, "[STF] Flag, SRQ, and lockout set\n"); + break; + + + 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 */ + 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 */ + if (baci.flag == SET) /* if the flag flip-flop is set */ + outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */ + break; + + + case ioIOI: /* I/O data input */ + if (baci.control) { /* control set? */ + baci_ibuf = TO_CHARCNT (baci_fcount); /* get FIFO count */ + + if (IO_MODE == RECV) /* receiving? */ + baci_ibuf = baci_ibuf | fifo_get (); /* add char and validity flag */ + + 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? */ + outbound.value = baci_status; /* return status */ + + tprintf (baci_dev, DEB_CPU, "[LIx%s] Status = %06o\n", + hold_or_clear, baci_status); + } + break; + + + 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 */ + working_set |= ioSIR; /* assert the Set Interrupt Request backplane signal */ + + tprintf (baci_dev, DEB_CMDS, "[OTx%s] Master reset\n", hold_or_clear); + } + + switch (GET_ID (baci_obuf)) { /* isolate ID code */ + + case 0: /* transmit data */ + if (IO_MODE == XMIT) { /* transmitting? */ + ch = baci_obuf & OUT_DATA; /* mask to character */ + fifo_put (ch); /* queue character */ + + if (baci_term.flags & UNIT_ATT) { /* attached to network? */ + if (TRACING (baci_dev, DEB_CMDS) && /* debugging? */ + (sim_is_active (&baci_term) == 0)) /* service stopped? */ + hp_trace (&baci_dev, DEB_CMDS, "[OTx%s] Terminal service scheduled, " + "time = %d\n", hold_or_clear, baci_term.wait); + + if (baci_fcount == 1) /* first char to xmit? */ + sim_activate_abs (&baci_term, /* start service with full char time */ + baci_term.wait); + else + sim_activate (&baci_term, /* start service if not running */ + baci_term.wait); + } + } + break; + + case 1: /* enable device status interrupt */ + baci_edsiw = baci_obuf; /* load new enable word */ + update_status (); /* may have enabled an interrupt */ + break; + + case 2: /* device status reference */ + if ((baci_term.flags & UNIT_DIAG) && /* diagnostic mode? */ + (baci_dsrw & OUT_DIAG) && /* and last DIAG was high? */ + !(baci_obuf & OUT_DIAG) && /* and new DIAG is low? */ + !(baci_icw & OUT_BAUDRATE)) /* and clock is external? */ + clock_uart (); /* pulse UART clock */ + + baci_dsrw = baci_obuf; /* load new reference word */ + update_status (); /* clocking UART may interrupt */ + break; + + case 3: /* character frame control */ + baci_cfcw = baci_obuf; /* load new frame word */ + break; + + 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 */ + baci_term.wait); + + tprintf (baci_dev, DEB_CMDS, "[OTx%s] Terminal service scheduled, " + "time = %d\n", hold_or_clear, baci_term.wait); + } + + else { /* external rate */ + sim_cancel (&baci_term); /* stop I/O service */ + + tprintf (baci_dev, DEB_CMDS, "[OTx%s] Terminal service stopped\n", + hold_or_clear); + } + } + + baci_icw = baci_obuf; /* load new reference word */ + update_status (); /* loopback may change status */ + break; + + case 5: /* interrupt status reset */ + baci_isrw = baci_obuf; /* load new reset word */ + + mask = (baci_isrw & OUT_IRQCLR) << /* form reset mask */ + IN_V_IRQCLR; /* for common irqs */ + + if (baci_isrw & OUT_CSC) /* add special char mask bit */ + mask = mask | IN_SPCHAR; /* if requested */ + + baci_status = baci_status & ~mask; /* clear specified status bits */ + break; + + case 6: /* special character */ + baci_spchar [baci_obuf & OUT_SPCHAR] = /* set special character entry */ + ((baci_obuf & OUT_SPFLAG) != 0); + break; + } + break; + + + 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 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 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 (!(inbound_signals & ioCLF)) /* STC without ,C ? */ + update_status (); /* clearing lockout might interrupt */ + break; + + + 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.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */ + break; + + + 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; + } + + IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ + } /* and continue until all signals are processed */ + +return outbound; /* return the outbound signals and value */ +} + + +/* BACI terminal service. + + The terminal service routine is used to transmit and receive characters. + + In terminal mode, it is started when a character is ready for output or when + the line poll routine determines that there are characters ready for input + and stopped when there are no more characters to output or input. When the + terminal is quiescent, this routine does not run. + + In diagnostic mode, it is started whenever an internal baud rate is set and + stopped when the external clock is requested. In this mode, the routine will + be called without an attached socket, so character I/O will be skipped. + + Because there is only one FIFO, the card is half-duplex and must be + configured for transmit or receive mode. The UART, though, is double- + buffered, so it may transmit and receive simultaneously. We implement both + the UART shift and holding registers for each mode. + + If a character is received by the UART while the card is in transmit mode, it + will remain in the receiver holding register (RHR). When the mode is + reversed, the RHR contents will be unloaded into the FIFO. Conversely, + transmit mode enables the output of the FIFO to be unloaded into the + transmitter holding register (THR). Characters received or transmitted pass + through the receiver register (RR) or transmitter register (TR), + respectively. They are not strictly necessary in terminal transactions but + are critical to diagnostic operations. + + The UART signals an overrun if a complete character is received while the RHR + still contains the previous character. The BACI does not use this signal, + though; an overrun is only indicated if the FIFO is full, and another + character is received. + + In "fast timing" mode, we defer the recognition of a received character until + the card is put into receive mode for the second or third consecutive ENQ/ACK + handshake. This improves RTE break-mode recognition. "Realistic timing" + mode behaves as the hardware does: a character present in the RHR is unloaded + into the FIFO as soon as receive mode is set. + + Fast timing mode also enables internal ENQ/ACK handshaking. We allow one + character time for the RTE driver to turn the card around, as otherwise the + ACK may not be seen by the driver. Also, the local ACK is supplied after any + received characters, as the driver detects operator attention only when the + first character after an ENQ is not an ACK. + + Finally, fast timing enables buffer combining. For output, all characters + present in the FIFO are unloaded into the line buffer before initiating a + packet send. For input, all characters present in the line buffer are loaded + into the FIFO. This reduces network traffic and decreases simulator overhead + (there is only one service routine entry per block, rather than one per + character). + + In fast output mode, it is imperative that not less than 1500 instructions + elapse between the first character load to the FIFO and the initiation of + transmission. The RTE driver must have enough time to output the maximum + number of contiguous characters (33) and reset the interrupt status flags + before the service routine is entered. Because all of the characters are + transmitted as a block, the FIFO empty flag will be set by the service + routine. If the driver has not yet exited at that time, the buffer-empty + interrupt will be cleared when the interrupt status reset is done. The + symptom will be a 3.8-second pause in output until the driver times out. + + To avoid this, the OTx output character handler does an absolute schedule for + the first character to ensure that a full character time is used. + + + Implementation notes: + + 1. The terminal multiplexer library "tmxr_putc_ln" routine returns + SCPE_STALL if it is called when the transmit buffer is full. When the + last character is added to the buffer, the routine returns SCPE_OK but + also changes the "xmte" field of the terminal multiplexer line (TMLN) + structure from 1 to 0 to indicate that further calls will be rejected. + The "xmte" value is set back to 1 when the transmit buffer empties. + + This presents two approaches to handling buffer overflows: either call + "tmxr_putc_ln" unconditionally and test for SCPE_STALL on return, or call + "tmxr_putc_ln" only if "xmte" is 1. The former approach adds a new + character to the transmit buffer as soon as space is available, while the + latter adds a new character only when the buffer has completely emptied. + With either approach, transmission must be rescheduled after a delay to + allow the buffer to drain. + + It would seem that the former approach is more attractive, as it would + allow the simulated I/O operation to complete more quickly. However, + there are two mitigating factors. First, the library attempts to write + the entire transmit buffer in one host system call, so there is usually + no time difference between freeing one buffer character and freeing the + entire buffer (barring host system buffer congestion). Second, the + routine increments a "character dropped" counter when returning + SCPE_STALL status. However, the characters actually would not be lost, + as the SCPE_STALL return would schedule retransmission when buffer space + is available, . This would lead to erroneous reporting in the SHOW + STATISTICS command. + + Therefore, we adopt the latter approach and reschedule transmission if + the "xmte" field is 0. Note that the "tmxr_poll_tx" routine still must + be called in this case, as it is responsible for transmitting the buffer + contents and therefore freeing space in the buffer. + + 2. The "tmxr_putc_ln" library routine returns SCPE_LOST if the line is not + connected. We ignore this error so that an OS may output an + initialization "welcome" message even when the terminal is not connected. + This permits the simulation to continue while ignoring the output. +*/ + +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 [0].xmte != 0); /* TRUE if the transmit buffer is not full */ + + +/* Transmission */ + +if (baci_ldsc [0].xmte == 0) /* if the transmit buffer is full */ + tprintf (baci_dev, DEB_XFER, "Transmission stalled for full buffer\n"); + + +while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UART? */ + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + baci_uart_tr = baci_uart_thr & data_mask; /* mask data into transmitter register */ + + if ((baci_uart_tr == ENQ) && fast_timing) { /* char is ENQ and fast timing? */ + baci_enq_seen = TRUE; /* set flag instead of transmitting */ + baci_enq_cntr = baci_enq_cntr + 1; /* bump ENQ counter */ + recv_loop = FALSE; /* skip recv to allow time before ACK */ + + tprintf (baci_dev, DEB_XFER, "Character ENQ absorbed internally, " + "ENQ count = %d\n", baci_enq_cntr); + } + + else { /* character is not ENQ or not fast timing */ + baci_enq_cntr = 0; /* reset ENQ counter */ + + if (is_attached) { /* attached to network? */ + status = tmxr_putc_ln (baci_ldsc, /* transmit the character */ + baci_uart_tr); + + if (status == SCPE_OK) /* transmitted OK? */ + tprintf (baci_dev, DEB_XFER, "Character %s transmitted from the UART\n", + fmt_char ((uint8) baci_uart_tr)); + + else { + tprintf (baci_dev, DEB_XFER, "Character %s transmission failed with status %d\n", + fmt_char ((uint8) baci_uart_tr), status); + + if (status == SCPE_LOST) /* if the line is not connected */ + status = SCPE_OK; /* then ignore the output */ + } + } + } + + if (status == SCPE_OK) { /* transmitted OK? */ + baci_uart_tr = CLEAR_R; /* clear transmitter register */ + + if (IO_MODE == XMIT) { /* transmit mode? */ + baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ + baci_uart_thr = fifo_get (); /* get next char into UART */ + update_status (); /* update FIFO status */ + } + + else /* receive mode */ + 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 [0].xmte != 0); /* and buffer space is available */ + } + + else /* otherwise transmission failed */ + xmit_loop = FALSE; /* so drop out of the loop */ + } + + +/* Deferred reception */ + +if (recv_loop && /* ok to process? */ + baci_uart_rhr && (IO_MODE == RECV) && /* and deferred char in RHR in recv mode? */ + (!baci_enq_seen || (baci_enq_cntr >= 2))) { /* and either no ENQ or at least 2nd ENQ? */ + + baci_uart_rhr = baci_uart_rhr & ~IN_VALID; /* clear valid bit */ + + tprintf (baci_dev, DEB_XFER, "Deferred character %s processed\n", + fmt_char ((uint8) baci_uart_rhr)); + + fifo_put ((uint8) baci_uart_rhr); /* move deferred character to FIFO */ + baci_uart_rhr = CLEAR_HR; /* clear RHR */ + update_status (); /* update FIFO status */ + } + + +/* Reception */ + +while (recv_loop) { /* OK to process? */ + 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 */ + + if (baci_uart_rr & SCPE_BREAK) { /* break detected? */ + baci_status = baci_status | IN_BREAK; /* set break status */ + + tprintf (baci_dev, DEB_XFER, "Break detected\n"); + } + + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + baci_uart_rhr = (uint16) (baci_uart_rr & data_mask); /* mask data into holding register */ + baci_uart_rr = CLEAR_R; /* clear receiver register */ + + tprintf (baci_dev, DEB_XFER, "Character %s received by the UART\n", + fmt_char ((uint8) baci_uart_rhr)); + + if (baci_term.flags & UNIT_CAPSLOCK) /* caps lock mode? */ + 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 */ + + 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 && baci.flag_buffer == CLEAR; /* loop if fast mode and no IRQ */ + } + + else { /* xmit or ENQ/ACK, leave char in RHR */ + baci_uart_rhr = baci_uart_rhr | IN_VALID; /* set character valid bit */ + recv_loop = FALSE; /* terminate loop */ + } + } + + +/* Housekeeping */ + +if (recv_loop && baci_enq_seen) { /* OK to process and ENQ seen? */ + baci_enq_seen = FALSE; /* reset flag */ + + tprintf (baci_dev, DEB_XFER, "Character ACK generated internally\n"); + + fifo_put (ACK); /* fake ACK from terminal */ + update_status (); /* update FIFO status */ + } + +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? */ + sim_activate (uptr, uptr->wait); /* reschedule service */ +else + tprintf (baci_dev, DEB_CMDS, "Terminal service stopped\n"); + +return status; +} + + +/* BACI line poll service. + + This service routine is used to poll for connections and incoming characters. + If characters are available, the terminal I/O service routine is scheduled. + It starts when the line is attached and stops when the line is detached. + + + Implementation notes: + + 1. Even though there is only one line, we poll for new connections + unconditionally. This is so that "tmxr_poll_conn" will report "All + connections busy" to a second Telnet connection. Otherwise, the user's + client would connect but then would be silently unresponsive. +*/ + +static t_stat baci_poll_svc (UNIT *uptr) +{ +if (tmxr_poll_conn (&baci_desc) >= 0) /* if new connection is established */ + 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? */ + sim_activate (&baci_term, baci_term.wait); /* activate I/O service */ + +if (uptr->wait == POLL_FIRST) /* first poll? */ + uptr->wait = hp_sync_poll (INITIAL); /* initial synchronization */ +else /* not first */ + uptr->wait = hp_sync_poll (SERVICE); /* continue synchronization */ + +sim_activate (uptr, uptr->wait); /* continue polling */ + +return SCPE_OK; +} + + +/* Simulator reset routine */ + +static t_stat baci_reset (DEVICE *dptr) +{ +io_assert (&baci_dev, ioa_POPIO); /* PRESET the device */ + +baci_ibuf = 0; /* clear input buffer */ +baci_obuf = 0; /* clear output buffer */ +baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */ + +baci_enq_seen = FALSE; /* reset ENQ seen flag */ +baci_enq_cntr = 0; /* clear ENQ counter */ + +baci_term.wait = service_time (baci_icw); /* set terminal I/O time */ + +if (baci_term.flags & UNIT_ATT) { /* device attached? */ + baci_poll.wait = POLL_FIRST; /* set up poll */ + sim_activate (&baci_poll, baci_poll.wait); /* start line poll immediately */ + } +else + sim_cancel (&baci_poll); /* else stop line poll */ + +return SCPE_OK; +} + + +/* Attach line */ + +static t_stat baci_attach (UNIT *uptr, char *cptr) +{ +t_stat status; + +if (uptr->flags & UNIT_DIAG) /* if the BACI is in diagnostic mode */ + return SCPE_NOFNC; /* then the command is not allowed */ + +else if (isdigit (*cptr)) /* otherwise if a network port number is specified */ + baci_term.flags |= UNIT_ATTABLE; /* then designate the unit as socket-attachable */ + +else /* otherwise */ + baci_term.flags &= ~UNIT_ATTABLE; /* assume a serial port connection */ + +status = tmxr_attach_unit (&baci_desc, uptr, uptr, cptr); /* attach the line */ + +if (status == SCPE_OK) { /* if the attach succeeded */ + baci_poll.wait = POLL_FIRST; /* then set up the poll */ + sim_activate (&baci_poll, baci_poll.wait); /* and begin polling */ + } + +return status; /* return the attachment status */ +} + + +/* Detach line */ + +static t_stat baci_detach (UNIT *uptr) +{ +t_stat status; + +status = tmxr_detach_unit (&baci_desc, uptr, uptr); /* detach the line */ + +if (status == SCPE_OK) { /* if the detach succeeded */ + baci_ldsc [0].rcve = 0; /* then disable line reception */ + + sim_cancel (uptr); /* cancel any pending line I/O */ + sim_cancel (&baci_poll); /* and stop polling */ + + baci_term.flags |= UNIT_ATTABLE; /* restore socket-attachable status */ + } + +return status; /* return the command status */ +} + + +/* Set DIAGNOSTIC/TERMINAL mode */ + +static t_stat baci_set_mode (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (value) /* if we're setting DIAGNOSTIC mode */ + baci_detach (uptr); /* then detach any existing connection */ + +return SCPE_OK; /* return success */ +} + + +/* Local routines */ + + +/* Master reset. + + This is the programmed card master reset, not the simulator reset routine. + Master reset normally clears the UART registers. However, if we are in "fast + timing" mode, the receiver holding register may hold a deferred character. + In this case, we do not clear the RHR, unless we are called from the + simulator reset routine. + + The HP BACI manual states that master reset "Clears Service Request (SRQ)." + An examination of the schematic, though, shows that it sets SRQ instead. +*/ + +static void master_reset (void) +{ +baci_fput = baci_fget = 0; /* clear FIFO indexes */ +baci_fcount = 0; /* clear FIFO counter */ +memset (baci_fifo, 0, sizeof (baci_fifo)); /* clear FIFO data */ + +baci_uart_thr = CLEAR_HR; /* clear transmitter holding register */ + +if (!(baci_term.flags & UNIT_FASTTIME)) /* real time mode? */ + baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */ + +baci_uart_tr = CLEAR_R; /* clear transmitter register */ +baci_uart_rr = CLEAR_R; /* clear receiver register */ + +baci_uart_clk = 0; /* clear UART clock */ +baci_bcount = 0; /* clear break counter */ + +baci.control = CLEAR; /* clear control */ +baci.flag = baci.flag_buffer = SET; /* set flag and flag buffer */ +baci.srq = SET; /* set SRQ */ +baci.lockout = SET; /* set lockout flip-flop */ + +baci_edsiw = 0; /* clear interrupt enables */ +baci_dsrw = 0; /* clear status reference */ +baci_cfcw = baci_cfcw & ~OUT_ECHO; /* clear echo flag */ +baci_icw = baci_icw & OUT_BAUDRATE; /* clear interface control */ + +if (baci_term.flags & UNIT_DIAG) { /* diagnostic mode? */ + baci_status = baci_status & ~IN_MODEM | IN_SPARE; /* clear loopback status, set BA */ + baci_ldsc [0].xmte = 1; /* enable transmitter */ + } + +return; +} + + +/* Update status. + + In diagnostic mode, several of the modem output lines are looped back to the + input lines. Also, CD is tied to BB (received data), which is presented on + the TEST status bit via an inversion. Echo mode couples BB to BA + (transmitted data), which is presented on the SPARE status bit. + + If a modem line interrupt condition is present and enabled, the DEVINT status + bit is set. Other potential "standard" interrupt sources are the special + character, break detected, and overrun/parity error bits. If DCPC transfers + are not selected, then the FIFO interrupts (buffer empty, half-full, and + full) and the "data ready" condition (i.e., receive and character modes + enabled and FIFO not empty) also produces an interrupt request. + + An interrupt request will set the card flag unless either the lockout or SRQ + flip-flops are set. SRQ will set if DCPC mode is enabled and there is room + (transmit mode) or data (receive mode) in the FIFO. +*/ + +static void update_status (void) +{ +if (baci_term.flags & UNIT_DIAG) { /* diagnostic mode? */ + baci_status = baci_status & ~IN_DIAG; /* clear loopback flags */ + + if (baci_icw & OUT_SXX) /* SCA to SCF and CF */ + baci_status = baci_status | IN_SXX | IN_CF; + if ((baci_icw & OUT_CA) && (baci_fcount < 128)) /* CA to CC and CE */ + baci_status = baci_status | IN_CC | IN_CE; + if (baci_icw & OUT_CD) /* CD to CB */ + baci_status = baci_status | IN_CB; + else { + baci_status = baci_status | IN_TEST; /* BB is inversion of CD */ + if (baci_cfcw & OUT_ECHO) + baci_status = baci_status | IN_SPARE; /* BB couples to BA with echo */ + } + + if (!(baci_cfcw & OUT_ECHO) && (baci_uart_tr & 1)) /* no echo and UART TR set? */ + baci_status = baci_status | IN_SPARE; /* BA to SPARE */ + } + +if (baci_edsiw & (baci_status ^ baci_dsrw) & IN_MODEM) /* device interrupt? */ + baci_status = baci_status | IN_DEVINT; /* set flag */ + +if ((baci_status & IN_STDIRQ) || /* standard interrupt? */ + !(baci_icw & OUT_DCPC) && /* or under program control */ + (baci_status & IN_FIFOIRQ) || /* and FIFO interrupt? */ + (IO_MODE == RECV) && /* or receiving */ + (baci_edsiw & OUT_ENCM) && /* and char mode */ + (baci_fget != baci_fput)) { /* and FIFO not empty? */ + + if (baci.lockout) /* interrupt lockout? */ + tprintf (baci_dev, DEB_CMDS, "Lockout prevents flag set, status = %06o\n", + baci_status); + + else if (baci.srq) /* SRQ set? */ + tprintf (baci_dev, DEB_CMDS, "SRQ prevents flag set, status = %06o\n", + baci_status); + + else { + 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); + } + } + +if ((baci_icw & OUT_DCPC) && /* DCPC enabled? */ + ((IO_MODE == XMIT) && (baci_fcount < 128) || /* and xmit and room in FIFO */ + (IO_MODE == RECV) && (baci_fcount > 0))) /* or recv and data in FIFO? */ + if (baci.lockout) /* interrupt lockout? */ + tprintf (baci_dev, DEB_CMDS, "Lockout prevents SRQ set, status = %06o\n", + baci_status); + + else { + 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); + } + +return; +} + + +/* Calculate service time from baud rate. + + Service times are based on 1580 instructions per millisecond, which is the + 1000 E-Series execution speed. The "external clock" rate uses the 9600 baud + rate, as most real terminals were set to their maximum rate. + + Note that the RTE driver has a race condition that will trip if the service + time is less than 1500 instructions. Therefore, these times cannot be + shortened arbitrarily. +*/ + +static int32 service_time (uint32 control_word) +{ +/* Baud Rates 0- 7 : ext., 50, 75, 110, 134.5, 150, 300, 600, */ +/* Baud Rates 8-15 : 900, 1200, 1800, 2400, 3600, 4800, 7200, 9600 */ +static const int32 ticks [] = { 1646, 316000, 210667, 143636, 117472, 105333, 52667, 26333, + 17556, 13667, 8778, 6583, 4389, 3292, 2194, 1646 }; + +return ticks [GET_BAUDRATE (control_word)]; /* return service time for indicated rate */ +} + + +/* FIFO manipulation routines. + + The BACI is a half-duplex device that has a single 128-byte FIFO that is used + for both transmitting and receiving. Whether the FIFO is connected to the + input or output of the UART is determined by the XMIT bit in word 4. A + separate 8-bit FIFO up/down counter is used to track the number of bytes + available. FIFO operations are complicated slightly by the UART, which is + double-buffered. + + The FIFO is modeled as a circular 128-byte array. Separate get and put + indexes track the current data extent. A FIFO character counter is used to + derive empty, half-full, and full status indications, and counts greater than + 128 are possible. + + In the transmit mode, an OTA/B with word type 0 generates SI (shift in) to + load the FIFO and increment the FIFO counter. When the UART is ready for a + character, THRE (UART transmitter holding register empty) and OR (FIFO output + ready) generate THRL (transmitter holding register load) and SO (FIFO shift + out) to unload the FIFO into the UART. When transmission of the character + over the serial line is complete, TRE (UART transmitter register empty) + decrements the FIFO counter. + + In the receive mode, the UART sets DR (data received) when has obtained a + character, which generates SI (FIFO shift in) to load the FIFO and increment + the FIFO counter. This also clocks PE (UART parity error) and IR (FIFO input + ready) into the overrun/parity error flip-flop. An LIA/B with control set + and with OR (FIFO output ready) set, indicating valid data is available, + generates SO (FIFO shift out) to unload the FIFO and decrement the FIFO + counter. + + Presuming an empty FIFO and UART, double-buffering in the transmit mode means + that the first byte deposited into the FIFO is removed and loaded into the + UART transmitter holding register. Even though the FIFO is actually empty, + the FIFO counter remains at 1, because FIFO decrement does not occur until + the UART actually transmits the data byte. The intended mode of operation is + to wait until the buffer-empty interrupt occurs, which will happen when the + final character is transmitted from the UART, before switching the BACI into + receive mode. The counter will match the FIFO contents properly, i.e., will + be zero, when the UART transmission completes. + + However, during diagnostic operation, FIFO testing will take this "extra" + count into consideration. For example, after a master reset, if ten bytes + are written to the FIFO in transmit mode, the first byte will pass through to + the UART transmitter holding register, and the next nine bytes will fill the + FIFO. The first byte read in receive mode will be byte 2, not byte 1; the + latter remains in the UART. After the ninth byte is read, OR (FIFO output + ready) will drop, resetting the valid data flip-flop and inhibiting any + further FIFO counter decrement pulses. The counter will remain at 1 until + another master reset is done. + + The same situation occurs in the RTE driver during ENQ/ACK handshakes. The + driver sets the card to transmit mode, sends an ENQ, waits for a short time + for the character to "bubble through" the FIFO and into the UART transmitter + holding register, and then switches the card to receive mode to await the + interrupt from the reception of the ACK. This is done to avoid the overhead + of the interrupt after the ENQ is transmitted. However, switching the card + into receive mode before the ENQ is actually transmitted means that the FIFO + counter will not decrement when that occurs, leaving the counter in an "off + by one" configuration. To remedy this, the driver does a master reset after + the ACK is received. + + Therefore, for proper operation, we must simulate both the UART + double-buffering and the decoupling of the FIFO and FIFO character counter. +*/ + + +/* Get a character from the FIFO. + + In receive mode, getting a character from the FIFO decrements the character + counter concurrently. In transmit mode, the counter must not be decremented + until the character is actually sent; in this latter case, the caller is + responsible for decrementing. Attempting to get a character when the FIFO is + empty returns the last valid data and does not alter the FIFO indexes. + + Because the FIFO counter may indicate more characters than are actually in + the FIFO, the count is not an accurate indicator of FIFO fill status. We + account for this by examining the get and put indexes. If these are equal, + then the FIFO is either empty or exactly full. We differentiate by examining + the FIFO counter and seeing if it is >= 128, indicating an (over)full + condition. If it is < 128, then the FIFO is empty, even if the counter is + not 0. +*/ + +static uint16 fifo_get (void) +{ +uint16 data; + +data = baci_fifo [baci_fget]; /* get character */ + +if ((baci_fget != baci_fput) || (baci_fcount >= 128)) { /* FIFO occupied? */ + if (IO_MODE == RECV) /* receive mode? */ + baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ + + tprintf (baci_dev, DEB_BUF, "Character %s get from FIFO [%d], " + "character counter = %d\n", + fmt_char ((uint8) data), baci_fget, baci_fcount); + + baci_fget = (baci_fget + 1) % FIFO_SIZE; /* bump index modulo array size */ + + if (baci_spchar [data]) /* is it a special character? */ + data = data | IN_SPFLAG; /* set flag */ + + data = data | IN_VALID; /* set valid flag in return */ + } + +else /* FIFO empty */ + tprintf (baci_dev, DEB_BUF, "Attempted get on empty FIFO, " + "character count = %d\n", baci_fcount); + +if (baci_fcount == 0) /* count now zero? */ + baci_status = baci_status | IN_BUFEMPTY; /* set buffer empty flag */ + +update_status (); /* update FIFO status */ + +return data; /* return character */ +} + + +/* Put a character into the FIFO. + + In transmit mode, available characters are unloaded from the FIFO into the + UART transmitter holding register as soon as the THR is empty. That is, + given an empty FIFO and THR, a stored character will pass through the FIFO + and into the THR immediately. Otherwise, the character will remain in the + FIFO. In either case, the FIFO character counter is incremented. + + In receive mode, characters are only unloaded from the FIFO explicitly, so + stores always load the FIFO and increment the counter. +*/ + +static void fifo_put (uint8 ch) +{ +uint32 index = 0; +t_bool pass_thru; + +pass_thru = (IO_MODE == XMIT) && /* pass thru if XMIT and THR empty */ + !(baci_uart_thr & IN_VALID); + +if (pass_thru) /* pass char thru to UART */ + baci_uart_thr = ch | IN_VALID; /* and set valid character flag */ + +else { /* RECV or THR occupied */ + index = baci_fput; /* save current index */ + baci_fifo [baci_fput] = ch; /* put char in FIFO */ + baci_fput = (baci_fput + 1) % FIFO_SIZE; /* bump index modulo array size */ + } + +baci_fcount = baci_fcount + 1; /* increment occupancy counter */ + +if (pass_thru) + tprintf (baci_dev, DEB_BUF, "Character %s put to UART transmitter holding register, " + "character counter = 1\n", fmt_char (ch)); +else + tprintf (baci_dev, DEB_BUF, "Character %s put to FIFO [%d], " + "character counter = %d\n", fmt_char (ch), index, baci_fcount); + +if ((IO_MODE == RECV) && (baci_spchar [ch])) /* receive mode and special character? */ + baci_status = baci_status | IN_SPCHAR; /* set special char seen flag */ + +if (baci_fcount == 64) /* FIFO half full? */ + baci_status = baci_status | IN_BUFHALF; + +else if (baci_fcount == 128) /* FIFO completely full? */ + baci_status = baci_status | IN_BUFFULL; + +else if (baci_fcount > 128) /* FIFO overrun? */ + baci_status = baci_status | IN_OVRUNPE; + +update_status (); /* update FIFO status */ + +return; +} + + +/* Clock the UART. + + In the diagnostic mode, the DIAG output is connected to the EXT CLK input. + If the baud rate of the Interface Control Word is set to "external clock," + then raising and lowering the DIAG output will pulse the UART transmitter and + receiver clock lines, initiating transmission or reception of serial data. + Sixteen pulses are needed to shift one bit through the UART. + + The diagnostic hood ties CD to BB (received data), so bits presented to CD + via the Interface Control Word can be clocked into the UART receiver register + (RR). Similarly, the UART transmitter register (TR) shifts data onto BA + (transmitted data), and the hood ties BA to SPARE, so transmitted bits are + presented to the SPARE bit in the status word. + + "baci_uart_clk" contains the number of clock pulses remaining for the current + character transfer. Calling this routine with "baci_uart_clk" = 0 initiates + a transfer. The value will be a multiple of 16 and will account for the + start bit, the data bits, the optional parity bit, and the stop bits. The + transfer terminates when the count reaches zero (or eight, if 1.5 stop bits + is selected during transmission). + + Every sixteen pulses when the lower four bits of the clock count are zero, + the transmitter or receiver register will be shifted to present or receive a + new serial bit. The registers are initialized to all ones for proper + handling of the stop bits. + + A break counter is maintained and incremented whenever a space (0) condition + is seen on the serial line. After 160 clock times (10 bits) of continuous + zero data, the "break seen" status is set. + + This routine is not used in terminal mode. +*/ + +static void clock_uart (void) +{ +uint32 uart_bits, data_bits, data_mask, parity, bit_low, i; + +if (baci_uart_clk > 0) { /* transfer in progress? */ + bit_low = (baci_icw & OUT_CD); /* get current receive bit */ + + if ((baci_uart_clk & 017) == 0) /* end of a bit? */ + if (IO_MODE == XMIT) /* transmit? */ + 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 ? ~D16_SIGN : ~0u); /* (inverted sense) */ + + if (bit_low) { /* another low bit? */ + baci_bcount = baci_bcount + 1; /* update break counter */ + + if (baci_bcount == 160) { /* break held long enough? */ + baci_status = baci_status | IN_BREAK; /* set break flag */ + + tprintf (baci_dev, DEB_XFER, "Break detected\n"); + } + } + + else /* high bit? */ + baci_bcount = 0; /* reset break counter */ + + baci_uart_clk = baci_uart_clk - 1; /* decrement clocks remaining */ + + if ((IO_MODE == XMIT) && /* transmit mode? */ + ((baci_uart_clk == 0) || /* and end of character? */ + (baci_uart_clk == 8) && /* or last stop bit */ + (baci_cfcw & OUT_STBITS) && /* and extra stop bit requested */ + ((baci_cfcw & OUT_CHARSIZE) == 0))) { /* and 1.5 stop bits used? */ + + baci_uart_clk = 0; /* clear clock count */ + + baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ + baci_uart_thr = fifo_get (); /* get next char into THR */ + update_status (); /* update FIFO status */ + + tprintf (baci_dev, DEB_XFER, "UART transmitter empty, " + "holding register = %06o\n", baci_uart_thr); + } + + else if ((IO_MODE == RECV) && /* receive mode? */ + (baci_uart_clk == 0)) { /* and end of character? */ + + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + + uart_bits = data_bits + /* calculate UART bits as data bits */ + ((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */ + ((baci_cfcw & OUT_STBITS) != 0); /* plus extra stop bit if used */ + + baci_uart_rhr = (uint16) (baci_uart_rr >> (16 - uart_bits)); /* position data to right align */ + baci_uart_rr = CLEAR_R; /* clear receiver register */ + + tprintf (baci_dev, DEB_XFER, "UART receiver = %06o (%s)\n", + baci_uart_rhr, fmt_char ((uint8) (baci_uart_rhr & data_mask))); + + fifo_put ((uint8) (baci_uart_rhr & data_mask)); /* put data in FIFO */ + update_status (); /* update FIFO status */ + + if (baci_cfcw & OUT_PARITY) { /* parity present? */ + data_mask = data_mask << 1 | 1; /* widen mask to encompass parity */ + uart_bits = baci_uart_rhr & data_mask; /* get data plus parity */ + + parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */ + + for (i = 0; i < data_bits + 1; i++) { /* calc parity of data + parity bit */ + parity = parity ^ uart_bits; /* parity calculated in LSB */ + uart_bits = uart_bits >> 1; + } + + if (parity & 1) { /* parity error? */ + baci_status = baci_status | IN_OVRUNPE; /* report it */ + + tprintf (baci_dev, DEB_XFER, "Parity error detected\n"); + } + } + } + } + +if ((baci_uart_clk == 0) && /* start of transfer? */ + ((IO_MODE == RECV) || /* and receive mode */ + (baci_uart_thr & IN_VALID))) { /* or character ready to transmit? */ + + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + + uart_bits = data_bits + /* calculate UART bits as data bits */ + ((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */ + 2 + ((baci_cfcw & OUT_STBITS) != 0); /* plus start/stop and extra stop if used */ + + baci_uart_clk = 16 * uart_bits; /* calculate clocks pulses expected */ + + if (IO_MODE == XMIT) { /* transmit mode? */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + baci_uart_tr = baci_uart_thr & data_mask; /* mask data into holding register */ + + if (baci_cfcw & OUT_PARITY) { /* add parity to this transmission? */ + uart_bits = baci_uart_tr; /* copy data bits */ + parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */ + + for (i = 0; i < data_bits; i++) { /* calculate parity of data */ + parity = parity ^ uart_bits; /* parity calculated in LSB */ + uart_bits = uart_bits >> 1; + } + + data_mask = data_mask << 1 | 1; /* extend mask for the parity bit */ + baci_uart_tr = baci_uart_tr | /* include parity in transmission register */ + (parity & 1) << data_bits; /* (mask to parity bit and position it) */ + } + + 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 & D16_MASK, + fmt_char ((uint8) (baci_uart_thr & data_mask)), + baci_uart_clk); + } + + else { + baci_uart_rr = CLEAR_R; /* clear receiver register */ + + tprintf (baci_dev, DEB_XFER, "UART receiver empty, " + "clock count = %d\n", baci_uart_clk); + } + } + +return; +} diff --git a/HP2100/hp2100_cpu.c b/HP2100/hp2100_cpu.c new file mode 100644 index 00000000..52e5a3c0 --- /dev/null +++ b/HP2100/hp2100_cpu.c @@ -0,0 +1,5234 @@ +/* hp2100_cpu.c: HP 21xx/1000 Central Processing Unit simulator + + Copyright (c) 1993-2016, Robert M. Supnik + 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 + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the authors. + + CPU 2114C/2115A/2116C/2100A/1000-M/E/F Central Processing Unit + I/O subsystem + Power Fail Recovery System + + 08-Dec-19 JDB Added "hp_reset_poll" call to "cpu_reset" + 03-Jul-19 JDB Substituted BP_EXEC for explicit 'E' switch in sim_brk_test + 08-Apr-19 JDB Suppress stop messages for step and breakpoints in DO files + 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 + 02-Apr-18 JDB SET CPU 21MX now configures an M-Series model + 22-Feb-18 JDB Reworked "cpu_ibl" into "cpu_copy_loader" + 11-Aug-17 JDB MEM must be disabled when DMS is disabled + 01-Aug-17 JDB Changed SET/SHOW CPU [NO]IDLE to use sim_*_idle routines + 22-Jul-17 JDB Renamed "intaddr" to CIR; added IR + 18-Jul-17 JDB Added CPU stops + 11-Jul-17 JDB Moved "hp_enbdis_pair" to hp2100_sys.c + Renamed "ibl_copy" to "cpu_ibl" + 10-Jul-17 JDB Renamed the global routine "iogrp" to "cpu_iog" + 07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool + 26-Jun-17 JDB Moved I/O instruction subopcode constants from hp2100_defs.h + 16-May-17 JDB Changed REG_A, REG_B to REG_X + 19-Apr-17 JDB SET CPU IDLE now omits idle loop tracing + 04-Apr-17 JDB Added "cpu_configuration" for symbolic ex/dep validation + Rejected model change no longer changes options + 21-Mar-17 JDB IOP is now illegal on the 1000 F-Series + 27-Feb-17 JDB Added BBL load for 21xx machines + ibl_copy no longer returns a status code + 22-Feb-17 JDB Added DMA tracing + 21-Feb-17 JDB Added bus tracing to the I/O dispatcher + 19-Jan-17 JDB Added CPU tracing + Consolidated the memory read and write routines + 05-Aug-16 JDB Renamed the P register from "PC" to "PR" + 13-May-16 JDB Modified for revised SCP API function parameter types + 31-Dec-14 JDB Corrected devdisp data parameters + 30-Dec-14 JDB Added S-register parameters to ibl_copy + 24-Dec-14 JDB Added casts for explicit downward conversions + 18-Mar-13 JDB Removed redundant extern declarations + 05-Feb-13 JDB HLT instruction handler now relies on sim_vm_fprint_stopped + 09-May-12 JDB Separated assignments from conditional expressions + 13-Jan-12 JDB Minor speedup in "is_mapped" + Added casts to cpu_mod, dmasio, dmapio, cpu_reset, dma_reset + 07-Apr-11 JDB Fixed I/O return status bug for DMA cycles + Failed I/O cycles now stop on failing instruction + 28-Mar-11 JDB Tidied up signal handling + 29-Oct-10 JDB Revised DMA for new multi-card paradigm + Consolidated DMA reset routines + DMA channels renamed from 0,1 to 1,2 to match documentation + 27-Oct-10 JDB Changed I/O instructions, handlers, and DMA for revised signal model + Changed I/O dispatch table to use DIB pointers + 19-Oct-10 JDB Removed DMA latency counter + 13-Oct-10 JDB Fixed DMA requests to enable stealing every cycle + Fixed DMA priority for channel 1 over channel 2 + Corrected comments for "cpu_set_idle" + 30-Sep-08 JDB Breakpoints on interrupt trap cells now work + 05-Sep-08 JDB VIS and IOP are now mutually exclusive on 1000-F + 11-Aug-08 JDB Removed A/B shadow register variables + 07-Aug-08 JDB Moved hp_setdev, hp_showdev to hp2100_sys.c + Moved non-existent memory checks to WritePW + 05-Aug-08 JDB Fixed mp_dms_jmp to accept lower bound, check write protection + 30-Jul-08 JDB Corrected DMS violation register set conditions + Redefined ABORT to pass address, moved def to hp2100_cpu.h + Combined dms and dms_io routines + 29-Jul-08 JDB JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort + 11-Jul-08 JDB Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA + 26-Jun-08 JDB Rewrote device I/O to model backplane signals + EDT no longer passes DMA channel + 30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag + 28-Apr-08 JDB Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE + 24-Apr-08 JDB Fixed single stepping through interrupts + 20-Apr-08 JDB Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags + 03-Dec-07 JDB Memory ex/dep and bkpt type default to current map mode + 26-Nov-07 JDB Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA + 15-Nov-07 JDB Corrected MP W5 (JSB) jumper action, SET/SHOW reversal, + mp_mevff clear on interrupt with I/O instruction in trap cell + 04-Nov-07 JDB Removed DBI support from 1000-M (was temporary for RTE-6/VM) + 28-Apr-07 RMS Removed clock initialization + 02-Mar-07 JDB EDT passes input flag and DMA channel in dat parameter + 11-Jan-07 JDB Added 12578A DMA byte packing + 28-Dec-06 JDB CLC 0 now sends CRS instead of CLC to devices + 26-Dec-06 JDB Fixed improper IRQ deferral for 21xx CPUs + Fixed improper interrupt servicing in resolve + 21-Dec-06 JDB Added 21xx loader enable/disable support + 16-Dec-06 JDB Added 2114 and 2115 CPU options. + Added support for 12607B (2114) and 12578A (2115/6) DMA + 01-Dec-06 JDB Added 1000-F CPU option (requires HAVE_INT64) + SHOW CPU displays 1000-M/E instead of 21MX-M/E + 16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c + 12-Oct-06 JDB Fixed INDMAX off-by-one error in resolve + 26-Sep-06 JDB Added iotrap parameter to UIG dispatchers for RTE microcode + 12-Sep-06 JDB iogrp returns NOTE_IOG to recalc interrupts + resolve returns NOTE_INDINT to service held-off interrupt + 16-Aug-06 JDB Added support for future microcode options, future F-Series + 09-Aug-06 JDB Added double integer microcode, 1000-M/E synonyms + Enhanced CPU option validity checking + Added DCPC as a synonym for DMA for 21MX simulations + 26-Dec-05 JDB Improved reporting in dev_conflict + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 21-Jan-05 JDB Reorganized CPU option flags + 15-Jan-05 RMS Split out EAU and MAC instructions + 26-Dec-04 RMS DMA reset doesn't clear alternate CTL flop (from Dave Bryan) + DMA reset shouldn't clear control words (from Dave Bryan) + Alternate CTL flop not visible as register (from Dave Bryan) + Fixed CBS, SBS, TBS to perform virtual reads + Separated A/B from M[0/1] for DMA IO (from Dave Bryan) + Fixed bug in JPY (from Dave Bryan) + 25-Dec-04 JDB Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E) + TIMER/EXECUTE/DIAG instructions disabled for 21MX-M + T-register reflects changes in M-register when halted + 25-Sep-04 JDB Moved MP into its own device; added MP option jumpers + Modified DMA to allow disabling + Modified SET CPU 2100/2116 to truncate memory > 32K + Added -F switch to SET CPU to force memory truncation + Fixed S-register behavior on 2116 + Fixed LIx/MIx behavior for DMA on 2116 and 2100 + Fixed LIx/MIx behavior for empty I/O card slots + Modified WRU to be REG_HRO + Added BRK and DEL to save console settings + Fixed use of "unsigned int16" in cpu_reset + Modified memory size routine to return SCPE_INCOMP if + memory size truncation declined + 20-Jul-04 RMS Fixed bug in breakpoint test (reported by Dave Bryan) + Back up PC on instruction errors (from Dave Bryan) + 14-May-04 RMS Fixed bugs and added features from Dave Bryan + - SBT increments B after store + - DMS console map must check dms_enb + - SFS x,C and SFC x,C work + - MP violation clears automatically on interrupt + - SFS/SFC 5 is not gated by protection enabled + - DMS enable does not disable mem prot checks + - DMS status inconsistent at simulator halt + - Examine/deposit are checking wrong addresses + - Physical addresses are 20b not 15b + - Revised DMS to use memory rather than internal format + - Added instruction printout to HALT message + - Added M and T internal registers + - Added N, S, and U breakpoints + Revised IBL facility to conform to microcode + Added DMA EDT I/O pseudo-opcode + Separated DMA SRQ (service request) from FLG + 12-Mar-03 RMS Added logical name support + 02-Feb-03 RMS Fixed last cycle bug in DMA output (found by Mike Gemeny) + 22-Nov-02 RMS Added 21MX IOP support + 24-Oct-02 RMS Fixed bugs in IOP and extended instructions + Fixed bugs in memory protection and DMS + Added clock calibration + 25-Sep-02 RMS Fixed bug in DMS decode (found by Robert Alan Byer) + 26-Jul-02 RMS Restructured extended instructions, added IOP support + 22-Mar-02 RMS Changed to allocate memory array dynamically + 11-Mar-02 RMS Cleaned up setjmp/auto variable interaction + 17-Feb-02 RMS Added DMS support + Fixed bugs in extended instructions + 03-Feb-02 RMS Added terminal multiplexor support + Changed PCQ macro to use unmodified PC + Fixed flop restore logic (found by Bill McDermith) + Fixed SZx,SLx,RSS bug (found by Bill McDermith) + Added floating point support + 16-Jan-02 RMS Added additional device support + 07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith) + 07-Dec-01 RMS Revised to use breakpoint package + 03-Dec-01 RMS Added extended SET/SHOW support + 10-Aug-01 RMS Removed register in declarations + 26-Nov-00 RMS Fixed bug in dual device number routine + 21-Nov-00 RMS Fixed bug in reset routine + 15-Oct-00 RMS Added dynamic device number support + + References: + - 2100A Computer Reference Manual + (02100-90001, December 1971) + - Model 2100A Computer Installation and Maintenance Manual + (02100-90002, August 1972) + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, March 1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, March 1981) + - HP 1000 Computer Real-Time Systems + (5091-4479, August 1992) + + + Hewlett-Packard sold the HP 21xx/1000 family of real-time computers from 1966 + through 2000. There are three major divisions within this family: the 21xx + core-memory machines, the 1000 (originally 21MX) M/E/F-Series semiconductor- + memory machines, and the 1000 L/A-Series machines. All machines are 16-bit + accumulator-oriented CISC machines running the same base instruction set. A + wide range of operating systems run on these machines, from a simple 4K word + paper-tape-based monitor to a megaword multi-user, multiprogramming disc- + based system and a multi-user time-shared BASIC system. + + This implementation is a simulator for the 2114, 2115, 2116, 2100, and 1000 + M/E/F-Series machines. A large variety of CPU options, device interface + cards, and peripherals are provided. High-speed I/O transfers are performed + by Direct Memory Access and Dual-Channel Port Controller options. This + 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 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 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 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 + Memory Reference Group, the Shift-Rotate Group, the Alter-Skip Group, and the + I/O Group. SRG instruction words may contain from one to four suboperation + codes that are executed from left-to-right, and ASG instruction words may + contain from one to eight suboperations. An optional Extended Arithmetic + Unit may be added to the 2115 and 2116 that provides hardware multiply and + divide, double-load and -store, and double-word shift and rotate + instructions. + + 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. + 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 + instructions. + + The 1000 M/E-Series machines also use microprogrammed processors and extend + the 2100 instruction set with two new index registers, X and Y, and a new + Extended Instruction Group consisting of 32 index-register instructions and + 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 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 + address selects one of 32 ten-bit map registers containing physical page + numbers. The ten-bit page number combined with the ten-bit page offset + yields a 20-bit physical address capable of accessing a location in a + one-megaword memory. DMS provides separate maps for system and user + programs, as well as for the two DCPC channels, and includes microcode that + implements the 38 Dynamic Mapping Instructions used to manipulate the mapping + system. + + Optional memory protection is accomplished by dividing the logical address + space into protected and unprotected parts. When protection is enabled, any + 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 + M-Series, and the 21MXE (occasionally, 21XE) corresponds to the 1000 + E-Series. The model numbers were changed before the introduction of the 1000 + F-Series, although some internal HP documentation refers to this machine as + the 21MXF. + + The terms MEM (Memory Expansion Module), MEU (Memory Expansion Unit), DMI + (Dynamic Mapping Instructions), and DMS (Dynamic Mapping System) are used + somewhat interchangeably to refer to the logical-to-physical memory address + translation option provided on the 1000-Series. DMS consists of the MEM card + (12731A) and the DMI firmware (13307A). However, MEM and MEU have been used + interchangeably to refer to the mapping card, as have DMI and DMS to refer to + the firmware instructions. + + + These CPU hardware registers are present in all machines: + + 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 (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: + + Name Width Description + ---- ----- ---------------------------------------------- + X 16 index register + Y 16 index register + + The data types supported by the base instruction set are: + + - 8-bit unsigned byte + - 16-bit unsigned integer + - 16-bit two's-complement integer + - 32-bit two's-complement integer + - 32-bit two's-complement floating point + + Multi-word values are stored in memory with the most-significant words in the + lowest addresses. Bytes are stored in memory with the most-significant byte + in the upper half of the 16-bit word and the least-significant byte in the + lower half. + + The instruction set is fairly irregular -- a legacy of its original + implementation in hardware in the 2116 and the accretion of microprogrammed + instructions in the 2100 and 1000 CPUs. Initially, there were five base-set + instruction groups: + + 1. Memory-Reference Group (MRG) + 2. Shift-Rotate Group (SRG) + 3. Alter-Skip Group (ASG) + 4. I/O Group (IOG) + 5. Macroinstruction Group (MAC) + + All of the instructions added after the 2116 are in the Macroinstruction + Group. + + 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 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: + + 15 14-12 10 Group Address Ranges + -- ----- -- ----- ------------------------------- + x nnn x MRG 010000-077777 and 110000-177777 + 0 000 0 SRG 000000-001777 and 004000-005777 + 0 000 1 ASG 002000-003777 and 006000-007777 + 1 000 1 IOG 102000-103777 and 106000-107777 + 1 000 0 MAC 100000-101777 and 104000-105777 + + Where: + + x = don't care + n = any combination other than all zeros + + The MAC group is subdivided into the Extended Arithmetic Group (EAG) and the + User Instruction Group (UIG), based on bits 11, 9, and 8, as follows: + + 11 9 8 Group Address Range + -- -- -- ----- ------------- + 0 0 0 EAG 100000-100377 + 0 0 1 EAG 100400-100777 + 0 1 0 EAG 101000-101377 + 0 1 1 UIG-1 101400-101777 + 1 0 0 EAG 104000-104377 + 1 0 1 EAG 104400-104777 + 1 1 0 UIG-0 105000-105377 + 1 1 1 UIG-1 105400-105777 + + All of the 2116 FPP instructions are in the UIG sets: 3 use 10144x opcodes + and the rest use 1050xx and 1054xx opcodes. The 2100 decodes only UIG-0 + instructions, whereas the 1000s use both UIG sets. In particular, the + 105740-105777 range is used by the 1000 Extended Instruction Group (EIG), + which is part of the 1000-Series base set. + + The 21xx and 1000 M/E/F-Series machines do not trap unimplemented + instructions. In general, unimplemented EAG instructions cause erroneous + execution, and unimplemented UIG instructions execute as NOP. However, there + are machine-to-machine variations, and some unimplemented instructions + execute as other, defined instructions. + + 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 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | I | mem op | P | memory address | MRG + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | I | mem op | R | P | memory address | MRG + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + I = direct/indirect (0/1) + R = A/B register (0/1) + P = base/current page (0/1) + + 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, P = P + 2 if M [MA] == 0 + 1000 ADA A = A + M [MA] + 1001 ADB B = 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 + 1111 STB M [MA] = B + + Bits 15 and 10 encode the type of access, as follows: + + 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 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + R = A/B register (0/1) + E = disable/enable op + 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 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 | 0 0 0 | R | 1 | r op | e op | E | S | L | I | Z | V | ASG + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + R = A/B register (0/1) + E = SEZ + S = SS* + L = SL* + I = IN* + 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 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 1 | 0 0 0 | R | 1 | H | I/O op | select code | IOG + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + 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 | 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 | 0 | shift/rotate op | shift count | EAG + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + 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 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 1 | 0 0 0 | R | 0 1 | module | operation | UIG + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + 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. + + + 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. + + 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. + + 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 + 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 + that may be enabled with a SET CPU STOP= command: + + Action + ------ ------------------------------------------ + UNIMPL stop on an unimplemented instruction + UNDEF stop on an undefined instruction + UNSC stop on an access to an unused select code + IOERR stop on an unreported I/O error + + If an enabled stop condition is detected, execution ceases with the + instruction pending, and control returns to the SCP prompt. When simulation + stops, execution may be resumed in two ways. If the cause of the stop has + not been remedied and the stop has not been disabled, resuming execution with + CONTINUE, STEP, GO, or RUN will cause the stop to occur again. Alternately, + specifying the "-B" switch with any of the preceding commands will resume + execution while bypassing the stop for the current instruction. + + The UNIMPL option stops the simulator if execution is attempted of an + instruction provided by a firmware option that is not currently installed + (e.g., a DAD instruction when the double-integer firmware is not installed) + or of an opcode provided by an installed option but not assigned to an + instruction (e.g., opcode 105335 from the double-integer firmware). + Bypassing the stop will execute the instruction as a NOP (no-operation). + + The UNDEF option stops the simulator if execution is attempted of an + instruction containing a decoded reserved bit pattern other than that defined + in the Operating and Reference manual for the CPU. For example, opcodes + 101700 and 105700 are not listed as DMS instructions, but they execute as + XMM instructions, rather than as NOP. The intent of this stop is to catch + instructions containing reserved fields with values that change the meaning + of those instructions. Bypassing the stop will decode and execute the + instruction in the same manner as the selected CPU. + + The UNSC option stops the simulator if an I/O instruction addresses a select + code that is not assigned to an enabled device (equivalent to an empty + hardware I/O backplane slot). Bypassing the stop will read the floating + S-bus or I/O-bus for LIA/B and MIA/B instructions or do nothing for all other + instructions. + + The IOERR option stops the simulator if an I/O error condition exists for a + device that does not report this status to the CPU. For example, the paper + tape reader device (PTR) does not report "no tape loaded" status, and the + processor interconnect device (IPL) does not report "cable disconnected." In + both cases, I/O to the device will simply hang with no indication of the + problem. Enabling the IOERR option will stop the simulator with an error + indication for these devices. + + In addition, a simulation stop will occur if an indirect addressing chain + exceeds the maximum length specified by a SET CPU INDIR= command. + Memory addresses may be indirect to indicate that the values point to the + target addresses rather than contain the target addresses. The target of an + indirect address may itself be indirect, and the CPU follows this chain of + addresses until it finds a direct address. Indirect addressing is typically + only one or two levels deep, but if the chain loops back on itself (e.g., if + an indirect address points at itself), then instruction execution will hang. + + The limit may be set to any number of levels up to 32,768. This is the + absolute maximum number of levels that can be created without an infinite + loop -- each location in memory points to the next one except for the last, + which contains the target value. In practice, anything over a few levels + likely represents a programming error. The default setting is 16 levels. + + + 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: + + Trace Action + ----- ------------------------------------------- + INSTR trace instructions executed + DATA trace memory data accesses + FETCH trace memory instruction fetches + REG trace registers + OPND trace instruction operands + EXEC trace matching instruction execution states + + A section of an example trace is: + + >>CPU instr: S 0002 05735 103101 CLO + >>CPU fetch: S 0002 05736 000036 instruction fetch + >>CPU reg: - **** 01011 042200 A 177777, B 000000, X 177777, Y 000000, e o i + >>CPU instr: S 0002 05736 000036 SLA,ELA + >>CPU fetch: S 0002 05737 102101 instruction fetch + >>CPU reg: - **** 01011 042200 A 177776, B 000000, X 177777, Y 000000, E o i + >>CPU instr: S 0002 05737 102101 STO + >>CPU fetch: S 0002 05740 002400 instruction fetch + >>CPU reg: - **** 01011 042200 A 177776, B 000000, X 177777, Y 000000, E O i + >>CPU instr: S 0002 05755 102100 STF 0 + >>CPU fetch: S 0002 05756 102705 instruction fetch + >>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I + >>CPU instr: S 0002 05756 102705 STC 5 + >>CPU fetch: S 0002 05757 105736 instruction fetch + >>CPU reg: P **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I + >>CPU instr: S 0002 05757 105736 UJP 2111 + >>CPU fetch: S 0002 05760 002111 instruction fetch + >>CPU fetch: U 0001 02111 026111 instruction fetch + >>CPU reg: P **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I + >>CPU instr: U 0001 02111 026111 JMP 2111 + >>CPU instr: U 0001 02111 000011 interrupt + >>CPU fetch: S 0000 00011 115013 instruction fetch + >>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I + >>CPU reg: - **** ***** ****** MPF 000000, MPV 002111, MES 163011, MEV 030000 + >>CPU instr: S 0000 00011 115013 JSB 1013,I + >>CPU data: S 0000 01013 005557 data read + >>CPU data: S 0002 05557 002111 data write + >>CPU fetch: S 0002 05560 103100 instruction fetch + >>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I + >>CPU instr: S 0002 05560 103100 CLF 0 + >>CPU fetch: S 0002 05561 105714 instruction fetch + >>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O i + >>CPU exec: ******************** + >>CPU reg: P **** 01567 000000 A 100036, B 000100, X 000100, Y 074000, E o I + >>CPU instr: U 0220 07063 105240 .PMAP + >>CPU data: U 0000 01776 000227 unprotected read + >>CPU data: U 0227 76100 000233 data read + >>CPU opnd: * **** 07065 105240 return location is P+2 (no error) + >>CPU fetch: U 0220 07065 127055 instruction fetch + >>CPU reg: P **** 01567 000000 A 100037, B 000101, X 000100, Y 074000, e o I + + The INSTR option traces instruction executions and interrupts. Each + instruction is printed in symbolic form before it is executed. + + The DATA option traces reads from and writes to memory. Each access is + classified by its usage type as "data" (using the current or alternate map + with access protection) or "unprotected" (using a specified map without + protection). + + The FETCH option traces instruction fetches from memory. Reads of the + additional words in a multiword instruction, such as the target address of a + DLD (double load) instruction, are also classified as fetches. + + The REG option traces register values. Two sets of registers are printed. + After executing each instruction, the working registers (A, B, E, O, S, and, + for 1000 CPUs, X and Y) and the state of the interrupt system (on or off) are + printed. After executing an instruction that may alter the Memory Protect or + Memory Expansion Module state, the MP fence and violation registers, the MEM + status and violation registers, and the current protection state are printed. + + The OPND option traces operand values. Some instructions that take memory + and register operands that are difficult to decode from DATA or REG traces + present the operand values in a higher-level format. The operand data and + value presented are specific to the instruction; see the instruction executor + comments for details. + + The EXEC option traces the execution of instructions that match + user-specified criteria. When a match occurs, all CPU trace options are + turned on for the duration of the execution of the matched instruction. The + prior trace settings are restored when a match fails. This option allows + detailed tracing of specified instructions while minimizing the log file size + compared to a full instruction trace. + + The various trace formats are interpreted as follows: + + >>CPU instr: U 0045 10341 016200 LDA 11200 + ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~ + | | | | | + | | | | +-- instruction mnemonic + | | | +---------- octal data (instruction opcode) + | | +------------------ octal logical address (P register) + | +----------------------- octal physical page number + +--------------------------- memory map (S/U/- system/user/disabled) + + >>CPU instr: U 0045 10341 000011 interrupt + ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~ + | | | | | + | | | | +-- interrupt classification + | | | +---------- octal device number (CIR register) + | | +------------------ octal logical address at interrupt (P register) + | +----------------------- octal physical page number at interrupt + +--------------------------- memory map (S/U/- system/user/disabled) + + >>CPU fetch: - 0000 10341 016200 instruction fetch + >>CPU data: U 0013 01200 123003 data read + >>CPU data: S 0013 01200 017200 unprotected write + ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~ + | | | | | + | | | | +-- memory access classification + | | | +------------ octal data (memory contents) + | | +-------------------- octal logical address (effective address) + | +------------------------- 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 + ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | | | + | | | | +-- 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 + ~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | | | + | | | | +-- 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 + ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | +-- operand-specific value + | +------------ operand-specific octal data + +-------------------- octal logical address (P register) + + + Implementation notes: + + 1. The simulator is fast enough, compared to the run-time of the longest + instructions, for interruptibility not to matter. However, the HP + diagnostics explicitly test interruptibility in the EIS and DMS + instructions and in long indirect address chains. Accordingly, the + simulator does "just enough" to pass these tests. In particular, if an + interrupt is pending but deferred at the beginning of an interruptible + 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. +*/ + + + +#include + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu_dmm.h" + + + +/* CPU program constants */ + +/* Alter-Skip Group instruction register fields */ + +#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 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 */ + + +/* CPU unit flags and accessors. + + 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 | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + 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 */ + +REG *sim_PC = NULL; /* the pointer to the P register */ + + +/* CPU global data structures */ + + +/* 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 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 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 */ + +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 */ + +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 */ + + +/* CPU local state. + + + Implementation notes: + + 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 */ + +static FLIP_FLOP interrupt_system = CLEAR; /* interrupt system */ +static uint32 interrupt_request = 0; /* the currently interrupting select code or zero if none */ + +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 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 */ + +static DEVICE *loader_rom [4] = { NULL }; /* the four boot loader ROM sockets in a 1000 CPU */ + + +/* CPU local data structures */ + + +/* CPU features table. + + The feature table is used to validate CPU feature changes within the subset + of features supported by a given CPU. Features in the typical list are + enabled when the CPU model is selected. If a feature appears in the typical + list but NOT in the optional list, then it is standard equipment and cannot + be disabled. If a feature appears in the optional list, then it may be + enabled or disabled as desired by the user. +*/ + +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 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, /* Option_2115 */ + UNIT_PFAIL | UNIT_DMA | UNIT_EAU, + 8 * 1024 + }, + + { UNIT_DMA, /* Option_2114 */ + UNIT_PFAIL | UNIT_DMA, + 16 * 1024 + }, + + { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* Option_2100 */ + UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP, + 32 * 1024 + }, + + { 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, /* 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 | /* 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, + 1024 * 1024 + } + }; + + +/* CPU local SCP support routine declarations */ + +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); + +static t_stat cpu_reset (DEVICE *dptr); +static t_stat cpu_boot (int32 unitno, DEVICE *dptr); + +static t_stat set_stops (UNIT *uptr, int32 option, char *cptr, void *desc); +static t_stat set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc); +static t_stat set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc); +static t_stat set_option (UNIT *uptr, int32 option, char *cptr, void *desc); +static t_stat clear_option (UNIT *uptr, int32 option, char *cptr, void *desc); +static t_stat set_loader (UNIT *uptr, int32 enable, char *cptr, void *desc); +static t_stat set_roms (UNIT *uptr, int32 option, char *cptr, void *desc); +static t_stat set_exec (UNIT *uptr, int32 option, char *cptr, void *desc); + +static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat show_model (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat show_roms (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat show_cage (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat show_exec (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, void *desc); + + +/* CPU local utility routine declarations */ + +static t_stat mrg_address (void); +static HP_WORD srg_uop (HP_WORD value, HP_WORD operation); +static t_stat machine_instruction (t_bool int_ack, uint32 *idle_save); +static t_bool reenable_interrupts (void); + + +/* CPU SCP data structures */ + + +/* Device information blocks */ + +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) */ + &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) */ + &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 "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. +*/ + +static UNIT cpu_unit [] = { + { UDATA (NULL, UNIT_FIX | UNIT_BINK, 0) } + }; + + +/* Register list. + + The CPU register list exposes the machine registers for user inspection and + modification. + + + Implementation notes: + + 1. All registers that reference variables of type HP_WORD must have the + REG_FIT flag for proper access if HP_WORD is a 16-bit type. + + 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) }, + + { FLDATA (INTSYS, interrupt_system, 0) }, + { FLDATA (INTEN, cpu_interrupt_enable, 0) }, + + { ORDATA (IOPSP, SPR, 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 (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 }, + + { 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 }, + + { NULL } + }; + + +/* Modifier list. + + + Implementation notes: + + 1. The 21MX monikers are deprecated in favor of the 1000 designations. See + the "HP 1000 Series Naming History" on the back inside cover of the + Technical Reference Handbook. + + 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 + option value must be passed to the validation routine. +*/ + +static MTAB cpu_mod [] = { +/* 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_FIELD, UNIT_1000_F, "", "1000-F", &set_model, &show_model, (void *) "1000-F" }, +#endif + + { 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_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_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_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_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 }, + +#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_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 }, +*/ + +/* Entry Flags Value Print String Match String Validation Display Descriptor */ +/* ------------------- ----------- ------------ --------------- ------------- -------------- ---------- */ + { MTAB_XDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle, NULL }, + { MTAB_XDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL, NULL }, + + { MTAB_XDV, 1, NULL, "LOADERENABLE", &set_loader, NULL, NULL }, + { MTAB_XDV, 0, NULL, "LOADERDISABLE", &set_loader, NULL, NULL }, + + { MTAB_XDV, 4 * 1024, NULL, "4K", &set_size, NULL, NULL }, + { MTAB_XDV, 8 * 1024, NULL, "8K", &set_size, NULL, NULL }, + { MTAB_XDV, 12 * 1024, NULL, "12K", &set_size, NULL, NULL }, + { MTAB_XDV, 16 * 1024, NULL, "16K", &set_size, NULL, NULL }, + { MTAB_XDV, 24 * 1024, NULL, "24K", &set_size, NULL, NULL }, + { MTAB_XDV, 32 * 1024, NULL, "32K", &set_size, NULL, NULL }, + { MTAB_XDV, 64 * 1024, NULL, "64K", &set_size, NULL, NULL }, + { MTAB_XDV, 128 * 1024, NULL, "128K", &set_size, NULL, NULL }, + { MTAB_XDV, 256 * 1024, NULL, "256K", &set_size, NULL, NULL }, + { MTAB_XDV, 512 * 1024, NULL, "512K", &set_size, NULL, NULL }, + { 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 }, + { MTAB_XDV | MTAB_NMO, 2, "INDIR", "INDIR", &set_stops, &show_stops, NULL }, + + { MTAB_XDV | MTAB_NMO, 1, "EXEC", "EXEC", &set_exec, &show_exec, NULL }, + { MTAB_XDV, 0, NULL, "NOEXEC", &set_exec, NULL, NULL }, + + { MTAB_XDV | MTAB_NMO, 0, "SPEED", NULL, NULL, &show_speed, NULL }, + + { 0 } + }; + + +/* Trace list */ + +static DEBTAB cpu_deb [] = { + { "INSTR", TRACE_INSTR }, /* trace instruction executions */ + { "DATA", TRACE_DATA }, /* trace memory data accesses */ + { "FETCH", TRACE_FETCH }, /* trace memory instruction fetches */ + { "REG", TRACE_REG }, /* trace register values */ + { "OPND", TRACE_OPND }, /* trace instruction operands */ + { "EXEC", TRACE_EXEC }, /* trace matching instruction execution states */ + { "NOOS", DEBUG_NOOS }, /* RTE-6/VM will not use OS firmware */ + { NULL, 0 } + }; + + +/* Simulation stop list. + + The simulator can be configured to detect certain machine instruction + conditions and stop execution when one of them occurs. Stops may be enabled + or disabled individually with these commands: + + SET CPU STOP=