/* hp3000_atc.c: HP 3000 30032B Asynchronous Terminal Controller simulator

   Copyright (c) 2014-2021, J. David Bryan
   Copyright (c) 2002-2012, Robert M Supnik

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
   AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the names of the authors shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from the authors.

   ATCD,ATCC    HP 30032B Asynchronous Terminal Controller

   01-Apr-21    JDB     Removed REG_FIT from register definitions (obsolete)
   27-Oct-20    JDB     Added the SET LINEORDER option
   26-Oct-20    JDB     Line order now leaves out channel 0
   25-Aug-20    JDB     Reset routine now sets up VM unit pointer hooks
   08-Jan-20    JDB     Revised modem control operation
   09-Dec-19    JDB     Replaced debugging macros with tracing macros
   17-Jun-19    JDB     Now detaches all TDI lines when entering DIAGNOSTIC mode
   03-May-19    JDB     Output stall now synchronizes with the poll wait
   26-Apr-19    JDB     Added "vm_console_[in/out]put_unit" declarations
   16-Apr-19    JDB     Revised for serial port support
   18-Mar-19    JDB     Reordered SCP includes
   30-Apr-18    JDB     Corrected typo in "store" routine comment
   18-Dec-17    JDB     Return event time instead of status from "activate_unit"
   11-Dec-17    JDB     Reschedule "line_service" if receive buffer has data
   26-Oct-17    JDB     Call "tmxr_poll_tx" if transmit buffer is full
   05-Sep-17    JDB     Changed REG_A (permit any symbolic override) to REG_X
   16-Sep-16    JDB     Fixed atcd_detach to skip channel cancel if SIM_SW_REST
   12-Sep-16    JDB     Changed DIB register macro usage from SRDATA to DIB_REG
   20-Jul-16    JDB     Corrected poll_unit "wait" field initializer.
   26-Jun-16    JDB     Removed tmxr_set_modem_control_passthru call in atcc_reset
   09-Jun-16    JDB     Added casts for ptrdiff_t to int32 values
   16-May-16    JDB     Fixed interrupt mask setting
   13-May-16    JDB     Modified for revised SCP API function parameter types
   21-Mar-16    JDB     Changed uint16 types to HP_WORD
   26-Aug-15    JDB     First release version
   31-Jul-15    JDB     Passes the terminal control diagnostic (D438A)
   11-Aug-14    JDB     Passes the terminal data diagnostic (D427A)
   28-Jul-14    JDB     Created from the HP 2100 12920A MUX device simulator

   References:
     - 30032B Asynchronous Terminal Controller Installation and Service Manual
         (30032-90004, February 1977)
     - Stand-Alone HP 30032B Terminal Data Interface Diagnostic
         (30032-90011, October 1980)
     - Stand-Alone HP 30061A Terminal Controller Interface Diagnostic
         (30060-90004, February 1976)
     - HP 3000 Series III Engineering Diagrams Set
         (30000-90141, April 1980)


   The HP 30032B Asynchronous Terminal Controller is a 16-channel terminal
   multiplexer used with the HP 3000 CX through Series III systems.  The ATC
   connects from 1 to 16 serial terminals or modems to the HP 3000 at
   programmable baud rates from 75 to 2400 bits per second.  Character sizes are
   also programmable from 5 to 12 bits in length, including the start and stop
   bits.  Each channel can be independently configured, including for separate
   send and receive rates.  The ATC is not buffered, so the CPU has to retrieve
   each character from a given channel before the next character arrives.  To
   avoid saturating the CPU with interrupt requests, the ATC maintains an
   internal "mini-interrupt" system that queues requests and holds additional
   interrupts off until the CPU acknowledges the current request.

   The HP 3000CX and Series I use a dedicated serial interface for the system
   console, while user terminals are connected to the ATC.  For the Series II
   and III, the separate card is eliminated, and channel 0 of the ATC is
   reserved for the console.

   This module is an adaptation of the code originally written by Bob Supnik for
   the HP2100 MUX simulator.  The MUX device simulates an HP 12920A interface
   for an HP 2100/1000 computer.  The 12920A is an ATC with the HP 3000 I/O bus
   connection replaced by an HP 2100 I/O bus connection.  Programming and
   operation of the two multiplexers are virtually identical.

   The ATC consists of a Terminal Data Interface, which provides direct
   connection for 16 serial terminals, and one or two optional Terminal Control
   Interfaces, which provides control and status lines for Bell 103 and 202 data
   sets, respectively.  The ATC base product, order number 30032, consisted of
   one TDI card.  Option -001 added one TCI, and option -002 added two.  A
   second ATC subsystem could be added to support an additional 16 terminals or
   modems.

   This simulation provides one TDI and one optional TCI.  Each of the channels
   may be connected either to a Telnet session or a serial port on the host
   machine.  Channel 0 is connected to the simulation console, which initially
   performs I/O to the controlling window but may be rerouted instead to a
   Telnet session or serial port, if desired.  Additional channel configuration
   options select the input mode (upshifted or normal), output mode (8-bit,
   7-bit, printable, or upshifted), and whether the HP-standard ENQ/ACK
   handshaking is done by the external device or internally by the simulator.

   A device mode specifies whether terminals or diagnostic loopback cables are
   connected to the TDI and TCI.  Enabling the diagnostic mode simulates the
   installation of eight HP 30062-60003 diagnostic test (loopback) cables
   between channels 0-1, 2-3, etc., as required by the multiplexer diagnostics.
   In this mode, sending data on one channel automatically receives the same
   data on the alternate channel.  In addition, all Telnet and serial sessions
   are disconnected, and the TDI is detached from the listening port.  While in
   diagnostic mode, the ATTACH command is not allowed.  Enabling terminal mode
   allows the TDI to be attached to accept incoming connections again.

   Another device mode specifies whether the TDI operates in real-time or
   optimized ("fast") timing mode.  In the former, character send and receive
   events occur at approximately the same rate (in machine instructions) as in
   hardware.  The latter mode increases the rate to the maximum value consistent
   with correct operation in MPE.

   Both the TDI and TCI are normally enabled, although the TCI will not be used
   unless MPE is configured to use data sets on one or more channels.  When so
   configured, logging off will cause the channel to disconnect the Telnet
   session or drop the Data Terminal Ready signal on the serial port.  A channel
   controlled by the TCI will be marked as "data set" in a unit listing;
   channels not controlled will be marked as "direct".

   The TDI and TCI may be disabled, if desired, although the TDI must be
   detached from the listening port first.  Disabling the TDI does not affect
   the simulation console, as the CPU process clock will take over console
   polling automatically.

   The Terminal Data Interface provides 16 send channels, 16 receive channels,
   and 5 auxiliary channels.  The auxiliary channels are receive-only and do
   not connect to external devices.  Rather, they may be connected as a group to
   one or more of the other channels.  Their primary purpose is to diagnose
   conditions (e.g., baud rate) on the connected channel(s).

   In hardware, a recirculating memory stores seven 8-bit words of data,
   parameters, and status for each of the 37 channels.  A set of registers form
   a "window" into the recirculating memory, and the memory makes one complete
   pass every 69.44 microseconds.  Serial transfer rates are determined by each
   channel's parameter word, which specifies the number of recirculations that
   occur for each bit sent or received.

   In simulation, the memory is represented by separate buffer, parameter, and
   status arrays.  Recirculation is simulated by indexing through each of the
   arrays in sequence.


   The TDI responds only to direct I/O instructions, as follows:

   TDI Control Word Format (CIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M | R |  channel number   | -   -   -   -   -   -   - | E | A |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     M = master reset
     R = reset interrupts
     E = enable store of preceding data or parameter word to memory
     A = acknowledge interrupt


   TDI Status Word Format (TIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | S | D | I | - | C | R | L | B | -   -   -   -   -   -   -   - |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     S = SIO OK (always 0)
     D = direct read/write I/O OK
     I = interrupt request
     C = read/write completion flag
     R = receive/send (0/1) character interrupt
     L = character lost
     B = break status


   TDI Parameter Word Format (WIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 1 | R | I | E | D | char size |           baud rate           |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     R = receive/send (0/1) configuration
     I = enable channel completion interrupt
     E = enable echo (receive) or generate parity (send)
     D = diagnose using the auxiliary channels

   Character size:
     The three least-significant bits of the sum of the data, parity, and stop
     bits.  For example, 7E1 is 1001, so 001 is coded.

   Baud rate:
     The value (14400 / device bit rate) - 1.  For example, 2400 baud is 005.


   TDI Output Data Word Format (WIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | 1 | -   - | S |                 send data                 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     S    = sync bit
     data = right-justified with leading ones


   TDI Input Data Word Format (RIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |      channel      | P |             receive data              |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     P    = computed parity
     data = right-justified with leading ones


   The Terminal Control Interface provides two serial control outputs and two
   serial status inputs for each of 16 channels.  The first TCI connects to the
   Request to Send (CA) and Data Terminal Ready (CD) control lines and the Data
   Carrier Detect (CF) and Data Set Ready (CC) status lines.  Addressable
   latches hold the control line values and assert them continuously to the 16
   channels.  In addition, a 16-word by 4-bit RAM holds the expected state for
   each channel's status lines and the corresponding interrupt enable bits to
   provide notification if those lines change.

   The TCI responds only to direct I/O instructions, as follows:


   TCI Control Word Format (CIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M | R | S | U |    channel    | W | X | Q | T | Y | Z | C | D |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     M = master reset
     R = reset interrupts
     S = scan status lines for changes
     U = enable DCD/DSR state update
     W = enable RTS change
     X = enable DTR change
     Q = new RTS state
     T = new DTR state
     Y = DCD interrupt enabled
     Z = DSR interrupt enabled
     C = expected DCD state
     D = expected DSR state


   TCI Status Word Format (TIO or RIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | 1 | I | 1 |    channel    | 0 | 0 | J | K | Y | Z | C | D |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     I = interrupt request
     J = DCD interrupt present
     K = DSR interrupt present
     Y = DCD interrupt enabled
     Z = DSR interrupt enabled
     C = current DCD state
     D = current DSR state


   Implementation notes:

    1. The UNIT_MODEM flag indicates that a channel is controlled by the TCI.
       However, no modifier entry is provided, nor is one needed, as the flag is
       set automatically when the TCI first initializes the channel.  MPE
       defines separate terminal subtype numbers for directly connected
       terminals and modem-connected terminals, which are set at system
       generation time.

    2. Both TMXR_VALID and SCPE_KFLAG are set on internally generated ACKs only
       so that a debug trace will record the generation correctly.
*/



#include <ctype.h>

#include "sim_defs.h"
#include "sim_tmxr.h"

#include "hp3000_defs.h"
#include "hp3000_io.h"



/* Program limits */

#define TERM_COUNT          16                              /* number of terminal channels */
#define AUX_COUNT           5                               /* number of auxiliary channels */
#define POLL_COUNT          1                               /* number of poll units */

#define RECV_CHAN_COUNT     (TERM_COUNT + AUX_COUNT)        /* number of receive channels */
#define SEND_CHAN_COUNT     TERM_COUNT                      /* number of send channels */
#define UNIT_COUNT          (TERM_COUNT + POLL_COUNT)       /* number of units */

#define FIRST_TERM          0                               /* first terminal index */
#define LAST_TERM           (FIRST_TERM + TERM_COUNT - 1)   /* last terminal index */
#define FIRST_AUX           TERM_COUNT                      /* first auxiliary index */
#define LAST_AUX            (FIRST_AUX + AUX_COUNT - 1)     /* last auxiliary index */


/* Program constants */

#define FAST_IO_TIME        500                 /* initial fast receive/send time in event ticks */

#define POLL_RATE           100                 /* poll 100 times per second (unless synchronized) */
#define POLL_TIME           mS (10)             /* poll time is 10 milliseconds */

#define NUL                 '\000'              /* null */
#define ENQ                 '\005'              /* enquire */
#define ACK                 '\006'              /* acknowledge */
#define ASCII_MASK          000177u             /* 7-bit ASCII character set mask */

#define GEN_ACK             (TMXR_VALID | SCPE_KFLAG | ACK) /* a generated ACK character */

#define SCAN_ALL            (-1)                /* scan all channels for completion */


/* Parity functions derived from the global lookup table */

#define RECV_PARITY(c)      (odd_parity [(c) & D8_MASK] ? 0 : DDR_PARITY)
#define SEND_PARITY(c)      (odd_parity [(c) & D8_MASK] ? 0 : DDS_PARITY)


/* Debug flags */

#define DEB_CSRW            (1u << 0)           /* trace command initiations and completions */
#define DEB_XFER            (1u << 1)           /* trace data receptions and transmissions */
#define DEB_IOB             (1u << 2)           /* trace I/O bus signals and data words */
#define DEB_SERV            (1u << 3)           /* trace channel service scheduling calls */
#define DEB_PSERV           (1u << 4)           /* trace poll service scheduling calls */


/* Common per-unit multiplexer channel state variables */

#define recv_time           u3                  /* realistic receive time in event ticks */
#define send_time           u4                  /* realistic send time in event ticks */
#define stop_bits           u5                  /* stop bits to be added to each character received */


/* Device flags */

#define DEV_DIAG_SHIFT      (DEV_V_UF + 0)              /* diagnostic loopback */
#define DEV_REALTIME_SHIFT  (DEV_V_UF + 1)              /* timing mode is realistic */

#define DEV_DIAG            (1u << DEV_DIAG_SHIFT)      /* diagnostic mode flag */
#define DEV_REALTIME        (1u << DEV_REALTIME_SHIFT)  /* realistic timing flag */


/* Unit flags */

#define UNIT_CAPSLOCK_SHIFT (TTUF_V_UF + 0)             /* caps lock mode */
#define UNIT_LOCALACK_SHIFT (TTUF_V_UF + 1)             /* local ACK mode */
#define UNIT_MODEM_SHIFT    (TTUF_V_UF + 2)             /* modem control */

#define UNIT_CAPSLOCK       (1u << UNIT_CAPSLOCK_SHIFT) /* caps lock is down flag */
#define UNIT_LOCALACK       (1u << UNIT_LOCALACK_SHIFT) /* ENQ/ACK mode is local flag */
#define UNIT_MODEM          (1u << UNIT_MODEM_SHIFT)    /* channel connects to a data set flag */


/* Unit references */

#define line_unit           atcd_unit                   /* receive/send channel units */
#define poll_unit           atcd_unit [LAST_TERM + 1]   /* input polling unit */


/* Activation reasons */

typedef enum {
    Receive,
    Send,
    Loop,
    Stall
    } ACTIVATOR;


/* TDI control word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M | R |  channel number   | -   -   -   -   -   -   - | E | A |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define DCN_MR              0100000u            /* (M) master reset */
#define DCN_IRQ_RESET       0040000u            /* (R) interrupt request reset */
#define DCN_CHAN_MASK       0037000u            /* channel number mask */
#define DCN_ENABLE          0000002u            /* (E) enable store of preceding data or parameter word */
#define DCN_ACKN            0000001u            /* (A) acknowledge interrupt */

#define DCN_CHAN_SHIFT      9                   /* channel number alignment shift */

#define DCN_CHAN(c)         (((c) & DCN_CHAN_MASK) >> DCN_CHAN_SHIFT)

static const BITSET_NAME tdi_control_names [] = {       /* TDI control word names */
    "master reset",                                     /*   bit  0 */
    "reset interrupt",                                  /*   bit  1 */
    NULL,                                               /*   bit  2 */
    NULL,                                               /*   bit  3 */
    NULL,                                               /*   bit  4 */
    NULL,                                               /*   bit  5 */
    NULL,                                               /*   bit  6 */
    NULL,                                               /*   bit  7 */
    NULL,                                               /*   bit  8 */
    NULL,                                               /*   bit  9 */
    NULL,                                               /*   bit 10 */
    NULL,                                               /*   bit 11 */
    NULL,                                               /*   bit 12 */
    NULL,                                               /*   bit 13 */
    "store word",                                       /*   bit 14 */
    "acknowledge interrupt"                             /*   bit 15 */
    };

