/* 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;
}
