| /* hp2100_baci.c: HP 12966A buffered asynchronous communications interface simulator | |
| Copyright (c) 2007-2008, 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 BACI card | |
| 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. | |
| 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 Telnet buffer as a | |
| block, rather than one character per service call, and on input, all of | |
| the characters available in the Telnet buffer are loaded into the FIFO as | |
| a block. | |
| 2. The ENQ/ACK handshake is done locally, without involving the Telnet | |
| 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 <ctype.h> | |
| #include "hp2100_defs.h" | |
| #include "sim_sock.h" | |
| #include "sim_tmxr.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) | |
| /* Debug flags */ | |
| #define DEB_CMDS (1 << 0) /* commands and status */ | |
| #define DEB_CPU (1 << 1) /* CPU I/O */ | |
| #define DEB_BUF (1 << 2) /* buffer gets and puts */ | |
| #define DEB_XFER (1 << 3) /* character reads and writes */ | |
| /* 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] /* Telnet polling unit */ | |
| /* BACI state variables */ | |
| FLIP_FLOP baci_control = CLEAR; /* control flip-flop */ | |
| FLIP_FLOP baci_flag = CLEAR; /* flag flip-flop */ | |
| FLIP_FLOP baci_flagbuf = CLEAR; /* flag buffer flip-flop */ | |
| FLIP_FLOP baci_srq = CLEAR; /* SRQ flip-flop */ | |
| FLIP_FLOP baci_lockout = CLEAR; /* interrupt lockout flip-flop */ | |
| uint16 baci_ibuf = 0; /* status/data in */ | |
| uint16 baci_obuf = 0; /* command/data out */ | |
| uint16 baci_status = 0; /* current status */ | |
| uint16 baci_edsiw = 0; /* enable device status word */ | |
| uint16 baci_dsrw = 0; /* device status reference word */ | |
| uint16 baci_cfcw = 0; /* character frame control word */ | |
| uint16 baci_icw = 0; /* interface control word */ | |
| uint16 baci_isrw = 0; /* interrupt status reset word */ | |
| uint32 baci_fput = 0; /* FIFO buffer add index */ | |
| uint32 baci_fget = 0; /* FIFO buffer remove index */ | |
| uint32 baci_fcount = 0; /* FIFO buffer counter */ | |
| uint32 baci_bcount = 0; /* break counter */ | |
| uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */ | |
| uint8 baci_spchar [256]; /* special character RAM */ | |
| uint16 baci_uart_thr = CLEAR_HR; /* UART transmitter holding register */ | |
| uint16 baci_uart_rhr = CLEAR_HR; /* UART receiver holding register */ | |
| int32 baci_uart_tr = CLEAR_R; /* UART transmitter register */ | |
| int32 baci_uart_rr = CLEAR_R; /* UART receiver register */ | |
| uint32 baci_uart_clk = 0; /* UART transmit/receive clock */ | |
| t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ | |
| uint32 baci_enq_cntr = 0; /* ENQ seen counter */ | |
| /* Terminal multiplexer library interface */ | |
| TMLN baci_ldsc = { 0 }; /* line descriptor */ | |
| TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */ | |
| /* BACI local routines */ | |
| static int32 service_time (uint32 control_word); | |
| static void update_status (void); | |
| static void master_reset (void); | |
| static uint32 fifo_get (void); | |
| static void fifo_put (uint8 ch); | |
| static void clock_uart (void); | |
| /* BACI global routines */ | |
| uint32 baci_io (uint32 select_code, IOSIG signal, uint32 data); | |
| t_stat baci_term_svc (UNIT *uptr); | |
| t_stat baci_poll_svc (UNIT *uptr); | |
| t_stat baci_reset (DEVICE *dptr); | |
| t_stat baci_attach (UNIT *uptr, char *cptr); | |
| t_stat baci_detach (UNIT *uptr); | |
| /* BACI data structures | |
| baci_dib BACI device information block | |
| baci_dev BACI device descriptor | |
| baci_unit BACI unit list | |
| baci_reg BACI register list | |
| baci_mod BACI modifier list | |
| baci_deb BACI debug list | |
| Two units are used: one to handle character I/O via the Telnet 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 Telnet 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. | |
| */ | |
| DIB baci_dib = { BACI, &baci_io }; | |
| DEVICE baci_dev; | |
| UNIT baci_unit[] = | |
| { { UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */ | |
| { UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } }; /* Telnet poll unit */ | |
| REG baci_reg[] = { | |
| { ORDATA (IBUF, baci_ibuf, 16), REG_FIT }, | |
| { ORDATA (OBUF, baci_obuf, 16), REG_FIT }, | |
| { ORDATA (STATUS, baci_status, 16), 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) }, | |
| { BRDATA (SPCHAR, baci_spchar, 8, 1, 256) }, | |
| { ORDATA (UARTTHR, baci_uart_thr, 16), REG_FIT }, | |
| { ORDATA (UARTTR, baci_uart_tr, 16), REG_NZ }, | |
| { ORDATA (UARTRHR, baci_uart_rhr, 16), REG_FIT }, | |
| { ORDATA (UARTRR, baci_uart_rr, 16), REG_NZ }, | |
| { DRDATA (UARTCLK, baci_uart_clk, 16) }, | |
| { DRDATA (CTIME, baci_term.wait, 19), REG_RO }, | |
| { 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_flagbuf, 0) }, | |
| { FLDATA (SRQ, baci_srq, 0) }, | |
| { ORDATA (DEVNO, baci_dib.devno, 6), REG_HRO }, | |
| { NULL } | |
| }; | |
| MTAB baci_mod[] = { | |
| { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL, NULL, NULL }, | |
| { UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL }, | |
| { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, | |
| { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, | |
| { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, | |
| { UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &baci_desc }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &baci_desc }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &tmxr_show_cstat, &baci_desc }, | |
| { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &baci_desc }, | |
| { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc }, | |
| { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &baci_dev }, | |
| { 0 } | |
| }; | |
| DEBTAB baci_deb[] = { | |
| { "CMDS", DEB_CMDS }, | |
| { "CPU", DEB_CPU }, | |
| { "BUF", DEB_BUF }, | |
| { "XFER", DEB_XFER }, | |
| { NULL, 0 } | |
| }; | |
| 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_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */ | |
| 0, /* debug control flags */ | |
| baci_deb, /* debug flag name table */ | |
| NULL, /* memory size change routine */ | |
| NULL }; /* logical device name */ | |
| /* I/O signal handler. | |
| 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. | |
| */ | |
| uint32 baci_io (uint32 select_code, IOSIG signal, uint32 data) | |
| { | |
| const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); | |
| const IOSIG base_signal = IOBASE (signal); /* derive base signal */ | |
| uint8 ch; | |
| uint32 mask; | |
| switch (base_signal) { /* dispatch base I/O signal */ | |
| case ioCLF: /* clear flag flip-flop */ | |
| baci_flag = baci_flagbuf = CLEAR; /* clear flag and flag buffer */ | |
| baci_srq = CLEAR; /* clear SRQ */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: [CLF] Flag and SRQ cleared\n", sim_deb); | |
| update_status (); /* FLG might set when SRQ clears */ | |
| break; | |
| case ioSTF: /* set flag flip-flop */ | |
| baci_flag = baci_flagbuf = SET; /* set flag and flag buffer */ | |
| baci_lockout = SET; /* set lockout */ | |
| baci_srq = SET; /* set SRQ */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: [STF] Flag, SRQ, and lockout set\n", sim_deb); | |
| break; | |
| case ioENF: /* enable flag */ | |
| baci_flag = baci_flagbuf = SET; /* set device flag and flag buffer */ | |
| baci_lockout = SET; /* set lockout */ | |
| break; | |
| case ioSFC: /* skip if flag is clear */ | |
| setstdSKF (baci); | |
| break; | |
| case ioSFS: /* skip if flag is set */ | |
| setstdSKF (baci); | |
| 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 */ | |
| data = baci_ibuf; /* return received data */ | |
| if (DEBUG_PRI (baci_dev, DEB_CPU)) | |
| fprintf (sim_deb, ">>BACI cpu: [LIx%s] Received data = %06o\n", hold_or_clear, data); | |
| } | |
| else { /* control clear? */ | |
| data = baci_status; /* return status */ | |
| if (DEBUG_PRI (baci_dev, DEB_CPU)) | |
| fprintf (sim_deb, ">>BACI cpu: [LIx%s] Status = %06o\n", hold_or_clear, data); | |
| } | |
| break; | |
| case ioIOO: /* I/O data output */ | |
| if (DEBUG_PRI (baci_dev, DEB_CPU)) | |
| fprintf (sim_deb, ">>BACI cpu: [OTx%s] Command = %06o\n", hold_or_clear, data); | |
| baci_obuf = data; | |
| if (baci_obuf & OUT_MR) { /* master reset? */ | |
| master_reset (); /* do before processing */ | |
| baci_io (select_code, ioSIR, 0); /* set interrupt request */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ">>BACI 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 (DEBUG_PRI (baci_dev, DEB_CMDS) && /* debugging? */ | |
| (sim_is_active (&baci_term) == 0)) /* service stopped? */ | |
| fprintf (sim_deb, ">>BACI 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); | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ">>BACI 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ">>BACI 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 */ | |
| /* fall into CRS handler */ | |
| case ioCRS: /* control reset */ | |
| master_reset (); /* issue master reset */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: [CRS] Master reset\n", sim_deb); | |
| break; | |
| case ioCLC: /* clear control flip-flop */ | |
| baci_control = CLEAR; /* clear control */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ">>BACI cmds: [CLC%s] Control cleared\n", hold_or_clear); | |
| break; | |
| case ioSTC: /* set control flip-flop */ | |
| baci_control = SET; /* set control */ | |
| baci_lockout = CLEAR; /* clear lockout */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ">>BACI cmds: [STC%s] Control set and lockout cleared\n", hold_or_clear); | |
| if (signal == ioSTC) /* STC without ,C ? */ | |
| update_status (); /* clearing lockout might interrupt */ | |
| break; | |
| case ioSIR: /* set interrupt request */ | |
| setstdPRL (select_code, baci); /* set standard PRL signal */ | |
| setstdIRQ (select_code, baci); /* set standard IRQ signal */ | |
| setSRQ (select_code, baci_srq); /* set SRQ signal */ | |
| break; | |
| case ioIAK: /* interrupt acknowledge */ | |
| baci_flagbuf = CLEAR; | |
| break; | |
| default: /* all other signals */ | |
| break; /* are ignored */ | |
| } | |
| if (signal > ioCLF) /* multiple signals? */ | |
| baci_io (select_code, ioCLF, 0); /* issue CLF */ | |
| else if (signal > ioSIR) /* signal affected interrupt status? */ | |
| baci_io (select_code, ioSIR, 0); /* set interrupt request */ | |
| return data; | |
| } | |
| /* 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 Telnet 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 (Telnet) | |
| 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 Telnet buffer before initiating a | |
| packet send. For input, all characters present in the Telnet 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. | |
| */ | |
| 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 xmit_loop = TRUE; | |
| t_bool recv_loop = TRUE; | |
| /* Transmission */ | |
| 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI xfer: Character ENQ absorbed internally, " | |
| "ENQ count = %d\n", baci_enq_cntr); | |
| } | |
| else { /* character is not an ENQ */ | |
| 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? */ | |
| DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI xfer: Character %s " | |
| "transmitted from UART\n", fmt_char (baci_uart_tr)); | |
| } | |
| } | |
| 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 */ | |
| } | |
| else | |
| xmit_loop = FALSE; | |
| } | |
| /* 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI 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))) { /* and new character available? */ | |
| if (baci_uart_rr & SCPE_BREAK) { /* break detected? */ | |
| baci_status = baci_status | IN_BREAK; /* set break status */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fputs (">>BACI xfer: Break detected\n", sim_deb); | |
| } | |
| 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 = baci_uart_rr & data_mask; /* mask data into holding register */ | |
| baci_uart_rr = CLEAR_R; /* clear receiver register */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI xfer: Character %s received by UART\n", | |
| fmt_char ((uint8) baci_uart_rhr)); | |
| if (baci_term.flags & UNIT_CAPSLOCK) /* caps lock mode? */ | |
| baci_uart_rhr = 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 && !IRQ (baci_dib.devno); /* 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fputs (">>BACI xfer: Character ACK generated internally\n", sim_deb); | |
| 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 | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: Terminal service stopped\n", sim_deb); | |
| return status; | |
| } | |
| /* BACI Telnet poll service. | |
| This service routine is used to poll for Telnet connections and incoming | |
| characters. If characters are available, the terminal I/O service routine is | |
| scheduled. It starts when the socket is attached and stops when the socket | |
| is detached. | |
| */ | |
| t_stat baci_poll_svc (UNIT *uptr) | |
| { | |
| if (baci_term.flags & UNIT_ATT) { /* attached to network? */ | |
| if (tmxr_poll_conn (&baci_desc) >= 0) /* new connection established? */ | |
| baci_ldsc.rcve = 1; /* 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 = sync_poll (INITIAL); /* initial synchronization */ | |
| else /* not first */ | |
| uptr->wait = sync_poll (SERVICE); /* continue synchronization */ | |
| sim_activate (uptr, uptr->wait); /* continue polling */ | |
| return SCPE_OK; | |
| } | |
| /* Simulator reset routine */ | |
| t_stat baci_reset (DEVICE *dptr) | |
| { | |
| baci_io (baci_dib.devno, ioPOPIO, 0); /* send POPIO signal */ | |
| 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 Telnet poll immediately */ | |
| } | |
| else | |
| sim_cancel (&baci_poll); /* else stop Telnet poll */ | |
| return SCPE_OK; | |
| } | |
| /* Attach controller */ | |
| t_stat baci_attach (UNIT *uptr, char *cptr) | |
| { | |
| t_stat status = SCPE_OK; | |
| status = tmxr_attach (&baci_desc, uptr, cptr); /* attach to socket */ | |
| if (status == SCPE_OK) { | |
| baci_poll.wait = POLL_FIRST; /* set up poll */ | |
| sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */ | |
| } | |
| return status; | |
| } | |
| /* Detach controller */ | |
| t_stat baci_detach (UNIT *uptr) | |
| { | |
| t_stat status; | |
| status = tmxr_detach (&baci_desc, uptr); /* detach socket */ | |
| baci_ldsc.rcve = 0; /* disable line reception */ | |
| sim_cancel (&baci_poll); /* stop Telnet poll */ | |
| return status; | |
| } | |
| /* 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_flagbuf = 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 */ | |
| 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? */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: Lockout prevents flag set", sim_deb); | |
| } | |
| else if (baci_srq) { /* SRQ set? */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: SRQ prevents flag set", sim_deb); | |
| } | |
| else { | |
| baci_io (baci_dib.devno, ioENF, 0); /* set flag */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: Flag and lockout set", sim_deb); | |
| } | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ", 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? */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: Lockout prevents SRQ set", sim_deb); | |
| } | |
| else { | |
| baci_srq = SET; /* set SRQ */ | |
| baci_io (baci_dib.devno, ioSIR, 0); /* set interrupt request */ | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fputs (">>BACI cmds: SRQ set", sim_deb); | |
| } | |
| if (DEBUG_PRI (baci_dev, DEB_CMDS)) | |
| fprintf (sim_deb, ", status = %06o\n", baci_status); | |
| } | |
| return; | |
| } | |
| /* Calculate service time from baud rate. | |
| Service times are based on 1580 instructions per second, 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) | |
| { | |
| 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 uint32 fifo_get (void) | |
| { | |
| uint32 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_BUF)) | |
| fprintf (sim_deb, ">>BACI buf: Character %s get from FIFO [%d], " | |
| "character counter = %d\n", fmt_char (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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_BUF)) | |
| fprintf (sim_deb, ">>BACI 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 (DEBUG_PRI (baci_dev, DEB_BUF)) | |
| if (pass_thru) | |
| fprintf (sim_deb, ">>BACI buf: Character %s put to UART transmitter holding register, " | |
| "character counter = 1\n", fmt_char (ch)); | |
| else | |
| fprintf (sim_deb, ">>BACI 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 ? ~SIGN : -1); /* (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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fputs (">>BACI xfer: Break detected\n", sim_deb); | |
| } | |
| } | |
| 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI 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 = baci_uart_rr >> (16 - uart_bits); /* position data to right align */ | |
| baci_uart_rr = CLEAR_R; /* clear receiver register */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI xfer: UART receiver = %06o (%s)\n", | |
| baci_uart_rhr, fmt_char (baci_uart_rhr & data_mask)); | |
| fifo_put (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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fputs (">>BACI xfer: Parity error detected\n", sim_deb); | |
| } | |
| } | |
| } | |
| } | |
| 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 */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI xfer: UART transmitter = %06o (%s), " | |
| "clock count = %d\n", baci_uart_tr & DMASK, | |
| fmt_char (baci_uart_thr & data_mask), baci_uart_clk); | |
| } | |
| else { | |
| baci_uart_rr = CLEAR_R; /* clear receiver register */ | |
| if (DEBUG_PRI (baci_dev, DEB_XFER)) | |
| fprintf (sim_deb, ">>BACI xfer: UART receiver empty, " | |
| "clock count = %d\n", baci_uart_clk); | |
| } | |
| } | |
| return; | |
| } |