static const BITSET_FORMAT tdi_control_format =         /* names, offset, direction, alternates, bar */
    { FMT_INIT (tdi_control_names, 0, msb_first, no_alt, no_bar) };


/* TDI status word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | D | I | - | C | R | L | B | -   -   -   -   -   -   -   - |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define DST_DIO_OK          0040000u            /* (D) direct I/O OK to use */
#define DST_IRQ             0020000u            /* (I) interrupt requested */
#define DST_COMPLETE        0004000u            /* (C) operation is complete and channel is ready to interrupt */
#define DST_SEND_IRQ        0002000u            /* (R) interrupt request is for character sent */
#define DST_CHAR_LOST       0001000u            /* (L) character was lost */
#define DST_BREAK           0000400u            /* (B) break occurred */
#define DST_DIAGNOSE        0000000u            /* status is from an auxiliary channel (not used on ATC) */

#define DST_CHAN(n)         0                   /* position channel number for status (not used on ATC) */

static const BITSET_NAME tdi_status_names [] = {        /* TDI status word names */
    "DIO OK",                                           /*   bit  1 */
    "interrupt",                                        /*   bit  2 */
    NULL,                                               /*   bit  3 */
    "complete",                                         /*   bit  4 */
    "\1send\0receive",                                  /*   bit  5 */
    "lost",                                             /*   bit  6 */
    "break"                                             /*   bit  7 */
    };

static const BITSET_FORMAT tdi_status_format =          /* names, offset, direction, alternates, bar */
    { FMT_INIT (tdi_status_names, 8, msb_first, has_alt, no_bar) };



/* TDI parameter word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 1 | R | I | E | D | char size |           baud rate           |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The baud rate is encoded as 14400 / device_bit_rate - 1, but the manual says
   to round the result, so that, e.g., the 110 baud rate encoding of 129.91 is
   rounded to 130.  To reconstruct the rate without floating-point calculations,
   the parameter print routine uses:

     baud_rate = (2 * 14400 / (encoded_rate + 1) + 1) / 2

   ...which is equivalent to:

     baud_rate = (int) (14400 / (encoded_rate + 1) + 0.5)

   The multiplexer pads the received character data to the left with one-bits.

   The PAD_BITS function generates the pad bits, assuming that the received
   character transmission has one stop bit.  This isn't always correct, e.g., a
   Teleprinter uses two stop bits at 110 baud, but there's no way to reconstruct
   the number of stop bits from the receive parameter word.
*/

#define DPI_IS_PARAM        0100000u            /* value is a parameter (always set) */
#define DPI_IS_SEND         0040000u            /* (R) value is a send parameter */
#define DPI_ENABLE_IRQ      0020000u            /* (I) enable interrupt requests */
#define DPI_ENABLE_PARITY   0010000u            /* (E) enable parity for send */
#define DPI_ENABLE_ECHO     0010000u            /* (E) enable echo for receive */
#define DPI_DIAGNOSE        0004000u            /* (D) connect to the auxiliary channels */
#define DPI_SIZE_MASK       0003400u            /* character size mask */
#define DPI_RATE_MASK       0000377u            /* baud rate mask */

#define DPI_CHAR_CONFIG     (DPI_SIZE_MASK | DPI_RATE_MASK) /* character configuration data */

#define DPI_SIZE_SHIFT      8                   /* character size alignment shift */
#define DPI_RATE_SHIFT      0                   /* baud rate alignment shift */

#define DPI_CHAR_SIZE(p)    (((p) & DPI_SIZE_MASK) >> DPI_SIZE_SHIFT)
#define DPI_BAUD_RATE(p)    (((p) & DPI_RATE_MASK) >> DPI_RATE_SHIFT)

#define BAUD_RATE(p)        ((28800 / (DPI_BAUD_RATE (p) + 1) + 1) / 2)

#define PAD_BITS(c)         (~((1u << bits_per_char [DPI_CHAR_SIZE (c)] - 2) - 1))


static const uint32 bits_per_char [8] = {       /* bits per character, indexed by DPI_CHAR_SIZE encoding */
    9, 10, 11, 12, 5, 6, 7, 8
    };

static const BITSET_NAME tdi_parameter_names [] = {     /* TDI parameter word names */
    "\1send\0receive",                                  /*   bit  1 */
    "enable interrupt",                                 /*   bit  2 */
    "enable parity/echo",                               /*   bit  3 */
    "diagnose"                                          /*   bit  4 */
    };

static const BITSET_FORMAT tdi_parameter_format =       /* names, offset, direction, alternates, bar */
    { FMT_INIT (tdi_parameter_names, 11, msb_first, has_alt, append_bar) };


/* TDI output (send) data word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | 1 | -   - | S |                 send data                 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define DDS_IS_SEND         0040000u            /* value is a send data word (always set) */
#define DDS_SYNC            0004000u            /* (S) sync */
#define DDS_DATA_MASK       0003777u            /* data value mask */
#define DDS_PARITY          0000200u            /* data parity bit */

#define DDS_MARK            (DDS_SYNC | DDS_DATA_MASK)  /* all-mark character */

#define DDS_DATA(d)         ((d) & DDS_DATA_MASK)


static const BITSET_NAME tdi_output_data_names [] = {   /* TDI output data word names */
    "send",                                             /*   bit  1 */
    NULL,                                               /*   bit  2 */
    NULL,                                               /*   bit  3 */
    "sync"                                              /*   bit  4 */
    };

static const BITSET_FORMAT tdi_output_data_format =     /* names, offset, direction, alternates, bar */
    { FMT_INIT (tdi_output_data_names, 11, msb_first, no_alt, append_bar) };


/* TDI input (receive) data word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |      channel      | P |             receive data              |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define DDR_CHAN_MASK       0174000u            /* channel number mask */
#define DDR_PARITY          0002000u            /* (P) computed parity bit */
#define DDR_DATA_MASK       0001777u            /* data value mask */

#define DDR_CHAN_SHIFT      11                  /* channel number alignment shift */
#define DDR_DATA_SHIFT      0                   /* data alignment shift */

#define DDR_CHAN(n)         ((n) << DDR_CHAN_SHIFT & DDR_CHAN_MASK)
#define DDR_DATA(d)         ((d) << DDR_DATA_SHIFT & DDR_DATA_MASK)

#define DDR_TO_CHAN(w)      (((w) & DDR_CHAN_MASK) >> DDR_CHAN_SHIFT)
#define DDR_TO_DATA(w)      (((w) & DDR_DATA_MASK) >> DDR_DATA_SHIFT)


static const BITSET_NAME tdi_input_data_names [] = {    /* TDI input data word names */
    "\1odd parity\0even parity",                        /*   bit  5 */
    };

static const BITSET_FORMAT tdi_input_data_format =      /* names, offset, direction, alternates, bar */
    { FMT_INIT (tdi_input_data_names, 10, msb_first, has_alt, append_bar) };


/* TCI control word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M | R | S | U |    channel    | W | X | Q | T | Y | Z | C | D |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define CCN_MR              0100000u            /* (M) master reset */
#define CCN_IRQ_RESET       0040000u            /* (R) interrupt request reset */
#define CCN_SCAN            0020000u            /* (S) scan enable */
#define CCN_UPDATE          0010000u            /* (U) update enable */
#define CCN_CHAN_MASK       0007400u            /* channel number mask */
#define CCN_ECX_MASK        0000300u            /* control output enable mask */
#define CCN_EC2             0000200u            /* (W) C2 output enable */
#define CCN_EC1             0000100u            /* (X) C1 output enable */
#define CCN_CX_MASK         0000060u            /* output mask */
#define CCN_C2              0000040u            /* (Q) C2 output [RTS] */
#define CCN_C1              0000020u            /* (T) C1 output [DTR] */
#define CCN_STAT_MASK       0000017u            /* status RAM mask */
#define CCN_ESX_MASK        0000014u            /* status interrupt enable mask */
#define CCN_ES2             0000010u            /* (Y) S2 interrupt enable */
#define CCN_ES1             0000004u            /* (Z) S1 interrupt enable */
#define CCN_SX_MASK         0000003u            /* status mask */
#define CCN_S2              0000002u            /* (C) S2 status [DCD]*/
#define CCN_S1              0000001u            /* (D) S1 status [DSR] */

#define CCN_CHAN_SHIFT      8                   /* channel number alignment shift */
#define CCN_CX_SHIFT        4                   /* control alignment shift */
#define CCN_ECX_SHIFT       2                   /* control output enable alignment shift (to Cx) */
#define CCN_ESX_SHIFT       2                   /* status interrupt enable alignment shift */

#define CCN_CHAN(c)         (((c) & CCN_CHAN_MASK) >> CCN_CHAN_SHIFT)
#define CCN_ECX(c)          (((c) & CCN_ECX_MASK)  >> CCN_ECX_SHIFT)
#define CCN_CX(c)           (((c) & CCN_CX_MASK)   >> CCN_CX_SHIFT)
#define CCN_ESX(c)          (((c) & CCN_ESX_MASK)  >> CCN_ESX_SHIFT)

static const BITSET_NAME tci_control_names [] = {       /* TCI control word names */
    "master reset",                                     /*   bit  0 */
    "reset interrupt",                                  /*   bit  1 */
    "scan",                                             /*   bit  2 */
    "update",                                           /*   bit  3 */
    NULL,                                               /*   bit  4 */
    NULL,                                               /*   bit  5 */
    NULL,                                               /*   bit  6 */
    NULL,                                               /*   bit  7 */
    "EC2",                                              /*   bit  8 */
    "EC1",                                              /*   bit  9 */
    "\1C2\0~C2",                                        /*   bit 10 */
    "\1C1\0~C1",                                        /*   bit 11 */
    "ES2",                                              /*   bit 12 */
    "ES1",                                              /*   bit 13 */
    "\1S2\0~S2",                                        /*   bit 14 */
    "\1S1\0~S1"                                         /*   bit 15 */
    };

static const BITSET_FORMAT tci_control_format =         /* names, offset, direction, alternates, bar */
    { FMT_INIT (tci_control_names, 0, msb_first, has_alt, no_bar) };


/* TCI status word.

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 1 | I | 1 |    channel    | - | - | J | K | Y | Z | C | D |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define CST_DIO_OK          0040000u            /* direct I/O OK to use (always set) */
#define CST_IRQ             0020000u            /* (I) interrupt request */
#define CST_ON              0010000u            /* (always set) */
#define CST_CHAN_MASK       0007400u            /* channel number mask */
#define CST_IX_MASK         0000060u            /* status interrupt mask */
#define CST_I2              0000040u            /* (J) S2 interrupt */
#define CST_I1              0000020u            /* (K) S1 interrupt */
#define CST_ESX_MASK        0000014u            /* status interrupt enable mask */
#define CST_ES2             0000010u            /* (Y) S2 interrupt enable */
#define CST_ES1             0000004u            /* (Z) S1 interrupt enable */
#define CST_SX_MASK         0000003u            /* status mask */
#define CST_S2              0000002u            /* (C) S2 status [DCD] */
#define CST_S1              0000001u            /* (D) S1 status [DSR] */

#define CST_CHAN_SHIFT      8                   /* channel number alignment shift */
#define CST_IX_SHIFT        4                   /* status interrupt alignment shift */

#define CST_CHAN(n)         ((n) << CST_CHAN_SHIFT & CST_CHAN_MASK)
#define CST_IX(i)           ((i) << CST_IX_SHIFT   & CST_IX_MASK)

static const BITSET_NAME tci_status_names [] = {        /* TCI status word names */
    "interrupt",                                        /*   bit  2 */
    NULL,                                               /*   bit  3 */
    NULL,                                               /*   bit  4 */
    NULL,                                               /*   bit  5 */
    NULL,                                               /*   bit  6 */
    NULL,                                               /*   bit  7 */
    NULL,                                               /*   bit  8 */
    NULL,                                               /*   bit  9 */
    "I2",                                               /*   bit 10 */
    "I1",                                               /*   bit 11 */
    "ES2",                                              /*   bit 12 */
    "ES1",                                              /*   bit 13 */
    "\1S2\0~S2",                                        /*   bit 14 */
    "\1S1\0~S1"                                         /*   bit 15 */
    };

static const BITSET_FORMAT tci_status_format =          /* names, offset, direction, alternates, bar */
    { FMT_INIT (tci_status_names, 0, msb_first, has_alt, no_bar) };


/* TCI #1 serial line bits */

#define RTS                 CCN_C2              /* TCI #1 C2 = Request to Send */
#define DTR                 CCN_C1              /* TCI #1 C1 = Data Terminal Ready */
#define DCD                 CCN_S2              /* TCI #1 S2 = Data Carrier Detect */
#define DSR                 CCN_S1              /* TCI #1 S1 = Data Set Ready */

static const BITSET_NAME tci_line_names [] = {  /* TCI serial line status names */
    "RTS",                                      /*   bit 10 */
    "DTR",                                      /*   bit 11 */
    NULL,                                       /*   bit 12 */
    NULL,                                       /*   bit 13 */
    "DCD",                                      /*   bit 14 */
    "DSR"                                       /*   bit 15 */
    };

static const BITSET_FORMAT tci_line_format =    /* names, offset, direction, alternates, bar */
    { FMT_INIT (tci_line_names, 0, msb_first, no_alt, no_bar) };


/* ATC global state */

t_bool atc_is_polling = TRUE;                   /* TRUE if the ATC is polling for the simulation console */


/* TDI interface state */

static HP_WORD tdi_control_word = 0;            /* control word */
static HP_WORD tdi_status_word  = 0;            /* status word */
static HP_WORD tdi_read_word    = 0;            /* read word */
static HP_WORD tdi_write_word   = 0;            /* write word */

static FLIP_FLOP tdi_interrupt_mask = SET;      /* interrupt mask flip-flop */
static FLIP_FLOP tdi_data_flag      = CLEAR;    /* data flag */

static int32 fast_data_time = FAST_IO_TIME;     /* fast receive/send time */


/* TDI per-channel state */

static HP_WORD recv_status [RECV_CHAN_COUNT];   /* receive status words */
static HP_WORD recv_param  [RECV_CHAN_COUNT];   /* receive parameter words */
static HP_WORD recv_buffer [RECV_CHAN_COUNT];   /* receive character buffers */

static HP_WORD send_status [SEND_CHAN_COUNT];   /* send status words */
static HP_WORD send_param  [SEND_CHAN_COUNT];   /* send parameter words */
static HP_WORD send_buffer [SEND_CHAN_COUNT];   /* send character buffers */


/* TCI interface state */

static HP_WORD tci_control_word = 0;            /* control word */
static HP_WORD tci_status_word  = 0;            /* status word */
static uint32  tci_cntr         = 0;            /* channel counter */

static FLIP_FLOP tci_interrupt_mask = SET;      /* interrupt mask flip-flop */
static FLIP_FLOP tci_scan           = CLEAR;    /* scanning enabled flip-flop */


/* TCI per-channel state */

static uint8 cntl_status [TERM_COUNT];          /* C2/C1/S2/S1 line status */
static uint8 cntl_param  [TERM_COUNT];          /* ES2/ES1/S2/S1 parameter RAM */


/* ATC local SCP support routines */

static CNTLR_INTRF atcd_interface;
static CNTLR_INTRF atcc_interface;

static t_stat atc_set_endis   (UNIT *uptr, int32 value, char  *cptr, void *desc);
static t_stat atc_set_mode    (UNIT *uptr, int32 value, char  *cptr, void *desc);
static t_stat atc_show_mode   (FILE *st,   UNIT  *uptr, int32 value, void *desc);
static t_stat atc_show_status (FILE *st,   UNIT  *uptr, int32 value, void *desc);

static t_stat atcd_reset (DEVICE *dptr);
static t_stat atcc_reset (DEVICE *dptr);

static t_stat atcd_attach (UNIT *uptr, char *cptr);
static t_stat atcd_detach (UNIT *uptr);


/* ATC local utility routines */

static void tdi_set_interrupt (void);
static void tdi_master_reset  (void);
static void tci_master_reset  (void);

