blob: 6b844f4b4817707d7a5286746adfe27434496521 [file] [log] [blame] [raw]
/* hp2100_baci.c: HP 12966A Buffered Asynchronous Communications Interface simulator
Copyright (c) 2007-2017, 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
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 <ctype.h>
#include "hp2100_defs.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)
/* 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 */
/* BACI state variables */
struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flagbuf; /* flag buffer flip-flop */
FLIP_FLOP srq; /* SRQ flip-flop */
FLIP_FLOP lockout; /* interrupt lockout flip-flop */
} baci = { CLEAR, CLEAR, CLEAR, CLEAR, CLEAR };
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 */
/* 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 global routines */
IOHANDLER baci_io;
t_stat baci_term_svc (UNIT *uptr);
t_stat baci_poll_svc (UNIT *uptr);
t_stat baci_reset (DEVICE *dptr);
t_stat baci_attach (UNIT *uptr, CONST char *cptr);
t_stat baci_detach (UNIT *uptr);
/* BACI data structures
baci_ldsc BACI terminal multiplexer line descriptor
baci_desc BACI terminal multiplexer device descriptor
baci_dib BACI device information block
baci_unit BACI unit list
baci_reg BACI register list
baci_mod BACI modifier list
baci_deb BACI debug list
baci_dev BACI device descriptor
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.
*/
DEVICE baci_dev;
TMLN baci_ldsc = { 0 }; /* line descriptor */
TMXR baci_desc = { 1, 0, 0, &baci_ldsc, NULL, &baci_dev }; /* device descriptor */
DIB baci_dib = { &baci_io, BACI, 0 };
UNIT baci_unit[] = {
{ UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */
{ UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* line poll unit */
};
REG baci_reg [] = {
/* 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.flagbuf, 0) },
{ FLDATA (SRQ, baci.srq, 0) },
{ ORDATA (SC, baci_dib.select_code, 6), REG_HRO },
{ ORDATA (DEVNO, baci_dib.select_code, 6), REG_HRO },
{ NULL }
};
MTAB baci_mod[] = {
{ UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAGNOSTIC", NULL, NULL, NULL },
{ UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL },
{ UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL },
{ UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL },
{ UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL },
{ UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL },
{ MTAB_XDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &baci_desc },
{ MTAB_XDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &baci_desc },
{ MTAB_XDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &tmxr_show_cstat, &baci_desc },
{ MTAB_XDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &baci_desc },
{ MTAB_XDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc },
{ MTAB_XDV, 1u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &baci_dib },
{ MTAB_XDV | MTAB_NMO, ~1u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &baci_dib },
{ 0 }
};
DEBTAB baci_deb [] = {
{ "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 baci_dev = {
"BACI", /* device name */
baci_unit, /* unit array */
baci_reg, /* register array */
baci_mod, /* modifier array */
2, /* number of units */
10, /* address radix */
31, /* address width */
1, /* address increment */
8, /* data radix */
8, /* data width */
&tmxr_ex, /* examine routine */
&tmxr_dep, /* deposit routine */
&baci_reset, /* reset routine */
NULL, /* boot routine */
&baci_attach, /* attach routine */
&baci_detach, /* detach routine */
&baci_dib, /* device information block */
DEV_DEBUG | DEV_DISABLE | DEV_MUX, /* device flags */
0, /* debug control flags */
baci_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL, /* logical device name */
NULL, /* help routine */
NULL, /* help attach routine*/
(void *) &baci_desc /* help context */
};
/* 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 (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
const char *hold_or_clear = (signal_set & ioCLF ? ",C" : "");
uint8 ch;
uint16 data;
uint32 mask;
IOSIGNAL signal;
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
baci.flag = baci.flagbuf = CLEAR; /* clear flag and flag buffer */
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 = baci.flagbuf = SET; /* set flag and flag buffer */
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 */
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 */
tprintf (baci_dev, DEB_CPU, "[LIx%s] Received data = %06o\n",
hold_or_clear, baci_ibuf);
}
else { /* control clear? */
data = baci_status; /* return status */
tprintf (baci_dev, DEB_CPU, "[LIx%s] Status = %06o\n",
hold_or_clear, baci_status);
}
stat_data = IORETURN (SCPE_OK, data); /* merge in return status */
break;
case ioIOO: /* I/O data output */
baci_obuf = IODATA (stat_data); /* get data value */
tprintf (baci_dev, DEB_CPU, "[OTx%s] Command = %06o\n",
hold_or_clear, baci_obuf);
if (baci_obuf & OUT_MR) { /* master reset? */
master_reset (); /* do before processing */
baci_io (&baci_dib, ioSIR, 0); /* set interrupt request */
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 ioCRS: /* control reset */
master_reset (); /* issue master reset */
tprintf (baci_dev, DEB_CMDS, "[CRS] Master reset\n");
break;
case ioCLC: /* clear control flip-flop */
baci.control = CLEAR; /* clear control */
tprintf (baci_dev, DEB_CMDS, "[CLC%s] Control cleared\n", hold_or_clear);
break;
case ioSTC: /* set control flip-flop */
baci.control = SET; /* set control */
baci.lockout = CLEAR; /* clear lockout */
tprintf (baci_dev, DEB_CMDS, "[STC%s] Control set and lockout cleared\n", hold_or_clear);
if (!(signal_set & ioCLF)) /* STC without ,C ? */
update_status (); /* clearing lockout might interrupt */
break;
case ioSIR: /* set interrupt request */
setstdPRL (baci); /* set standard PRL signal */
setstdIRQ (baci); /* set standard IRQ signal */
setSRQ (dibptr->select_code, baci.srq); /* set SRQ signal */
break;
case ioIAK: /* interrupt acknowledge */
baci.flagbuf = CLEAR;
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_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 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 tranmit 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
<unit> 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.
*/
t_stat baci_term_svc (UNIT *uptr)
{
uint32 data_bits, data_mask;
const t_bool fast_timing = (baci_term.flags & UNIT_FASTTIME) != 0;
const t_bool is_attached = (baci_term.flags & UNIT_ATT) != 0;
t_stat status = SCPE_OK;
t_bool recv_loop = TRUE;
t_bool xmit_loop = (baci_ldsc.xmte != 0); /* TRUE if the transmit buffer is not full */
/* Transmission */
if (baci_ldsc.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.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 && !IRQ (baci_dib.select_code); /* 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.
*/
t_stat baci_poll_svc (UNIT *uptr)
{
if (tmxr_poll_conn (&baci_desc) >= 0) /* if new connection is established */
baci_ldsc.rcve = 1; /* then enable line to receive */
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)
{
IOPRESET (&baci_dib); /* PRESET device (does not use PON) */
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 */
t_stat baci_attach (UNIT *uptr, CONST 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 line poll immediately */
}
return status;
}
/* Detach line */
t_stat baci_detach (UNIT *uptr)
{
t_stat status;
baci_ldsc.rcve = 0; /* disable line reception */
sim_cancel (&baci_poll); /* stop line poll */
status = tmxr_detach (&baci_desc, uptr); /* detach socket */
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 */
baci_ldsc.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_io (&baci_dib, ioENF, 0); /* set flag */
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 SRQ */
baci_io (&baci_dib, ioSIR, 0); /* set interrupt request */
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 ? ~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 */
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 & DMASK,
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;
}