static t_stat  line_service  (UNIT    *uptr);
static t_stat  poll_service  (UNIT    *uptr);
static int32   activate_unit (UNIT    *uptr,   ACTIVATOR reason);
static uint32  service_time  (HP_WORD control, ACTIVATOR reason);
static void    store         (HP_WORD control, HP_WORD   data);
static void    receive       (int32   channel, int32 data, t_bool loopback);
static void    diagnose      (HP_WORD control, int32 data);
static void    scan_channels (int32   channel);
static HP_WORD scan_status   (void);
static void    update_status (uint32  channel);


/* ATC SCP data structures */

DEVICE atcd_dev;                                /* incomplete device structure */
DEVICE atcc_dev;                                /* incomplete device structure */


/* Terminal multiplexer library structures.

   The ATC uses the connection line order feature to bypass channel 0, which is
   dedicated to the system console.  For convenience, the system console is
   connected to the simulation console.  As such, it calls the console I/O
   routines instead of the terminal multiplexer routines.
*/

static int32 atcd_order [TERM_COUNT] = {        /* line connection order */
    1,  2,  3,  4,  5,  6,  7,  8,
    9, 10, 11, 12, 13, 14, 15, -1 };

static TMLN atcd_ldsc [TERM_COUNT] = {          /* line descriptors */
    { 0 }
    };

static TMXR atcd_mdsc = {                       /* multiplexer descriptor */
    TERM_COUNT,                                 /* number of terminal lines */
    0,                                          /* listening port (reserved) */
    0,                                          /* master socket  (reserved) */
    atcd_ldsc,                                  /* line descriptors */
    atcd_order,                                 /* line connection order */
    &atcd_dev                                   /* multiplexer device */
    };


/* Device information blocks */

static DIB atcd_dib = {
    &atcd_interface,                            /* device interface */
    7,                                          /* device number */
    SRNO_UNUSED,                                /* service request number */
    0,                                          /* interrupt priority */
    INTMASK_E                                   /* interrupt mask */
    };

static DIB atcc_dib = {
    &atcc_interface,                            /* device interface */
    8,                                          /* device number */
    SRNO_UNUSED,                                /* service request number */
    8,                                          /* interrupt priority */
    INTMASK_E                                   /* interrupt mask */
    };


/* Unit lists.

   The first sixteen TDI units correspond to the sixteen multiplexer main
   send/receive channels.  These handle character I/O via the Telnet library.  A
   seventeenth unit is responsible for polling for connections and socket I/O.
   It also holds the master socket.

   Channel 0 is reserved for the system console and is connected to the
   simulation console.  As such, it's not likely to be using an HP terminal
   emulator, so the default is CAPSLOCK input mode and 7P output mode.  The
   remainder of the channels default to NOCAPSLOCK and 7B, as they're likely to
   be connected to HP terminals or terminal emulators.  All channels initially
   omit the UNIT_MODEM flag to allow the MPE terminal subtype configuration to
   determine which channels support data sets and which do not.

   The TDI line service routine runs only when there are characters to read or
   write.  It is scheduled either at a realistic rate corresponding to the
   programmed baud rate of the channel to be serviced, or at a somewhat faster
   optimized rate.  The multiplexer connection and input poll must run
   continuously, but it may operate much more slowly, as the only requirement is
   that it must not present a perceptible lag to human input.  It is coscheduled
   with the process clock to permit idling.  The poll unit is hidden by
   disabling it, so as to present a logical picture of the multiplexer to the
   user.

   The TCI does not use any units, but a dummy one is defined to satisfy SCP
   requirements.


   Implementation notes:

    1. There are no units corresponding to the auxiliary receive channels.  This
       is because reception isn't scheduled on these channels but instead occurs
       concurrently with the main channel that is connected to the auxiliary
       channels.
*/

static UNIT atcd_unit [UNIT_COUNT] = {
    { UDATA (&line_service, TT_MODE_7P | UNIT_LOCALACK | UNIT_CAPSLOCK,  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK,                  0)            },
    { UDATA (&poll_service, UNIT_ATTABLE | UNIT_DIS | UNIT_IDLE,         0), POLL_TIME }  /* multiplexer poll unit */
    };

static UNIT atcc_unit [] = {                    /* a dummy unit to satisfy SCP requirements */
    { UDATA (NULL, 0, 0) }
    };


/* Register lists.

   The internal state of the TDI and TCI are exposed to the user and to ensure
   that SAVE and RESTORE pick up the values.  The user may set FTIME explicitly
   as needed to accommodate software that does not work with the default
   setting.


   Implementation notes:

    1. The TCI control and status line register definitions use the VM-defined
       FBDATA macro.  This macro defines a flag that is replicated in the same
       bit position in each element of an array.
*/

static REG atcd_reg [] = {
/*    Macro   Name    Location             Radix  Width  Offset       Depth         Flags   */
/*    ------  ------  -------------------  -----  -----  ------  ----------------  -------- */
    { ORDATA (CNTL,   tdi_control_word,            16),                                     },
    { ORDATA (STAT,   tdi_status_word,             16),                                     },
    { ORDATA (READ,   tdi_read_word,               16),                            REG_X    },
    { ORDATA (WRITE,  tdi_write_word,              16),                            REG_X    },
    { FLDATA (FLAG,   tdi_data_flag,                        0)                              },
    { FLDATA (MASK,   tdi_interrupt_mask,                   0)                              },
    { DRDATA (FTIME,  fast_data_time,              24),                            PV_LEFT  },
    { BRDATA (RSTAT,  recv_status,           8,    16,           RECV_CHAN_COUNT)           },
    { BRDATA (RPARM,  recv_param,            8,    16,           RECV_CHAN_COUNT)           },
    { BRDATA (RBUFR,  recv_buffer,           8,    16,           RECV_CHAN_COUNT), REG_X    },
    { BRDATA (SSTAT,  send_status,           8,    16,           SEND_CHAN_COUNT)           },
    { BRDATA (SPARM,  send_param,            8,    16,           SEND_CHAN_COUNT)           },
    { BRDATA (SBUFR,  send_buffer,           8,    16,           SEND_CHAN_COUNT), REG_X    },
    { FLDATA (POLL,   atc_is_polling,                       0),                    REG_HRO  },

      DIB_REGS (atcd_dib),

    { NULL }
    };

static REG atcc_reg [] = {
/*    Macro   Name    Location             Width  Offset     Depth      Flags   */
/*    ------  ------  -------------------  -----  ------  -----------  -------  */
    { ORDATA (CNTL,   tci_control_word,     16),                                },
    { ORDATA (STAT,   tci_status_word,      16),                                },
    { DRDATA (CNTR,   tci_cntr,              4)                                 },
    { FLDATA (SCAN,   tci_scan,                      0)                         },
    { FLDATA (MASK,   tci_interrupt_mask,            0)                         },

    { FBDATA (C2,     cntl_status,                   5,   TERM_COUNT), PV_RZRO  },
    { FBDATA (C1,     cntl_status,                   4,   TERM_COUNT), PV_RZRO  },
    { FBDATA (S2,     cntl_status,                   1,   TERM_COUNT), PV_RZRO  },
    { FBDATA (S1,     cntl_status,                   0,   TERM_COUNT), PV_RZRO  },

    { FBDATA (ES2,    cntl_param,                    3,   TERM_COUNT), PV_RZRO  },
    { FBDATA (ES1,    cntl_param,                    2,   TERM_COUNT), PV_RZRO  },
    { FBDATA (MS2,    cntl_param,                    1,   TERM_COUNT), PV_RZRO  },
    { FBDATA (MS1,    cntl_param,                    0,   TERM_COUNT), PV_RZRO  },

      DIB_REGS (atcc_dib),

    { NULL }
    };


/* Modifier lists.


   Implementation notes:

    1. User-specified line connection orders are supported, but the LINEORDER
       modifier entry "value" field restricts the minimum allowed line number
       to 1, so that line 0 (the system console) cannot be included in the
       connection list.
*/

typedef enum {
    Fast_Time,
    Real_Time,
    Terminal,
    Diagnostic
    } DEVICE_MODES;


static MTAB atcd_mod [] = {
/*    Mask Value     Match Value    Print String      Match String  Validation  Display  Descriptor */
/*    -------------  -------------  ----------------  ------------  ----------  -------  ---------- */
    { UNIT_MODEM,    UNIT_MODEM,    "data set",       NULL,         NULL,       NULL,    NULL       },
    { UNIT_MODEM,    0,             "direct",         NULL,         NULL,       NULL,    NULL       },

    { UNIT_LOCALACK, UNIT_LOCALACK, "local ENQ/ACK",  "LOCALACK",   NULL,       NULL,    NULL       },
    { UNIT_LOCALACK, 0,             "remote ENQ/ACK", "REMOTEACK",  NULL,       NULL,    NULL       },

    { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK",   NULL,       NULL,    NULL       },
    { UNIT_CAPSLOCK, 0,             "CAPS LOCK up",   "NOCAPSLOCK", NULL,       NULL,    NULL       },

    { TT_MODE,       TT_MODE_UC,    "UC output",      "UC",         NULL,       NULL,    NULL       },
    { TT_MODE,       TT_MODE_7B,    "7b output",      "7B",         NULL,       NULL,    NULL       },
    { TT_MODE,       TT_MODE_7P,    "7p output",      "7P",         NULL,       NULL,    NULL       },
    { TT_MODE,       TT_MODE_8B,    "8b output",      "8B",         NULL,       NULL,    NULL       },

/*    Entry Flags           Value        Print String   Match String  Validation         Display             Descriptor          */
/*    --------------------  -----------  -------------  ------------  -----------------  ------------------  ------------------- */
    { MTAB_XUN | MTAB_NC,   0,           "LOG",         "LOG",        &tmxr_set_log,     &tmxr_show_log,     (void *) &atcd_mdsc },
    { MTAB_XUN | MTAB_NC,   0,           NULL,          "NOLOG",      &tmxr_set_nolog,   NULL,               (void *) &atcd_mdsc },
    { MTAB_XUN,             0,           NULL,          "DISCONNECT", &tmxr_dscln,       NULL,               (void *) &atcd_mdsc },

    { MTAB_XDV,             Fast_Time,   NULL,          "FASTTIME",   &atc_set_mode,     NULL,               (void *) &atcd_dev  },
    { MTAB_XDV,             Real_Time,   NULL,          "REALTIME",   &atc_set_mode,     NULL,               (void *) &atcd_dev  },
    { MTAB_XDV,             Terminal,    NULL,          "TERMINAL",   &atc_set_mode,     NULL,               (void *) &atcd_dev  },
    { MTAB_XDV,             Diagnostic,  NULL,          "DIAGNOSTIC", &atc_set_mode,     NULL,               (void *) &atcd_dev  },
    { MTAB_XDV,             0,           "MODES",       NULL,         NULL,              &atc_show_mode,     (void *) &atcd_dev  },

    { MTAB_XDV,             0,           "",            NULL,         NULL,              &atc_show_status,   (void *) &atcd_mdsc },
    { MTAB_XDV | MTAB_NMO,  1,           "CONNECTIONS", NULL,         NULL,              &tmxr_show_cstat,   (void *) &atcd_mdsc },
    { MTAB_XDV | MTAB_NMO,  0,           "STATISTICS",  NULL,         NULL,              &tmxr_show_cstat,   (void *) &atcd_mdsc },
    { MTAB_XDV | MTAB_NMO,  1,           "LINEORDER",   "LINEORDER",  &tmxr_set_lnorder, &tmxr_show_lnorder, (void *) &atcd_mdsc },

    { MTAB_XDV,             VAL_DEVNO,   "DEVNO",       "DEVNO",      &hp_set_dib,       &hp_show_dib,       (void *) &atcd_dib  },
    { MTAB_XDV,             VAL_INTMASK, "INTMASK",     "INTMASK",    &hp_set_dib,       &hp_show_dib,       (void *) &atcd_dib  },
    { MTAB_XDV,             VAL_INTPRI,  "INTPRI",      "INTPRI",     &hp_set_dib,       &hp_show_dib,       (void *) &atcd_dib  },

    { MTAB_XDV | MTAB_NMO,  1,           NULL,          "ENABLED",    &atc_set_endis,    NULL,               NULL                },
    { MTAB_XDV | MTAB_NMO,  0,           NULL,          "DISABLED",   &atc_set_endis,    NULL,               NULL                },
    { 0 }
    };

static MTAB atcc_mod [] = {
/*    Entry Flags  Value        Print String  Match String  Validation     Display         Descriptor         */
/*    -----------  -----------  ------------  ------------  -------------  --------------  ------------------ */
    { MTAB_XDV,    Terminal,    NULL,         "TERMINAL",   &atc_set_mode, NULL,           (void *) &atcc_dev },
    { MTAB_XDV,    Diagnostic,  NULL,         "DIAGNOSTIC", &atc_set_mode, NULL,           (void *) &atcc_dev },
    { MTAB_XDV,    1,           "MODES",      NULL,         NULL,          &atc_show_mode, (void *) &atcc_dev },

    { MTAB_XDV,    VAL_DEVNO,   "DEVNO",      "DEVNO",      &hp_set_dib,   &hp_show_dib,   (void *) &atcc_dib },
    { MTAB_XDV,    VAL_INTMASK, "INTMASK",    "INTMASK",    &hp_set_dib,   &hp_show_dib,   (void *) &atcc_dib },
    { MTAB_XDV,    VAL_INTPRI,  "INTPRI",     "INTPRI",     &hp_set_dib,   &hp_show_dib,   (void *) &atcc_dib },
    { 0 }
    };


/* Debugging trace lists */

static DEBTAB atcd_deb [] = {
    { "CSRW",  DEB_CSRW  },                     /* Interface control, status, read, and write actions */
    { "SERV",  DEB_SERV  },                     /* Channel unit service scheduling calls */
    { "PSERV", DEB_PSERV },                     /* Poll unit service scheduling calls */
    { "XFER",  DEB_XFER  },                     /* Data receptions and transmissions */
    { "IOBUS", DEB_IOB   },                     /* Interface I/O bus signals and data words */
    { NULL,    0         }
    };

static DEBTAB atcc_deb [] = {
    { "CSRW",  DEB_CSRW  },                     /* Interface control, status, read, and write actions */
    { "PSERV", DEB_PSERV },                     /* Poll unit service scheduling calls */
    { "XFER",  DEB_XFER  },                     /* Control and status line changes */
    { "IOBUS", DEB_IOB   },                     /* Interface I/O bus signals and data words */
    { NULL,    0         }
    };


/* Device descriptors.

   Both devices may be disabled.  However, we want to be able to disable the TDI
   while it is polling for the simulation console, which the standard SCP
   routine will not do (it refuses if any unit is active).  So we define our own
   DISABLED and ENABLED modifiers and a validation routine that sets or clears
   the DEV_DIS flag and then calls atcd_reset.  The reset routine cancels or
   reenables the poll as indicated.
*/

DEVICE atcd_dev = {
    "ATCD",                                     /* device name */
    atcd_unit,                                  /* unit array */
    atcd_reg,                                   /* register array */
    atcd_mod,                                   /* modifier array */
    UNIT_COUNT,                                 /* number of units */
    10,                                         /* address radix */
    PA_WIDTH,                                   /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    DV_WIDTH,                                   /* data width */
    &tmxr_ex,                                   /* examine routine */
    &tmxr_dep,                                  /* deposit routine */
    &atcd_reset,                                /* reset routine */
    NULL,                                       /* boot routine */
    &atcd_attach,                               /* attach routine */
    &atcd_detach,                               /* detach routine */
    &atcd_dib,                                  /* device information block pointer */
    DEV_DEBUG | DEV_DISABLE,                    /* device flags */
    0,                                          /* debug control flags */
    atcd_deb,                                   /* debug flag name array */
    NULL,                                       /* memory size change routine */
    NULL };                                     /* logical device name */

DEVICE atcc_dev = {
    "ATCC",                                     /* device name */
    atcc_unit,                                  /* unit array */
    atcc_reg,                                   /* register array */
    atcc_mod,                                   /* modifier array */
    1,                                          /* number of units */
    10,                                         /* address radix */
    PA_WIDTH,                                   /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    DV_WIDTH,                                   /* data width */
    NULL,                                       /* examine routine */
    NULL,                                       /* deposit routine */
    &atcc_reset,                                /* reset routine */
    NULL,                                       /* boot routine */
    NULL,                                       /* attach routine */
    NULL,                                       /* detach routine */
    &atcc_dib,                                  /* device information block pointer */
    DEV_DEBUG | DEV_DISABLE,                    /* device flags */
    0,                                          /* debug control flags */
    atcc_deb,                                   /* debug flag name array */
    NULL,                                       /* memory size change routine */
    NULL };                                     /* logical device name */



/* ATC local SCP support routines */



/* TDI interface.

   The interface is installed on the IOP bus and receives direct I/O commands
   from the IOP.  In simulation, the asserted signals on the bus are represented
   as bits in the inbound_signals set.  Each signal is processed sequentially in
   numerical order, and a set of similar outbound_signals is assembled and
   returned to the caller, simulating assertion of the corresponding backplane
   signals.

   Before a channel can receive or send, it must be configured.  The number of
   the channel to configure is set via a CIO instruction, followed by parameters
   for baud rate and character size via WIO instructions.  Data to be sent is
   passed to the interface via WIO, while received data is picked up with RIO
   instructions.

   When a channel has completed sending or receiving a character, it will set
   its completion flag.  If the TDI data flag is clear, indicating that all
   prior interrupts have been serviced, a scan of the serviced channel is made
   to see if the channel is enabled to interrupt.  If it is, the TDI data flag
   will be set, the channel flag will be cleared, and an interrupt will be
   requested.  When the interrupt is serviced and acknowledged, the flag will be
   cleared, and the scan will continue to look for other channel flags.

   The status word is set during the scan to reflect the interrupting channel
   status.  If status bit 3 (DST_COMPLETE) is clear, then status bits 5, 6, and
   7 (DST_SEND_IRQ, DST_CHAR_LOST, and DST_BREAK) retain their values from the
   prior send or receive interrupt.


   Implementation notes:

    1. In hardware, the DIO OK status bit (bit 1) is denied when a store to the
       recirculating memory is pending and is reasserted once the designated
       channel rotates into the window and the parameter or data is stored.  The
       duration of the denial varies from 0 to 69.44 microseconds, depending on
       the location of the window in memory when DWRITESTB is asserted.  In
       simulation, DIO OK is always asserted.

    2. Receipt of a DRESETINT signal clears the interrupt request and active
       flip-flops but does not cancel a request pending but not yet serviced by
       the IOP.  However, when the IOP does service the request by asserting
       INTPOLLIN, the interface routine returns INTPOLLOUT, which will cancel
       the request.
*/

static SIGNALS_DATA atcd_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET    working_set      = inbound_signals;
HP_WORD        outbound_value   = 0;
OUTBOUND_SET   outbound_signals = NO_SIGNALS;

tprintf (atcd_dev, DEB_IOB, "Received data %06o with signals %s\n",
         inbound_value, fmt_bitset (inbound_signals, inbound_format));

while (working_set) {                                   /* while signals remain */
    signal = IONEXTSIG (working_set);                   /*   isolate the next signal */

    switch (signal) {                                   /* dispatch the I/O signal */

        case DCONTSTB:
            tprintf (atcd_dev, DEB_CSRW, (inbound_value & DCN_ENABLE
                                           ? "Control is %s | channel %u\n"
                                           : "Control is %s\n"),
                     fmt_bitset (inbound_value, tdi_control_format),
                     DCN_CHAN (inbound_value));

            tdi_control_word = inbound_value;           /* save the control word */

            if (tdi_control_word & DCN_MR)              /* if master reset is requested */
                tdi_master_reset ();                    /*   then perform an I/O reset */

            if (tdi_control_word & DCN_IRQ_RESET)       /* if reset interrupt is requested */
                dibptr->interrupt_request = CLEAR;      /*   then clear the interrupt request */

            if (tdi_control_word & DCN_ENABLE)              /* if output is enabled */
                store (tdi_control_word, tdi_write_word);   /*   then store the parameter or data word */

            if (tdi_control_word & DCN_ACKN) {          /* if acknowledge interrupt is requested */
                tdi_data_flag = CLEAR;                  /*   then clear the data flag */

                scan_channels (SCAN_ALL);               /* scan all channels for a new interrupt request */
                }
            break;


        case DSTATSTB:
            tdi_status_word |= DST_DIO_OK;              /* the interface is always ready for commands */

            if (dibptr->interrupt_request == SET)       /* reflect the interrupt request value */
                tdi_status_word |= DST_IRQ;             /*   in the status word */
            else                                        /*     to indicate */
                tdi_status_word &= ~DST_IRQ;            /*       whether or not a request is pending */

            if (tdi_data_flag == SET)                   /* reflect the data flag value */
                tdi_status_word |= DST_COMPLETE;        /*   in the status word */
            else                                        /*     to indicate */
                tdi_status_word &= ~DST_COMPLETE;       /*       whether or not a channel has completed */

            outbound_value = tdi_status_word;           /* return the status word */

            tprintf (atcd_dev, DEB_CSRW, "Status is %s\n",
                     fmt_bitset (outbound_value, tdi_status_format));
            break;


        case DWRITESTB:
            tdi_write_word = inbound_value;             /* save the data or parameter word */

            if (TRACING (atcd_dev, DEB_CSRW))
                if (inbound_value & DPI_IS_PARAM)
                    hp_trace (&atcd_dev, DEB_CSRW, "Parameter is %s%u bits | %u baud\n",
                              fmt_bitset (inbound_value, tdi_parameter_format),
                              bits_per_char [DPI_CHAR_SIZE (inbound_value)],
                              BAUD_RATE (inbound_value));

                else
                    hp_trace (&atcd_dev, DEB_CSRW, "Output data is %s%04o\n",
                              fmt_bitset (inbound_value, tdi_output_data_format),
                              DDS_DATA (inbound_value));
            break;


        case DREADSTB:
            outbound_value = tdi_read_word;             /* return the data word */

            tprintf (atcd_dev, DEB_CSRW, "Input data is channel %u | %s%04o\n",
                     DDR_TO_CHAN (outbound_value),
                     fmt_bitset (outbound_value, tdi_input_data_format),
                     DDR_TO_DATA (outbound_value));
            break;


        case DSETINT:
            dibptr->interrupt_request = SET;            /* request an interrupt */

            if (tdi_interrupt_mask)                     /* if the interrupt mask is satisfied */
                outbound_signals |= INTREQ;             /*   then assert the INTREQ signal */
            break;


        case DRESETINT:
            dibptr->interrupt_active = CLEAR;           /* reset the interrupt active flip-flop */
            break;


        case INTPOLLIN:
            if (dibptr->interrupt_request) {            /* if a request is pending */
                dibptr->interrupt_request = CLEAR;      /*   then clear it */
                dibptr->interrupt_active  = SET;        /*     and mark it now active */

                outbound_signals |= INTACK;             /* acknowledge the interrupt */
                outbound_value = dibptr->device_number; /*   and return our device number */
                }
            else                                        /* otherwise the request has been reset */
                outbound_signals |= INTPOLLOUT;         /*   so let the IOP know to cancel it */
            break;


        case DSETMASK:
            if (dibptr->interrupt_mask == INTMASK_E)                /* if the mask is always enabled */
                tdi_interrupt_mask = SET;                           /*   then set the mask flip-flop */
            else                                                    /* otherwise */
                tdi_interrupt_mask = D_FF (dibptr->interrupt_mask   /*   set the mask flip-flop if the mask bit */
                                           & inbound_value);        /*     is present in the mask value */

            if (tdi_interrupt_mask && dibptr->interrupt_request)    /* if the mask is enabled and a request is pending */
                outbound_signals |= INTREQ;                         /*   then assert INTREQ */
            break;


        case DSTARTIO:                                  /* not used by this interface */
        case XFERERROR:                                 /* not used by this interface */
        case ACKSR:                                     /* not used by this interface */
        case TOGGLESR:                                  /* not used by this interface */
        case TOGGLESIOOK:                               /* not used by this interface */
        case TOGGLEINXFER:                              /* not used by this interface */
        case TOGGLEOUTXFER:                             /* not used by this interface */
        case READNEXTWD:                                /* not used by this interface */
        case PREADSTB:                                  /* not used by this interface */
        case PWRITESTB:                                 /* not used by this interface */
        case PCMD1:                                     /* not used by this interface */
        case PCONTSTB:                                  /* not used by this interface */
        case PSTATSTB:                                  /* not used by this interface */
        case DEVNODB:                                   /* not used by this interface */
        case SETINT:                                    /* not used by this interface */
        case EOT:                                       /* not used by this interface */
        case SETJMP:                                    /* not used by this interface */
        case CHANSO:                                    /* not used by this interface */
        case PFWARN:                                    /* not used by this interface */
            break;
        }

    IOCLEARSIG (working_set, signal);                   /* remove the current signal from the set */
    }

tprintf (atcd_dev, DEB_IOB, "Returned data %06o with signals %s\n",
         outbound_value, fmt_bitset (outbound_signals, outbound_format));

return IORETURN (outbound_signals, outbound_value);     /* return the outbound signals and value */
}


/* TCI interface.

   The interface is installed on the IOP bus and receives direct I/O commands
   from the IOP.  In simulation, the asserted signals on the bus are represented
   as bits in the inbound_signals set.  Each signal is processed sequentially in
   numerical order, and a set of similar outbound_signals is assembled and
   returned to the caller, simulating assertion of the corresponding backplane
   signals.  For this interface, a read order executes identically to a test
   order, and a write order is ignored.

   The control word contains three independent enables that affect the
   interpretation of the rest of the word.  Bit 3 (CCN_UPDATE) must be set to
   enable storing bits 12-15 (CCN_ES2/1 and CCN_S2/1) into the state RAM.  Bits
   8 (CCN_EC2) and 9 (CCN_EC1) must be set to enable storing bits 10 (CCN_C2)
   and 11 (CCN_C1), respectively, into the addressable latch.  If none of these
   enables are set, then only bits 0-2 are interpreted.


   Implementation notes:

    1. The cntl_status array contains the values for the serial device control
       and status lines.  The line bit positions in the array correspond to the
       C2/C1 and S2/S1 positions in the control word.

    2. A control word write directed to a given channel sets that channel's
       UNIT_MODEM flag to indicate that the serial line status should be
       updated at each input poll service.

    3. The terminal multiplexer library will disconnect an associated Telnet
       session if DTR is dropped.

    4. Receipt of a DRESETINT signal clears the interrupt request and active
       flip-flops but does not cancel a request pending but not yet serviced by
       the IOP.  However, when the IOP does service the request by asserting
       INTPOLLIN, the interface routine returns INTPOLLOUT, which will cancel
       the request.
*/

static SIGNALS_DATA atcc_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
INBOUND_SIGNAL signal;
INBOUND_SET    working_set      = inbound_signals;
HP_WORD        outbound_value   = 0;
OUTBOUND_SET   outbound_signals = NO_SIGNALS;
TMCKT          control_set;

tprintf (atcc_dev, DEB_IOB, "Received data %06o with signals %s\n",
         inbound_value, fmt_bitset (inbound_signals, inbound_format));

while (working_set) {                                   /* while signals remain */
    signal = IONEXTSIG (working_set);                   /*   isolate the next signal */

    switch (signal) {                                   /* dispatch the I/O signal */

        case DCONTSTB:
            tci_cntr = CCN_CHAN (inbound_value);        /* set the counter to the target channel */

            tprintf (atcc_dev, DEB_CSRW, "Control is channel %u | %s\n",
                     tci_cntr, fmt_bitset (inbound_value, tci_control_format));

            tci_control_word = inbound_value;           /* save the control word */

            line_unit [tci_cntr].flags |= UNIT_MODEM;   /* set the modem control flag on this unit */

            if (tci_control_word & CCN_MR)              /* if master reset is requested */
                tci_master_reset ();                    /*   then perform an I/O reset */

            if (tci_control_word & CCN_IRQ_RESET)       /* if reset interrupt is requested */
                dibptr->interrupt_request = CLEAR;      /*   then clear the interrupt request */

            cntl_status [tci_cntr] = cntl_status [tci_cntr]             /* set the control lines */
                                       & ~CCN_ECX (tci_control_word)    /*   that are enabled for output */
                                     | CCN_CX_MASK                      /*     to the control bits */
                                       & CCN_ECX (tci_control_word)     /*       that are enabled */
                                       & tci_control_word;              /*         in the control word */

            tprintf (atcc_dev, DEB_XFER, "Channel %u line status is %s\n",
                     tci_cntr, fmt_bitset (cntl_status [tci_cntr], tci_line_format));

            if (atcc_dev.flags & DEV_DIAG) {                /* if the interface is in diagnostic mode */
                cntl_status [tci_cntr ^ 1] =                /*   then loop the control lines */
                  cntl_status [tci_cntr ^ 1] & ~CCN_SX_MASK /*     back to the alternate channel */
                  | CCN_CX (cntl_status [tci_cntr]);        /*       from the selected channel */

                tprintf (atcc_dev, DEB_XFER, "Channel %u line status is %s\n",
                         tci_cntr ^ 1, fmt_bitset (cntl_status [tci_cntr ^ 1], tci_line_format));
                }

            else if (tci_control_word & CCN_ECX_MASK) { /* otherwise if either control line is enabled */
                control_set = No_Signals;               /*   then initialize the control set */

                if (RTS & cntl_status [tci_cntr])       /* if the RTS flag is set */
                    control_set |= RTS_Control;         /*   then assert the RTS signal */

                if (DTR & cntl_status [tci_cntr])       /* if the DTR flag is set */
                    control_set |= DTR_Control;         /*   then assert the DTR signal */

                else if (cntl_status [tci_cntr] & DSR)  /* setting DTR down will disconnect the channel */
                    tprintf (atcc_dev, DEB_CSRW, "Channel %u disconnected by DTR drop\n",
                             tci_cntr);

                tmxr_control_line (&atcd_ldsc [tci_cntr],   /* tell the multiplexer library */
                                   control_set);            /*   to set the lines as indicated */
                }

            if (tci_control_word & CCN_UPDATE)              /* if the status output is enabled */
                cntl_param [tci_cntr] = tci_control_word    /*   then store the status line enables and states */
                                          & CCN_STAT_MASK;  /*     in the parameter RAM */

            tci_scan = D_FF (tci_control_word & CCN_SCAN);  /* set or clear the scan flip-flop as directed */

            if (tci_scan)                               /* if scanning is enabled */
                scan_status ();                         /*   then look for channel status changes */
            break;


        case DREADSTB:                                  /* RIO and TIO return the same value */
        case DSTATSTB:
            if ((atcc_dev.flags & DEV_DIAG) == 0)       /* if the interface is not in diagnostic mode */
                update_status (tci_cntr);               /*   then update the current line status */

            tci_status_word = CST_DIO_OK | CST_ON       /* form the status word */
                               | CST_CHAN (tci_cntr)
                               | cntl_param [tci_cntr] & CST_ESX_MASK
                               | cntl_status [tci_cntr] & CST_SX_MASK
                               | scan_status ();

            if (dibptr->interrupt_request == SET)       /* reflect the interrupt request value */
                tci_status_word |= CST_IRQ;             /*   in the status word */

            outbound_value = tci_status_word;           /* return the status word */

            tprintf (atcc_dev, DEB_CSRW, "Status is channel %u | %s\n",
                     tci_cntr, fmt_bitset (outbound_value, tci_status_format));
            break;


        case DSETINT:
            dibptr->interrupt_request = SET;            /* request an interrupt */

            if (tci_interrupt_mask)                     /* if the interrupt mask is satisfied */
                outbound_signals |= INTREQ;             /*   then assert the INTREQ signal */
            break;


        case DRESETINT:
            dibptr->interrupt_active = CLEAR;           /* reset the interrupt active flip-flop */
            break;


        case INTPOLLIN:
            if (dibptr->interrupt_request) {            /* if a request is pending */
                dibptr->interrupt_request = CLEAR;      /*   then clear it */
                dibptr->interrupt_active  = SET;        /*     and mark it now active */

                outbound_signals |= INTACK;             /* acknowledge the interrupt */
                outbound_value = dibptr->device_number; /*   and return our device number */
                }
            else                                        /* otherwise the request has been reset */
                outbound_signals |= INTPOLLOUT;         /*   so let the IOP know to cancel it */
            break;


        case DSETMASK:
            if (dibptr->interrupt_mask == INTMASK_E)                /* if the mask is always enabled */
                tci_interrupt_mask = SET;                           /*   then set the mask flip-flop */
            else                                                    /* otherwise */
                tci_interrupt_mask = D_FF (dibptr->interrupt_mask   /*   set the mask flip-flop if the mask bit */
                                           & inbound_value);        /*     is present in the mask value */

            if (tci_interrupt_mask && dibptr->interrupt_request)    /* if the mask is enabled and a request is pending */
                outbound_signals |= INTREQ;                         /*   then assert INTREQ */
            break;


        case DWRITESTB:                                 /* not used by this interface */
        case DSTARTIO:                                  /* not used by this interface */
        case XFERERROR:                                 /* not used by this interface */
        case ACKSR:                                     /* not used by this interface */
        case TOGGLESR:                                  /* not used by this interface */
        case TOGGLESIOOK:                               /* not used by this interface */
        case TOGGLEINXFER:                              /* not used by this interface */
        case TOGGLEOUTXFER:                             /* not used by this interface */
        case READNEXTWD:                                /* not used by this interface */
        case PREADSTB:                                  /* not used by this interface */
        case PWRITESTB:                                 /* not used by this interface */
        case PCMD1:                                     /* not used by this interface */
        case PCONTSTB:                                  /* not used by this interface */
        case PSTATSTB:                                  /* not used by this interface */
        case DEVNODB:                                   /* not used by this interface */
        case SETINT:                                    /* not used by this interface */
        case EOT:                                       /* not used by this interface */
        case SETJMP:                                    /* not used by this interface */
        case CHANSO:                                    /* not used by this interface */
        case PFWARN:                                    /* not used by this interface */
            break;
        }

    IOCLEARSIG (working_set, signal);                   /* remove the current signal from the set */
    }

tprintf (atcc_dev, DEB_IOB, "Returned data %06o with signals %s\n",
         outbound_value, fmt_bitset (outbound_signals, outbound_format));

return IORETURN (outbound_signals, outbound_value);     /* return the outbound signals and value */
}


/* Enable or disable the TDI.

   This validation routine is entered with "value" set to 1 for an ENABLE and 0
   for a DISABLE, and "cptr" pointing to the next character after the keyword.
   If the TDI is already enabled or disabled, respectively, the routine returns
   with no further action.  Otherwise, if "value" is 1, the device is enabled by
   clearing the DEV_DIS flag, and the polling flag is set TRUE to indicate that
   the TDI is polling for the simulation console.  If "value" is 0, a check is
   made to see if the TDI is listening for connections.  If it is, the disable
   request is rejected; the device must be detached first.  Otherwise, the
   device is disabled by setting the DEV_DIS flag, and the polling flag is set
   FALSE to indicate that the TDI is no longer polling for the simulation
   console (the PCLK device will take over when the polling flag is FALSE).

   In either case, the device is reset, which will restart or cancel the poll,
   as appropriate.
*/

static t_stat atc_set_endis (UNIT *uptr, int32 value, char *cptr, void *desc)
{
if (value)                                              /* if this is an ENABLE request */
    if (atcd_dev.flags & DEV_DIS) {                     /*   then if the device is disabled */
        atcd_dev.flags &= ~DEV_DIS;                     /*     then reenable it */
        atc_is_polling = TRUE;                          /*       and set the polling flag */
        }

    else                                                /*   otherwise the device is already enabled */
        return SCPE_OK;                                 /*     so there's nothing to do */

else                                                    /* otherwise this is a DISABLE request */
    if (atcd_dev.flags & DEV_DIS)                       /*   so if the device is already disabled */
        return SCPE_OK;                                 /*     so there's nothing to do */

    else if (poll_unit.flags & UNIT_ATT)                /*   otherwise if the poll unit is still attached */
        return SCPE_NOFNC;                              /*     then report that the command failed */

    else {                                              /*   otherwise */
        atcd_dev.flags |= DEV_DIS;                      /*     disable the device */
        atc_is_polling = FALSE;                         /*       and clear the polling flag */
        }

return atcd_reset (&atcd_dev);                          /* reset the TDI and restart or cancel polling */
}


/* Set the device modes.

   The device flag implied by the DEVICE_MODES "value" passed to the routine is
   set or cleared in the device specified by the "desc" parameter.  The unit and
   character pointers are not used.

   If the TDI is being put into diagnostic mode, all Telnet and serial
   connections are disconnected to avoid interference during the diagnostic run.


   Implementation notes:

    1. In hardware, terminals and modems must be disconnected from the ATC and
       loopback cables installed between each pair or channels when the
       diagnostic is run.  In simulation, setting DIAG mode detaches any
       existing listening port, so that Telnet sessions will not interfere with
       the internal loopback connections from the send to the receive channels.

    2. We fake a RESTORE to be able to detach the poll unit directly.  If this
       succeeds, then all Telnet connections have been dropped.  However, any
       existing serial connections are undisturbed, so we look for
       still-connected channels (which must be serial connections) and then
       detach them individually.
*/

static t_stat atc_set_mode (UNIT *uptr, int32 value, char *cptr, void *desc)
{
DEVICE * const dptr = (DEVICE *) desc;                  /* a pointer to the device */
uint32 channel;

switch ((DEVICE_MODES) value) {                         /* dispatch based on the mode to set */

    case Fast_Time:                                     /* entering optimized timing mode */
        dptr->flags &= ~DEV_REALTIME;                   /*   so clear the real-time flag */
        break;


    case Real_Time:                                     /* entering realistic timing mode */
        dptr->flags |= DEV_REALTIME;                    /*   so set the flag */
        break;


    case Terminal:                                      /* entering terminal mode */
        dptr->flags &= ~DEV_DIAG;                       /*   so clear the diagnostic flag */
        break;


    case Diagnostic:                                    /* entering the diagnostic mode */
        dptr->flags |= DEV_DIAG;                        /*   so set the flag */

        if (dptr == &atcd_dev) {                        /* if we're setting the TDI mode */
            sim_switches = SIM_SW_REST;                 /*   then detach the poll unit */
            atcd_detach (&poll_unit);                   /*     and any existing Telnet connections */

            for (channel = 0; channel < TERM_COUNT; channel++) {        /* detach any existing */
                tmxr_detach_line (&atcd_mdsc, &line_unit [channel]);    /*   serial connections */

                atcd_ldsc [channel].rcve = 0;           /* disable reception */
                sim_cancel (&line_unit [channel]);      /*   and cancel any transfer in progress */
                }
            }

        break;
    }

return SCPE_OK;
}


/* Show the device modes.

   The output stream and device pointer are passed in the "st" and "desc"
   parameters, respectively.  If "value" is 0, then all of the flags are checked
   for the TDI.  If "value" is 1, then only the diagnostic flag is checked for
   the TCI.  The unit pointer is not used.
*/

static t_stat atc_show_mode (FILE *st, UNIT *uptr, int32 value, void *desc)
{
const DEVICE * const dptr = (const DEVICE *) desc;      /* a pointer to the device */

if (value == 0)                                         /* if this is the TDI */
    if (dptr->flags & DEV_REALTIME)                     /*   then if the real-time flag is set */
        fputs ("realistic timing, ", st);               /*     then report that we are using realistic timing */
    else                                                /*   otherwise */
        fputs ("fast timing, ", st);                    /*     report that we are using optimized timing */

if (dptr->flags & DEV_DIAG)                             /* if the diagnostic flag is set */
    fputs ("diagnostic mode", st);                      /*   then report that we're in loopback mode */
else                                                    /* otherwise */
    fputs ("terminal mode", st);                        /*   we're in normal (terminal) mode */

return SCPE_OK;
}


/* Show the TDI device status.

   The attachment condition and connection count are printed to the stream
   specified by "st" as part of the ATCD device display.  The "desc" parameter
   is a pointer to the terminal multiplexer library descriptor; the unit pointer
   and value parameters are not used.
*/

static t_stat atc_show_status (FILE *st, UNIT *uptr, int32 value, void *desc)
{
if (poll_unit.flags & UNIT_ATT)                         /* if the poll unit is attached */
    fprintf (st, "attached to port %s, ",               /*   then report it */
             poll_unit.filename);                       /*     with the listening port number */
else                                                    /* otherwise */
    fprintf (st, "not attached, ");                     /*   report the condition */

tmxr_show_summ (st, uptr, value, desc);                 /* also report the count of connections */

return SCPE_OK;
}


/* TDI device reset.

   This routine is called for a RESET or RESET ATCD command.  It is the
   simulation equivalent of the IORESET signal, which is asserted by the front
   panel LOAD and DUMP switches.

   If a power-on reset (RESET -P) is being done, the poll timer is initialized.
   In addition, the original FASTTIME setting is restored, in case it's been
   changed by the user.

   If the polling flag is set, then start or resynchronize the poll unit with
   the process clock to enable idling.  If the CPU process clock is calibrated,
   then the poll event service is synchronized with the process clock service.
   Otherwise, the service time is set up but is otherwise asynchronous with the
   process clock.

   If the polling flag is clear, then the poll is stopped, as it's not needed.


   Implementation notes:

    1. To synchronize events, the poll must be activated absolutely, as a
       service event may already be scheduled, and normal activation will not
       disturb an existing event.
*/

static t_stat atcd_reset (DEVICE *dptr)
{
tdi_master_reset ();                                    /* perform a master reset */

if (sim_switches & SWMASK ('P')) {                      /* if this is a power-on reset */
    sim_rtcn_init (poll_unit.wait, TMR_ATC);            /*   then initialize the poll timer */
    fast_data_time = FAST_IO_TIME;                      /* restore the initial fast data time */
    atcd_ldsc [0].xmte = 1;                             /* enable transmission on the system console port */

    vm_console_input_unit  = &atcd_unit [0];            /* set up the console input */
    vm_console_output_unit = &atcd_unit [0];            /*   and console output unit pointers */
    }

if (atc_is_polling) {                                       /* if we're polling for the simulation console */
    if (cpu_is_calibrated)                                  /*   then if the process clock is calibrated */
        poll_unit.wait = sim_activate_time (cpu_pclk_uptr); /*     then synchronize with it */
    else                                                    /*   otherwise */
        poll_unit.wait = POLL_TIME;                         /*     set up an independent poll time */

    sim_activate_abs (&poll_unit, poll_unit.wait);          /* restart the poll timer */
    }

else                                                    /* otherwise */
    sim_cancel (&poll_unit);                            /*   cancel the poll */

return SCPE_OK;
}


/* TCI device reset.

   This routine is called for a RESET or RESET ATCC command.  It is the
   simulation equivalent of the IORESET signal, which is asserted by the front
   panel LOAD and DUMP switches.

   If a power-on reset (RESET -P) is being done, then the modem control flag is
   removed from each channel, and the multiplexer library is told to revert to
   internal control of the DTR and RTS lines.  This is necessary so that
   channels not controlled by the TCI will be able to connect (TCI-controlled
   channels will have their DTR and RTS state set by the MPE TCI initialization
   routine, which will also establish external control).
*/

static t_stat atcc_reset (DEVICE *dptr)
{
uint32 channel;

tci_master_reset ();                                        /* perform a master reset */

if (sim_switches & SWMASK ('P'))                            /* if this is a power-on reset */
    for (channel = 0; channel < TERM_COUNT; channel++) {    /*   then for each terminal channel */
        line_unit [channel].flags &= ~UNIT_MODEM;           /*     reset the modem control flag */

        tmxr_control_line (&atcd_ldsc [channel],            /* reset the channel */
                           Reset_Control);                  /*   to internal control */
        }

return SCPE_OK;
}


/* Attach the TDI to a Telnet listening port or serial port.

   This routine is called by the ATTACH ATCD <netport> command to attach the TDI
   to the listening port indicated by <netport> and by ATTACH ATCD<n> <serport>
   to attach line <n> to serial port <serport>.  For the former, it is logically
   the ATCD device that is attached; however, SIMH only allows units to be
   attached.  This makes sense for devices such as tape drives, where the
   attached media is a property of a specific drive.  In our case, though, the
   listening port is a property of the TDI card, not of any given serial line.
   As ATTACH ATCD is equivalent to ATTACH ATCD0, the port would, by default, be
   attached to the first channel and be reported there in a SHOW ATCD command.

   To preserve the logical picture, we attach the listening port to the poll
   unit (unit 16), which is normally disabled to inhibit its display.  Serial
   ports are attached to line units 1-15 normally; line 0 is dedicated to the
   system console, which is attached by default to the simulation console.
   Attachment is reported by the "atc_show_status" routine.

   Attaching is not permitted if the TDI is in diagnostic mode.  This prevents
   interference while the diagnostic is executing.


   Implementation notes:

    1. If we are being called as part of RESTORE processing, we may see a
       request to attach the poll unit (unit 16).  This will occur if the
       listening port was attached when the SAVE was done.  If the user attempts
       to attach unit 16 directly, the command will be rejected by SCP because
       the unit is disabled.

    2. If the poll unit is attached during a SAVE, it will be enabled as part of
       RESTORE processing.  We always unilaterally disable this unit to ensure
       that it remains hidden.

    3. ATTACH ATCD will pass a pointer unit 0.  This is because SCP treats
       ATTACH ATCD as equivalent to ATTACH ATCD0.  The "tmxr_attach_unit"
       routine differentiates these cases by examining the "sim_ref_type" global
       to see if a device was referenced and, if so, applying the request to the
       poll unit.
*/

static t_stat atcd_attach (UNIT *uptr, char *cptr)
{
t_stat status;

if (atcd_dev.flags & DEV_DIAG)                          /* if the multiplexer is in diagnostic mode */
    status = SCPE_NOFNC;                                /*   then the command is not allowed */
else                                                    /* otherwise */
    status = tmxr_attach_unit (&atcd_mdsc, &poll_unit,  /*   attempt to attach the designated unit */
                               uptr, cptr);

poll_unit.flags |= UNIT_DIS;                            /* ensure that the poll unit remains disabled */

return status;                                          /* return the result of the attach */
}


/* Detach the TDI.

   We are called by the DETACH ATCD command to detach the listening port and all
   Telnet sessions and by the DETACH ATCD<n> to detach a serial port from line
   <n>.  We will also be called by DETACH ALL, RESTORE, and during simulator
   shutdown.  For DETACH ALL and RESTORE, we must not fail the call, or
   processing of other units will cease.

   Detaching the listening port will disconnect all active Telnet connections
   but will not disturb any lines connected to serial ports.  Therefore, we look
   for newly freed lines and clean them up.


   Implementation notes:

    1. During simulator shutdown, we will be called for units 0-15 (detach_all
       in scp.c calls the detach routines of all units that do NOT have
       UNIT_ATTABLE), as well as for unit 16 if it is attached.

    2. A RESTORE command will call us for unit 16 if it is attached.  In this
       case, the terminal channels will have already been rescheduled as
       appropriate, so canceling them is skipped.

    3. Because DETACH ATCD will pass unit 0, the "tmxr_detach_unit" routine
       examines the "sim_ref_type" global to see if a device was referenced and,
       if so, applies the request to the poll unit.

    4. Detaching the listening port resets each line first, which flushes the
       output buffer.  This ensures that buffered data that has not been output
       via a poll_service entry before the DETACH command was issued is written
       before the line is disconnected.
*/

static t_stat atcd_detach (UNIT *uptr)
{
uint32 channel;
t_stat status;

status = tmxr_detach_unit (&atcd_mdsc, &poll_unit, uptr);   /* detach the specified unit */

if (status == SCPE_OK)                                  /* if the detach succeeded */
    for (channel = 0; channel < TERM_COUNT; channel++)  /*   then look for Telnet connections */
        if (tmxr_line_free (&atcd_ldsc [channel])) {    /* if the line is now free */
            atcd_ldsc [channel].rcve = 0;               /*   then disable reception */
            sim_cancel (&line_unit [channel]);          /*     and cancel any transfer in progress */
            }

return status;                                          /* return the status of the detach */
}



/* ATC local utility routines */



/* Request a TDI interrupt.

   The data flag and interrupt request flip-flops are set.  If the interrupt
   mask permits, the interrupt request is passed to the IOP.
*/

static void tdi_set_interrupt (void)
{
tdi_data_flag = SET;                                    /* set the data flag */

atcd_dib.interrupt_request = SET;                       /* request an interrupt */

if (tdi_interrupt_mask)                                 /* if the interrupt mask is satisfied */
    iop_assert_INTREQ (&atcd_dib);                      /*   then assert the INTREQ signal to the IOP */

return;
}


/* TDI master reset.

   A master reset is generated either by an IORESET signal or a programmed
   master reset (CIO bit 0 set).  It clears any pending or active interrupt,
   sets the interrupt mask, clears the status word and data flag, and resets all
   channels to their initial, unconfigured state.


   Implementation notes:

    1. In hardware, a master reset sets the Initialize flip-flop.  This causes a
       direct clear of the recirculating memory window registers, thereby
       clearing each channel's buffer, parameter, and status values as they
       pass through the window.  The flip-flop is cleared when a control word is
       sent with the master clear bit (CIO bit 0) cleared.  A full recirculation
       takes 69.44 microseconds, so the CPU must allow at least this time for
       each channel to pass through the window to ensure that all memory
       locations are reset.  In simulation, the clear occurs "instantaneously."
*/

static void tdi_master_reset (void)
{
uint32 chan;

atcd_dib.interrupt_request = CLEAR;                     /* clear any current */
atcd_dib.interrupt_active  = CLEAR;                     /*   interrupt request */

tdi_interrupt_mask = SET;                               /* set the interrupt mask */

tdi_status_word = 0;                                    /* clear the status word */
tdi_data_flag   = CLEAR;                                /*   and the data flag */

for (chan = FIRST_TERM; chan <= LAST_TERM; chan++) {    /* for each terminal channel */
    recv_buffer [chan] = 0;                             /*   clear the receive data buffer */
    recv_param  [chan] = 0;                             /*     and parameter */
    recv_status [chan] = 0;                             /*       and status */

    send_buffer [chan] = 0;                             /* also clear the send data buffer */
    send_param  [chan] = 0;                             /*   and parameter */
    send_status [chan] = 0;                             /*     and status */

    sim_cancel (&line_unit [chan]);                     /* cancel any transfer in progress */
    }

for (chan = FIRST_AUX; chan <= LAST_AUX; chan++) {      /* for each auxiliary channel */
    recv_buffer [chan] = 0;                             /*   clear the receive data buffer */
    recv_param  [chan] = 0;                             /*     and parameter */
    recv_status [chan] = 0;                             /*       and status */
    }

return;
}


/* TCI master reset.

   A master reset is generated either by an IORESET signal or a programmed
   master reset (CIO bit 0 set).  It clears any pending or active interrupt,
   sets the interrupt mask, clears the control word and channel counter, and
   resets all channels to their initial, unconfigured state.


   Implementation notes:

    1. In hardware, a master reset sets the Status Clear flip-flop.  This causes
       a direct clear of the Control Word Holding Register and enables writing
       into each location of the addressable latches and state RAM.  The
       flip-flop is reset automatically when the channel counter rolls over.
       This takes approximately 12 microseconds, so the CPU must allow at least
       this time before sending new control information.  In simulation, the
       master reset occurs "instantaneously."

    2. In hardware, the C2 and C1 control line outputs are cleared by a master
       clear.  In simulation, we also clear the S2 and S1 status line input
       values.  This is OK, because they will be reestablished at the next poll
       service entry.
*/


static void tci_master_reset (void)
{
uint32 chan;

atcc_dib.interrupt_request = CLEAR;                     /* clear any current */
atcc_dib.interrupt_active  = CLEAR;                     /*   interrupt request */

tci_interrupt_mask = SET;                               /* set the interrupt mask */

tci_control_word = 0;                                   /* clear the control word */
tci_cntr = 0;                                           /*   and the channel counter */

for (chan = FIRST_TERM; chan <= LAST_TERM; chan++) {    /* for each terminal channel */
    cntl_status [chan] = 0;                             /*   clear all serial line values */
    cntl_param  [chan] = 0;                             /*     and the parameter RAM */
    }

return;
}


/* Multiplexer channel service.

   The channel service routine runs only when there are characters to read or
   write.  It is scheduled either at a realistic rate corresponding to the
   programmed baud rate of the channel to be serviced, or at a somewhat faster
   optimized rate.  It is entered when a channel buffer is ready for output,
   when the poll routine determines that there are characters ready for input,
   or while waiting for an ACK to complete an ENQ/ACK handshake.

   On entry, the receive channel buffer is checked for a character.  If one is
   not already present, then the terminal multiplexer library is called to
   retrieve the waiting character.  If a valid character is now available, it is
   processed.  If the receive channel has its "diagnose" bit set, the character
   is also passed to the auxiliary channels.

   If a received character is not available, then the unit's "wait" field is
   checked to see if an ACK is expected in reply to an earlier ENQ.  If a wait
   time is present, it is doubled, and the service is rescheduled.  However, if
   the new wait time is longer than the current poll time, service rescheduling
   is abandoned in favor of the normal poll for received characters.

   The send channel buffer is then checked for a character to output.  If one is
   present, then if it is an all-mark (sync) character, it is discarded, as the
   receiver would never see it.  Otherwise, if the TDI is in diagnostic mode,
   then the character is looped back to the associated receive channel by
   storing it in that channel's receive buffer and then recursively calling the
   routine for that channel.

   If the TDI is in terminal mode, then if the channel flag is set for local
   ENQ/ACK handshaking, and the character is an ENQ, it is discarded, an ACK is
   stored in the channel's receive buffer, and its reception is scheduled.
   Otherwise, the character is processed and then transmitted either to the
   simulation console (if output is to channel 0) or to the terminal multiplexer
   library for output via Telnet or a serial port on the host machine.  If the
   channel has its "diagnose" bit set, the character is also passed to the
   auxiliary channels.  If an ENQ was transmitted, service is rescheduled to
   wait for reception of the ACK.

   If the data flag is clear, the indicated receive and send channels are
   checked for completion flags.  If either is set, an interrupt is requested.

   If characters remain in the Telnet receive buffer, the service routine is
   rescheduled to receive the next one.  Otherwise, the routine goes idle until
   the next character is output or the next poll determines that there are
   characters to receive.


   Implementation notes:

    1. The "wait" fields of the channel units are not used (i.e., are set to
       zero) except when channels are waiting for ACKs.  Because we want to
       retrieve the ACK as quickly as possible but also minimize the load on the
       host system, we initially wait the normal reception time (fast or
       realistic) and then double the wait each time it expires without
       reception.  Therefore, a non-zero "wait" value indicates that an ACK is
       expected.

    2. Receipt of any character cancels a pending ACK wait, even though it is
       possible that the character is not an ACK (for example, if the character
       was received but not processed before the ENQ was sent).

    3. Calling "tmxr_getc_ln" for channel 0 is OK, as reception is disabled by
       default and therefore will return 0.

    4. The send channel buffer will always be non-zero if a character is present
       (even a NUL) because the data word will have DDS_IS_SEND set.

       The receive buffer will always be non-zero if a character is present
       (even a NUL) because characters from the console will have SCPE_KFLAG
       set, characters from the terminal multiplexer library will have
       TMXR_VALID set, and characters looped back from sending will have
       DDS_IS_SEND set.

    5. Reception of a loopback character is performed immediately because the
       reception occurs concurrently with transmission.  Reception of a locally
       generated ACK is scheduled with a one-character delay to reflect the
       remote device transmission delay.

    6. If storing an ACK locally overwrites a character already present but not
       yet processed, then the receive routine will set the character lost flag.

    7. Both TMXR_VALID and SCPE_KFLAG are set on internally generated ACKs only
       so that a debug trace will record the generation correctly.

    8. The terminal multiplexer library "tmxr_putc_ln" routine returns
       SCPE_STALL if it is called when the transmit buffer is full.  However,
       when it is called to add the last character to the buffer, the routine
       returns SCPE_OK but changes the "xmte" field of the terminal multiplexer
       line (TMLN) structure from 1 to 0 to indicate that further calls will be
       rejected; the value is set back to 1 when the transmit buffer empties.

       This presents two approaches to handling buffer overflows: either call
       "tmxr_putc_ln" unconditionally and test for SCPE_STALL on return, or call
       "tmxr_putc_ln" only if "xmte" is 1.  The former approach adds a new
       character to the transmit buffer as soon as space is available, while the
       latter adds a new character only when the buffer has completely emptied.
       With either approach, transmission must be rescheduled after a delay to
       allow the buffer to drain.

       It would seem that the former approach is more attractive, as it would
       allow the simulated I/O operation to complete more quickly.  However,
       there are two mitigating factors.  First, the library attempts to write
       the entire transmit buffer in one host system call, so there is usually
       no time difference between freeing one buffer character and freeing the
       entire buffer (barring host system buffer congestion).  Second, the
       routine increments a "character dropped" counter when returning
       SCPE_STALL status.  However, the characters actually would not be lost,
       as the SCPE_STALL return would schedule retransmission when buffer space
       is available, .  This would lead to erroneous reporting in the SHOW
       <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.

       The console library "sim_putchar_s" routine can also return SCPE_STALL.
       However, there is no TMLN structure involved when the system console is
       connected to the simulation console.  If the system console is redirected
       to a Telnet connection with a SET CONSOLE TELNET command, the associated
       TMLN line is internal to the "sim_console" module.  In neither case is
       the "xmte" field is available for examination.

       For console stalls, then, the only way to detect a stall is to call the
       routine and check the return status.

       Both library "put" routines also return SCPE_LOST if the line has been
       dropped on the remote end.  We ignore that error here to allow the
       simulation to continue while ignoring the output to a disconnected
       terminal.

    9. Characters written using "tmxr_putc_ln" are buffered and not transmitted
       until the buffer is full, the character is an ENQ (and so we will be
       waiting to receive an ACK), or the next input poll is performed.  The
       last case ensures that the buffer is flushed when output is complete, as
       there is no indication from the CPU that the last character has been
       sent.  This does incur a delay of up to 10 milliseconds, but this is
       imperceptible by the user.  Buffering offers significantly better
       throughput compared to transmitting each character as it is written.

   10. The receive/send completion flag (buffer flag) will not set unless the
       interrupt enable flag for that channel is also set.  If enable is not
       set, the completion indication will be lost.
*/

static t_stat line_service (UNIT *uptr)
{
const  int32 channel = (int32) (uptr - line_unit);          /* the channel number */
const  int32 alt_channel = channel ^ 1;                     /* alternate channel number for diagnostic mode */
const  t_bool loopback = (atcd_dev.flags & DEV_DIAG) != 0;  /* TRUE if device is set for diagnostic mode */
int32  recv_data, send_data, char_data, cvtd_data;
t_stat result = SCPE_OK;

tprintf (atcd_dev, DEB_SERV, "Channel %d service entered\n",
         channel);


/* Reception service */

recv_data = recv_buffer [channel];                      /* get the current buffer character */

if (recv_data == 0) {                                   /* if no character is present */
    if (uptr->wait)                                     /*   then if the channel is waiting for an ACK */
        tmxr_poll_rx (&atcd_mdsc);                      /*     then poll the line to see if it has arrived */

    recv_data = tmxr_getc_ln (&atcd_ldsc [channel]);    /* see if there's now a character ready */
    }

if (recv_data & ~DDR_DATA_MASK) {                       /* if we now have a valid character */
    receive (channel, recv_data, loopback);             /*   then process the reception */

    if (recv_param [channel] & DPI_DIAGNOSE)            /* if a diagnosis is requested */
        diagnose (recv_param [channel], recv_data);     /*   then route the data to the auxiliary channels */

    uptr->wait = 0;                                     /* clear any pending ACK wait */
    }

else if (uptr->wait) {                                  /* otherwise if an ACK is expected but has not arrived */
    uptr->wait = uptr-> wait * 2;                       /*   then double the wait time for the next check */

    if (uptr->wait < poll_unit.wait) {                  /* if the wait is shorter than the standard poll wait */
        sim_activate (uptr, uptr->wait);                /*   then reschedule the line service */

        tprintf (atcd_dev, DEB_SERV, "Channel %d delay %d service rescheduled for ACK\n",
                 channel, uptr->wait);
        }
    }


/* Transmission service */

if (send_buffer [channel]) {                                /* if data is available to send */
    send_data = DDS_DATA (send_buffer [channel]);           /*   then pick up the data and stop bits */
    char_data = send_data & ASCII_MASK;                     /*     and also the ASCII character value */

    if (send_status [channel] & DST_COMPLETE) {             /* if the last completion hasn't been acknowledged */
        send_status [channel] |= DST_CHAR_LOST;             /*   then indicate an overrun condition */

        tprintf (atcd_dev, DEB_CSRW, "Channel %d send data overrun\n",
                 channel);
        }

    if ((send_buffer [channel] & DDS_MARK) == DDS_MARK) {   /* if it's an all-mark character */
        send_buffer [channel] = 0;                          /*   then the receiver won't see it */

        if (send_param [channel] & DPI_ENABLE_IRQ)          /* if this channel is enabled to interrupt */
            send_status [channel] |= DST_COMPLETE;          /*   then set the completion flag */

        tprintf (atcd_dev, DEB_XFER, (loopback
                                       ? "Channel %d sync character sent to channel %d\n"
                                       : "Channel %d sync character sent\n"),
                 channel, alt_channel);
        }

    else if (loopback) {                                    /* otherwise if the device is in loopback mode */
        if (send_param [channel] & DPI_DIAGNOSE)            /*   then if a diagnosis is requested */
            diagnose (send_param [channel], send_data);     /*     then route the data to the auxiliary channels */

        if ((send_buffer [channel] & DDR_DATA_MASK) == 0)       /* if all bits are clear */
            recv_buffer [alt_channel] = SCPE_BREAK;             /*   then it will be seen as a BREAK */
        else                                                    /* otherwise a character will be received */
            recv_buffer [alt_channel] = send_buffer [channel];  /*   so store it in the buffer */

        send_buffer [channel] = 0;                          /* clear the send buffer */

        if (send_param [channel] & DPI_ENABLE_IRQ)          /* if this channel is enabled to interrupt */
            send_status [channel] |= DST_COMPLETE;          /*   then set the completion flag */

        tprintf (atcd_dev, DEB_XFER, "Channel %d character %s sent to channel %d\n",
                 channel, fmt_char (char_data), alt_channel);

        line_service (&line_unit [alt_channel]);            /* receive the character on the alternate channel */
        }

    else if (char_data == ENQ && uptr->flags & UNIT_LOCALACK) { /* otherwise if it's an ENQ and local reply is enabled */
        recv_buffer [channel] = GEN_ACK;                        /*   then "receive" an ACK on the channel */

        send_buffer [channel] = 0;                              /* discard the ENQ */

        if (send_param [channel] & DPI_ENABLE_IRQ)              /* if this channel is enabled to interrupt */
            send_status [channel] |= DST_COMPLETE;              /*   then set the completion flag */

        tprintf (atcd_dev, DEB_XFER, "Channel %d character ENQ absorbed internally\n",
                 channel);

        activate_unit (uptr, Receive);                          /* schedule the reception */
        }

    else {                                                      /* otherwise it's a normal character */
        cvtd_data = sim_tt_outcvt (LOWER_BYTE (send_data),      /*   so convert it as directed */
                                   TT_GET_MODE (uptr->flags));  /*     by the output mode flag */

        if (cvtd_data >= 0 && atcd_ldsc [channel].xmte == 0) {  /* if it's printable but the transmit buffer is full */
            activate_unit (uptr, Stall);                        /*   then retry the output a while later */

            tmxr_poll_tx (&atcd_mdsc);                          /* transmit the line buffer */

            tprintf (atcd_dev, DEB_XFER, "Channel %d character %s transmission stalled for full buffer\n",
                     channel, fmt_char (cvtd_data));
            }

        else {                                                  /* otherwise the character will be consumed */
            if (cvtd_data >= 0)                                 /* if the converted character is printable */
                if (channel == 0)                               /*   then if we are writing to channel 0 */
                    result = sim_putchar_s (cvtd_data);         /*     then output it to the simulation console */

                else {                                              /*   otherwise */
                    result = tmxr_putc_ln (&atcd_ldsc [channel],    /*     output it to the multiplexer line */
                                           cvtd_data);

                    if (char_data == ENQ                        /* if sending an ENQ */
                      || atcd_ldsc [channel].xmte == 0)         /*   or the output buffer is full */
                        tmxr_poll_tx (&atcd_mdsc);              /*     then transmit the line buffer */
                    }

            if (result == SCPE_OK || result == SCPE_LOST) {     /* if the character is queued to transmit */
                if (TRACING (atcd_dev, DEB_XFER))
                    if (result == SCPE_LOST)
                        hp_trace (&atcd_dev, DEB_XFER, "Channel %d character %s discarded by connection loss\n",
                                  channel, fmt_char (char_data));

                    else if (cvtd_data >= 0)
                        hp_trace (&atcd_dev, DEB_XFER, "Channel %d character %s sent\n",
                                  channel, fmt_char (cvtd_data));

                    else
                        hp_trace (&atcd_dev, DEB_XFER, "Channel %d character %s discarded by output filter\n",
                                  channel, fmt_char (char_data));

                if (send_param [channel] & DPI_DIAGNOSE)        /* if a diagnosis is requested */
                    diagnose (send_param [channel], send_data); /*   then route the data to the auxiliary channels */

                send_buffer [channel] = 0;                      /* clear the buffer */

                if (send_param [channel] & DPI_ENABLE_IRQ)      /* if this channel is enabled to interrupt */
                    send_status [channel] |= DST_COMPLETE;      /*   then set the completion flag */

                if (cvtd_data == ENQ && result == SCPE_OK)      /* if an ENQ was successfully sent */
                    uptr->wait = activate_unit (uptr, Receive); /*   then schedule the ACK reception */

                result = SCPE_OK;                               /* return OK in case the connection was lost */
                }

            else if (result == SCPE_STALL) {                    /* otherwise if a console stall occurred */
                activate_unit (uptr, Stall);                    /*   then retry the output a while later */
                result = SCPE_OK;                               /*     and return OK to continue execution */

                tprintf (atcd_dev, DEB_XFER, "Channel %d character %s console transmission stalled\n",
                         channel, fmt_char (cvtd_data));
                }
            }
        }
    }


if (tdi_data_flag == CLEAR)                             /* if an interrupt is not currently pending */
    scan_channels (channel);                            /*   then scan the channels for completion flags */

if (tmxr_rqln (&atcd_ldsc [channel]))                   /* if characters are still available on this channel */
    activate_unit (uptr, Receive);                      /*   then reschedule the line service */

return result;                                          /* return the result of the service */
}


/* Multiplexer poll service.

   The poll service routine is used to poll for Telnet connections and incoming
   characters.  It also polls the simulation console for channel 0 and flushes
   the output buffers for all channels.  Polling starts at simulator startup or
   when the TDI is enabled and stops when it is disabled.

   Implementation notes:

    1. The poll service routine may be entered with the TCI either enabled or
       disabled.  It will not be entered if the TDI is disabled, as it may be
       disabled only when it is detached from a listening port.

    2. "Channel connected" trace messages are logged to the TCI, so that they
       can be paired with "Channel disconnected" messages that occur when the
       TCI drops DTR.

    3. If a character is received on the simulation console, we must call the
       channel 0 line service directly.  This is necessary because the poll time
       may be shorter than the channel service time, and as the console provides
       no buffering, a second character received before the channel service had
       been entered would be lost.

    4. A channel that is waiting for an ACK to complete an ENQ/ACK handshake has
       its unit "wait" field set non-zero.  If the field value is greater than
       the realistic reception time, then the line service is scheduled
       immediately, as the channel has already waited the minimum time necessary
       (the "wait" field value is doubled each time the line service is entered
       before the ACK has been received).  Otherwise, the service is scheduled
       using the normal reception time.
*/

static t_stat poll_service (UNIT *uptr)
{
int32  connection;
uint32 chan;
t_stat status = SCPE_OK;

tprintf (atcd_dev, DEB_PSERV, "Poll delay %d service entered\n",
         uptr->wait);

if ((atcc_dev.flags & DEV_DIS) == 0)
    tprintf (atcc_dev, DEB_PSERV, "Poll delay %d service entered\n",
             uptr->wait);

if ((atcd_dev.flags & DEV_DIAG) == 0) {                 /* if we're not in diagnostic mode */
    connection = tmxr_poll_conn (&atcd_mdsc);           /*   then check for a new multiplexer connection */

    if (connection != -1) {                             /* if a new connection was established */
        atcd_ldsc [connection].rcve = 1;                /*   then enable the channel to receive */

        tprintf (atcc_dev, DEB_XFER, "Channel %d connected\n",
                 connection);
        }
    }

tmxr_poll_tx (&atcd_mdsc);                              /* flush the multiplexer output buffers */
tmxr_poll_rx (&atcd_mdsc);                              /*   and poll the multiplexer connections for input */

if ((atcc_dev.flags & (DEV_DIAG | DEV_DIS)) == 0)       /* if we're not in diagnostic mode or are disabled */
    for (chan = FIRST_TERM; chan <= LAST_TERM; chan++)  /*   then scan the channels for line state changes */
        if (line_unit [chan].flags & UNIT_MODEM)        /* if the channel is controlled by the TCI */
            update_status (chan);                       /*   then update the current line state */

status = sim_poll_kbd ();                               /* poll the simulation console keyboard for input */

if (status >= SCPE_KFLAG) {                             /* if a character was present */
    recv_buffer [0] = (HP_WORD) status;                 /*   then save it for processing */
    status = SCPE_OK;                                   /*     and then clear the status */

    line_service (&line_unit [0]);                      /* run the system console's I/O service */
    }

for (chan = FIRST_TERM; chan <= LAST_TERM; chan++)                  /* check each of the channels for available input */
    if (tmxr_rqln (&atcd_ldsc [chan]))                              /* if characters are available on this channel */
        if (line_unit [chan].wait > line_unit [chan].recv_time) {   /*   then if the channel is waiting for an ACK */
            sim_cancel (&line_unit [chan]);                         /*     then cancel any current wait */
            activate_unit (&line_unit [chan], Loop);                /*       and activate the line service immediately */
            }

        else                                                        /*   otherwise this is a normal input */
            activate_unit (&line_unit [chan], Receive);             /*     so schedule with the normal receive timing */

if (cpu_is_calibrated)                                  /* if the process clock is calibrated */
    uptr->wait = sim_activate_time (cpu_pclk_uptr);     /*   then synchronize with it */
else                                                    /* otherwise */
    uptr->wait = sim_rtcn_calb (POLL_RATE, TMR_ATC);    /*   calibrate the poll timer independently */

sim_activate (uptr, uptr->wait);                        /* continue polling */

if (tci_scan)                                           /* if scanning is active */
    scan_status ();                                     /*   then check for line status changes */

return status;                                          /* return the service status */
}


/* Activate a channel unit.

   The specified unit is activated to receive or send a character.  The reason
   for the activation is specified by the "reason" parameter.  If the TDI is in
   real-time mode, the previously calculated service time is used to schedule
   the event.  Otherwise, the current value of the optimized timing delay is
   used.  If tracing is enabled, the activation is logged to the debug file.


   Implementation notes:

    1. The loopback time is the difference between the reception and
       transmission times, as the latter event has already occurred when we are
       called.

    2. A stall is rescheduled to synchronize with the poll timer.  This ensures
       that idling is permitted even when a port is stalled for an extended
       length of time (such as when the simulation console is waiting for input
       in concurrent mode).
*/

static int32 activate_unit (UNIT *uptr, ACTIVATOR reason)
{
const int32 channel = (int32) (uptr - line_unit);       /* the channel number */
int32 delay = 0;

if (atcd_dev.flags & (DEV_DIAG | DEV_REALTIME))         /* if either diagnostic or real-time mode is set */
    switch (reason) {                                   /*   then dispatch the REALTIME activation */

        case Receive:                                   /* reception event */
            delay = uptr->recv_time;                    /* schedule for the realistic reception time */
            break;

        case Send:                                      /* transmission event */
            delay = uptr->send_time;                    /* schedule for the realistic transmission time */
            break;

        case Loop:                                      /* diagnostic loopback reception event */
            delay = uptr->recv_time - uptr->send_time;  /* schedule the additional reception overhead */

            if (delay < 0)                              /* if the receive time is less than the send time */
                delay = 0;                              /*   then schedule the reception immediately */
            break;

        case Stall:                                     /* transmission stall event */
            delay = sim_activate_time (&poll_unit);     /* use the remaining poll time as the wait */
            break;
        }

else                                                    /* otherwise, we are in optimized timing mode */
    switch (reason) {                                   /*   so dispatch the FASTTIME activation */

        case Receive:                                   /* reception event */
        case Send:                                      /* transmission event */
            delay = fast_data_time;                     /* use the optimized timing value */
            break;

        case Loop:                                      /* diagnostic loopback reception event */
            delay = 1;                                  /* use a nominal delay */
            break;

        case Stall:                                     /* transmission stall event */
            delay = sim_activate_time (&poll_unit);     /* use the remaining poll time as the wait */
            break;
        }

tprintf (atcd_dev, DEB_SERV, "Channel %d delay %d service scheduled\n",
         channel, delay);

sim_activate (uptr, delay);                             /* activate the unit */
return delay;                                           /*   and return the activation delay */
}


/* Calculate the service time.

   The realistic channel service time in event ticks per character is calculated
   from the encoded character size and baud rate in the supplied control word.
   The time consists of the transfer time plus a small overhead, which is
   different for receiving and sending.

   The character size field in the control word is generated by this equation:

     encoded_size = (bits_per_character - 1) AND 7

   That is, the encoded character size is the value expressed by the three
   least-significant bits of the count of the data and stop bits.  Therefore,
   the actual number of bits per character (including the start bit) is encoded
   as:

     Actual  Encoded
     ------  -------
        5       4
        6       5
        7       6
        8       7
        9       0
       10       1
       11       2
       12       3

   The baud rate field in the control word is generated by this equation:

                      14400
     encoded_rate = --------- - 1
                    baud_rate

   The transmission and overhead times are related to the recirculation of the
   multiplexer's internal memory, which contains the data, parameters, and
   status for each of the 16 send channels, 16 receive channels, and 5 auxiliary
   channels.  Data for a given channel can be accessed only once per
   recirculation, which takes 69.44 microseconds (1/14400 of a second).  The
   encoded rate plus one gives the number of recirculations corresponding to a
   single bit time; multiplying by the number of bits per character gives the
   number of recirculations to send or receive an entire character.

   All operations encounter two overhead delays.  First, an average of one-half
   of a recirculation must occur to align the memory with the channel of
   interest.  Second, a full recirculation is required after receiving or
   sending is complete before an interrupt may be generated.

   For receiving, there is an additional delay to right-justify the received
   character in the data accumulator.  The accumulator is a 12-bit shift
   register, with received data bits are shifted from left to right.  When the
   final bit is entered, the register must be shifted additionally until the
   first data bit is in the LSB (i.e., until the start bit is shifted out of the
   register).  One shift per recirculation is performed, and the number of
   additional shifts required is 12 + 1 - the number of bits per character.

   Justification begins immediately after the stop bit has been received, so the
   full set of recirculations for that bit are skipped in lieu of justification.
   Also, reception of the start bit is delayed by one-half of the bit time to
   improve noise immunity.

   Therefore, given R = encoded_rate + 1 and B = bits_per_character, the number
   of memory recirculations required for sending is:

        0.5  to align memory with the target channel (on average)
     + R * B to send the start, data, and stop bits
     +   1   to set the data flag to request an interrupt

   For example, at 2400 baud (encoded rate 5), a 10-bit character size, and
   69.44 microseconds per circulation, the service time would be:

       34.7 usec to align
     4166.7 usec to send the start, data, and stop bits
       69.4 usec to set the data flag
     ===========
     4270.8 usec from initiation to data flag

  The number of memory recirculations required for receiving is:

           0.5      to align memory with the target channel (on average)
     +    R / 2     to receive the start bit
     + R * (B - 1)  to receive the data and stop bits
     + (12 - B + 1) to right-justify the data
     +      1       to set the data flag to request an interrupt

   Using the same example as above, the service time would be:

       34.7 usec to align
      208.3 usec to receive the start bit
     3750.0 usec to receive the data and stop bits
      208.3 usec to right-justify the data
       69.4 usec to set the data flag
     ===========
     4270.7 usec from initiation to data flag


   Implementation notes:

    1. The multiplexer uses an 8-bit field to set the baud rate.  In practice,
       only the common rates (110, 150, 300, 600, 1200, 2400) will be used.
       Still, the real-time calculation must accommodate any valid setting, so a
       lookup table is infeasible.

    2. The receive calculation is simplified by noting that R / 2 + R * (B - 1)
       is equivalent to R * B - R / 2, so the send calculation may be reused.
       Note that the receive time may be less than the send time, typically when
       the baud rate is low, so that the time to send the stop bits is longer
       than the time to right-justify the reception.  This means that the
       "addition" of the receive overhead may actually be a subtraction.
*/

static uint32 service_time (HP_WORD control, ACTIVATOR reason)
{
const  double recirc_time = 69.44;                                  /* microseconds per memory recirculation */
const  uint32 recirc_per_bit = DPI_BAUD_RATE (control) + 1;         /* number of memory recirculations per bit */
const  uint32 char_size = bits_per_char [DPI_CHAR_SIZE (control)];  /* number of bits per character */
double usec_per_char;

usec_per_char = recirc_time *                           /* calculate the overhead for sending */
                  (char_size * recirc_per_bit + 1.5);

if (reason == Receive)                                  /* if we're receiving */
    usec_per_char += recirc_time *                      /*   then add the additional receiving overhead */
                       (12 - char_size + 1
                        - recirc_per_bit / 2.0);

return (uint32) (usec_per_char / USEC_PER_EVENT);       /* return the service time for indicated rate */
}


/* Store a word in the recirculating memory.

   A parameter or data word is stored in the recirculating memory for the
   channel indicated by the associated field of the "control" parameter.  If the
   channel number is out of range, the store is ignored.

   For receive and send parameters, the realistic service time is calculated and
   stored in the unit for use when a receive or send event is scheduled.  For
   send data, parity is calculated and added if specified by the channel's
   parameter, and the transmission event is scheduled.  For a receive parameter,
   the pad bits that would normally be added during right-justification after
   reception are calculated and stored in the unit.


   Implementation notes:

    1. Service times are not calculated or set for auxiliary channels because
       events are not scheduled on them (and so no units are allocated for
       them).

    2. Pad bits begin with the stop bit and continue until the character is
       right-justified in the receive buffer.  The calculation assumes one stop
       bit, but there is no way of ascertaining the actual number of stop bits
       from the parameter word.
*/

static void store (HP_WORD control, HP_WORD data)
{
const uint32 channel = DCN_CHAN (control);              /* current channel number */

if (data & DDS_IS_SEND)                                 /* if this is a send parameter or data */
    if (channel > LAST_TERM)                            /*   then report if the channel number is out of range */
        tprintf (atcd_dev, DEB_CSRW, "Send channel %u invalid\n",
                 channel);

    else if (data & DPI_IS_PARAM) {                     /* otherwise if this is a parameter store */
        send_param [channel] = data;                    /*   then save it */
        line_unit [channel].send_time =                 /*    and set the service time */
          service_time (data, Send);

        tprintf (atcd_dev, DEB_CSRW, "Channel %u send parameter %06o stored\n",
                 channel, data);
        }

    else {                                              /* otherwise this is a data store */
        if (send_param [channel] & DPI_ENABLE_PARITY)   /* if parity is enabled */
            data = data & ~DDS_PARITY                   /*   then replace the parity bit */
                     | SEND_PARITY (data);              /*     with the calculated value */

        send_buffer [channel] = data;                   /* store it in the buffer */

        tprintf (atcd_dev, DEB_CSRW, "Channel %u send data %06o stored\n",
                 channel, data);

        activate_unit (&line_unit [channel], Send);     /* schedule the transmission event */
        }

else                                                    /* otherwise this is a receive parameter */
    if (channel >= RECV_CHAN_COUNT)                     /* report if the channel number is out of range */
        tprintf (atcd_dev, DEB_CSRW, "Receive channel %u invalid\n",
                 channel);

    else if (data & DPI_IS_PARAM) {                     /* otherwise if this is a parameter store */
        recv_param [channel] = data;                    /*   then save it */

        if (channel <= LAST_TERM) {                     /* if this is a terminal channel */
            line_unit [channel].recv_time =             /*   and not an auxiliary channel */
              service_time (data, Receive);             /*     then set the service time */

            line_unit [channel].stop_bits =             /* set the stop bits mask for reception */
              PAD_BITS (data);
            }

        tprintf (atcd_dev, DEB_CSRW, "Channel %u receive parameter %06o stored\n",
                 channel, data);
        }

    else                                                /* otherwise a data store to a receive channel is invalid */
        tprintf (atcd_dev, DEB_CSRW, "Channel %u receive output data word %06o invalid\n",
                 channel, data);
}


/* Process a character received from a channel.

   This routine is called to process received data on a channel, typically when
   a character exists in the channel's receive buffer, but also when a character
   is received on an auxiliary channel.  The "channel" parameter indicates the
   channel on which reception occurred, "data" is the (full) character data
   as received from the console or terminal multiplexer libraries, and
   "loopback" is TRUE if the data should be looped back to the alternate channel
   for diagnostic execution.

   On entry, the bits required to pad the character are obtained.  If a BREAK
   was detected, then break status is set, and the character is set to NUL,
   reflecting the all-space reception.  Otherwise, if a character is already
   present in the receive buffer, "character lost" status is set to indicate
   that it will be overwritten.

   If this is a loopback reception, and echo is enabled on the channel, the
   character is sent back to the alternate channel.  Otherwise, if this is a
   main and not auxiliary channel reception, the character is upshifted if the
   UNIT_CAPSLOCK flag is set.  If echo is enabled, the character is written back
   to the console or terminal multiplexer library line.  Finally, the completion
   flag is set if enabled.


   Implementation notes:

    1. The echo to a terminal multiplexer library line will return SCPE_LOST if
       the line has been dropped on the remote end.  We can ignore the error
       here, as the line drop will be picked up when the next input poll is
       performed.

       In addition, the SCPE_STALL returned for a full output buffer is also
       ignored, as there's no way of queuing echoed characters while waiting for
       the buffer to empty.
*/

static void receive (int32 channel, int32 data, t_bool loopback)
{
int32 recv_data, char_data, char_echo, pad;

recv_data = data & DDR_DATA_MASK;                       /* mask to just the character data */
char_data = recv_data & ASCII_MASK;                     /*   and to the equivalent ASCII character */

if (channel <= LAST_TERM)                               /* if this is a receive channel */
    pad = line_unit [channel].stop_bits;                /*   then set the stop-bit padding from the unit */
else                                                    /* otherwise it's an auxiliary channel */
    pad = PAD_BITS (recv_param [channel]);              /*   so calculate the padding */


if (data & SCPE_BREAK) {                                /* if a break was detected */
    recv_buffer [channel] = NUL;                        /*   then return a NUL character */
    recv_status [channel] |= DST_BREAK;                 /*     and set break reception status */

    tprintf (atcd_dev, DEB_XFER, "Channel %d break detected\n",
             channel);
    }

else {                                                  /* otherwise a normal character was received */
    if (recv_status [channel] & DST_COMPLETE) {         /* if a character is already pending */
        recv_status [channel] |= DST_CHAR_LOST;         /*   then the previous character will be lost */

        tprintf (atcd_dev, DEB_CSRW, "Channel %d receive data overrun\n",
                 channel);
        }

    recv_buffer [channel] = recv_data | pad;            /* save the character and padding in the buffer */

    if (loopback) {                                     /* if this channel has a loopback cable installed */
        if (recv_param [channel] & DPI_ENABLE_ECHO) {   /*   and the channel has echo enabled */
            recv_buffer [channel ^ 1] = data;           /*     then send the data back to the other channel */

            activate_unit (&line_unit [channel ^ 1], Loop); /* schedule the reception */

            tprintf (atcd_dev, DEB_XFER, "Channel %d character %s echoed to channel %d\n",
                     channel, fmt_char (char_data), channel ^ 1);
            }
        }

    else if (channel <= LAST_TERM) {                        /* otherwise if it's a receive channel */
        if (line_unit [channel].flags & UNIT_CAPSLOCK) {    /*   then if caps lock is down */
            recv_data = toupper (recv_data);                /*     then convert to upper case if lower */
            recv_buffer [channel] = recv_data | pad;        /*       and replace the character in the buffer */
            }

        if (recv_param [channel] & DPI_ENABLE_ECHO) {   /* if the channel has echo enabled */
            char_echo = sim_tt_outcvt (recv_data,       /*   then convert the character per the output mode */
                                       TT_GET_MODE (line_unit [channel].flags));

            if (char_echo >= 0) {                       /* if the converted character is valid for the mode */
                if (channel == 0)                       /*   then if this is for channel 0 */
                    sim_putchar (char_echo);            /*     then write it back to the simulation console */

                else {                                  /* otherwise */
                    tmxr_putc_ln (&atcd_ldsc [channel], /*   write it to the multiplexer output line */
                                  char_echo);

                    if (atcd_ldsc [channel].xmte == 0)  /* if the output buffer is full */
                        tmxr_poll_tx (&atcd_mdsc);      /*   then transmit the line buffer */
                    }

                tprintf (atcd_dev, DEB_XFER, ("Channel %d character %s echoed\n"),
                         channel, fmt_char (char_echo));
                }

            else                                            /* otherwise the echo character was discarded */
                tprintf (atcd_dev, DEB_XFER, "Channel %d character %s echo discarded by output filter\n",
                         channel, fmt_char (char_data));
            }
        }
    }

if (recv_param [channel] & DPI_ENABLE_IRQ)              /* if the channel is enabled to interrupt */
    recv_status [channel] |= DST_COMPLETE;              /*   then set the completion flag */

tprintf (atcd_dev, DEB_XFER, "Channel %d character %s %s\n",
         channel, fmt_char (char_data),
         (data == GEN_ACK ? "generated internally" : "received"));

return;
}


/* Check for a character received on an auxiliary channel.

   If a send or receive channel has its "diagnose" bit set, then this routine is
   called to check if any of the auxiliary channels would receive the character
   too.  If one or more would, then the "receive" routine is called to store the
   character in the appropriate buffer.

   The diagnosis mode is typically used to speed-sense a receive channel.
   In hardware, reception on a given channel is simultaneously received on the
   five auxiliary channels, with each channel set for a different baud rate.
   When a specific character (e.g., CR) is sent, only the channel with the
   correct baud rate setting will receive the intended character.  By
   determining which channel received the correct data, the baud rate of the
   sending terminal may be obtained.

   In simulation, a main channel will receive a character regardless of the baud
   rate configuration.  Therefore, an auxiliary channel will receive the same
   character only if it is configured for the same baud rate and character size.
*/

static void diagnose (HP_WORD control, int32 data)
{
const HP_WORD config = control & DPI_CHAR_CONFIG;           /* main channel character size and baud rate */
int32 channel;

for (channel = FIRST_AUX; channel <= LAST_AUX; channel++)   /* scan the auxiliary channels */
    if ((recv_param [channel] & DPI_CHAR_CONFIG) == config) /* if the character configurations match */
        receive (channel, data, FALSE);                     /*   then receive the data on this channel */

return;
}


/* Scan the channels for a transfer completion interrupt.

   If the multiplexer data flag is not set, this routine is called to scan the
   channels for completion flags.  If the "channel" parameter value is SCAN_ALL,
   then all of the channels are checked.  Otherwise, only the specified channel
   is checked.

   If a channel has its completion flag set, the multiplexer data and status
   words are set for return to the CPU, the data flag is set, and an interrupt
   is requested.  The channel requesting the interrupt is contained in the
   status word.

   In hardware, the recirculating buffer consists of the sixteen receive
   channels, then the sixteen send channels, and then the five auxiliary
   channels.  The completion flags are checked in this order during the
   recirculation after a completion flag is set.  If the scan has been inhibited
   by the data flag, it will commence with the channel currently in the
   recirculation window at the time the flag was cleared and then continue in
   the order indicated.

   In simulation, the scan is always initiated as though at the beginning of a
   recirculation.


   Implementation notes:

    1. After a send completion, the data word contains all ones (stop bits).
*/

static void scan_channels (int32 channel)
{
int32 chan, first_chan, last_chan;

if (channel == SCAN_ALL) {                              /* if all channels are to be scanned */
    first_chan = FIRST_TERM;                            /*   then set the loop limits */
    last_chan  = LAST_TERM;                             /*     to the full range of channels */
    }

else                                                    /* otherwise scan just the channel indicated */
    first_chan = last_chan = channel;                   /*   plus the auxiliary channels if requested */

for (chan = first_chan; chan <= last_chan; chan++) {    /* scan the receive channels */
    if (recv_status [chan] & DST_COMPLETE) {            /* if this channel's completion flag is set */
        tdi_read_word = DDR_DATA (recv_buffer [chan])   /*   then form the input data word */
                          | DDR_CHAN (chan)             /*     from the character, channel, and parity */
                          | RECV_PARITY (recv_buffer [chan]);

        tdi_status_word = recv_status [chan]            /* form the partial status word */
                            | DST_CHAN (chan);

        recv_buffer [chan] = 0;                         /* clear the receive buffer */
        recv_status [chan] = 0;                         /*   and the channel status */

        tprintf (atcd_dev, DEB_CSRW, "Channel %d receive interrupt requested\n",
                 chan);

        tdi_set_interrupt ();                           /* set the data flag and request an interrupt */
        return;                                         /*   and terminate scanning */
        }
    }

for (chan = first_chan; chan <= last_chan; chan++) {    /* scan the send channels */
    if (send_status [chan] & DST_COMPLETE) {            /* if this channel's completion flag is set */
        tdi_read_word = DDR_DATA_MASK                   /*   then form the input data word from the */
                          | DDR_CHAN (chan);            /*     data input buffer and the channel number */

        tdi_status_word = send_status [chan]            /* form the partial status word */
                            | DST_CHAN (chan)
                            | DST_SEND_IRQ;

        send_status [chan] = 0;                         /* clear the channel status */

        tprintf (atcd_dev, DEB_CSRW, "Channel %d send interrupt requested\n",
                 chan);

        tdi_set_interrupt ();                           /* set the data flag and request an interrupt */
        return;                                         /*   and terminate scanning */
        }
    }

if (channel == SCAN_ALL                                     /* if we're scanning all channels */
  || send_param [channel] & DPI_DIAGNOSE                    /*   or the indicated channel is diagnosing */
  || recv_param [channel] & DPI_DIAGNOSE)                   /*     its transmission or reception */
    for (chan = FIRST_AUX; chan <= LAST_AUX; chan++) {      /*       then scan the auxiliary channels */
        if (recv_status [chan] & DST_COMPLETE) {            /* if this channel's completion flag is set */
            tdi_read_word = DDR_DATA (recv_buffer [chan])   /*   then form the input data word */
                              | DDR_CHAN (chan)             /*     from the character, channel, and parity */
                              | RECV_PARITY (recv_buffer [chan]);

            tdi_status_word = recv_status [chan]            /* form the partial status word */
                                | DST_CHAN (chan)
                                | DST_DIAGNOSE;

            recv_buffer [chan] = 0;                         /* clear the receive buffer */
            recv_status [chan] = 0;                         /*   and the channel status */

            tprintf (atcd_dev, DEB_CSRW, "Channel %d receive interrupt requested\n",
                     chan);

            tdi_set_interrupt ();                           /* set the data flag and request an interrupt */
            return;                                         /*   and terminate scanning */
            }
        }

return;                                                     /* no channel has completed */
}


/* Check for a control interrupt.

   If the scan flag is clear, then return the interrupt status bits for the
   channel indicated by the current control counter value.  Otherwise, scan all
   of the control channels, starting with the current counter, to check for a
   status mismatch.  This occurs when either of the incoming status bits does
   not match the stored status, and the corresponding mismatch detection is
   enabled.  If an enabled mismatch is found, request an interrupt from the CPU,
   clear the scan flag, and return the interrupt status bits with the counter
   pointing at the interrupting channel.
*/

static HP_WORD scan_status (void)
{
uint32  chan_count;
HP_WORD interrupts;

if (tci_scan)                                               /* if the control interface is scanning */
    chan_count = TERM_COUNT;                                /*   then look at all of the channels */
else                                                        /* otherwise */
    chan_count = 1;                                         /*   look at only the current channel */

while (chan_count > 0) {                                    /* scan the control channels */
    interrupts = CST_IX (CCN_ESX (cntl_param [tci_cntr])    /* check for an enabled status mismatch */
                   & (cntl_param [tci_cntr] ^ cntl_status [tci_cntr]));

    if (tci_scan) {                                         /* if the interface is scanning */
        if (interrupts) {                                   /*   and a mismatch was found */
            atcc_dib.interrupt_request = SET;               /*     then request an interrupt */

            if (tci_interrupt_mask)                         /* if the interrupt mask is satisfied */
                iop_assert_INTREQ (&atcc_dib);              /*   then assert the INTREQ signal */

            tci_scan = CLEAR;                               /* stop the scan at the current channel */

            tprintf (atcc_dev, DEB_CSRW, "Channel %u interrupt requested\n",
                                         tci_cntr);
            break;
            }

        tci_cntr = (tci_cntr + 1) % TERM_COUNT;             /* set the counter to the next channel in sequence */
        }

    chan_count = chan_count - 1;                            /* drop the count of channels to check */
    }

return interrupts;                                          /* return the interrupt status bits */
}


/* Update the channel status.

   This routine updates the stored RS-232 line status for the specified channel.
   It obtains the status, either real for a serial connection or simulated for a
   Telnet connection, and updates the DCD and DSR states in the channel array.
*/

static void update_status (uint32 channel)
{
TMCKT line_state;

line_state = tmxr_line_status (&atcd_ldsc [channel]);   /* get the current line state */

if (line_state & DCD_Status)                            /* if DCD is set */
    cntl_status [channel] |= DCD;                       /*   then set the corresponding line flag */
else                                                    /* otherwise */
    cntl_status [channel] &= ~DCD;                      /*   clear the corresponding flag */

if (line_state & DSR_Status) {                          /* if DSR is now set */
    if ((cntl_status [channel] & DSR) == 0)             /*   but was not previously */
        tprintf (atcc_dev, DEB_XFER,                    /*     then a connection has occurred */
                 "Channel %u connection raised DSR\n", channel);

    cntl_status [channel] |= DSR;                       /* set the corresponding flag */
    }

else {                                                  /* otherwise DSR is now clear */
    if (cntl_status [channel] & DSR)                    /*   but if it was previously set */
        tprintf (atcc_dev, DEB_XFER,                    /*     then a disconnection has occurred */
                 "Channel %u disconnection dropped DSR\n", channel);

    cntl_status [channel] &= ~DSR;                      /* clear the corresponding flag */
    }

return;
}
