/* hp3000_scmb.c: HP 3000 30033A Selector Channel Maintenance Board simulator

   Copyright (c) 2016-2021, J. David Bryan

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

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

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

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

   SCMB1,SCMB2  HP 30033A Selector Channel Maintenance Board

   01-Apr-21    JDB     Removed REG_FIT from register definitions (obsolete)
   09-Dec-19    JDB     Replaced debugging macros with tracing macros
   12-Sep-16    JDB     Changed DIB register macro usage from SRDATA to DIB_REG
   11-Jun-16    JDB     Bit mask constants are now unsigned
   13-May-16    JDB     Modified for revised SCP API function parameter types
   21-Mar-16    JDB     Changed uint16 types to HP_WORD
   21-Sep-15    JDB     First release version
   27-Jan-15    JDB     Passes the selector channel diagnostic (D429A)
   12-Jan-15    JDB     Passes the SCMB diagnostic (D429A)
   07-Jan-15    JDB     Created

   References:
     - HP 3000 Series II/III System Reference Manual
         (30000-90020, July 1978)
     - HP 3000 Series II Computer System System Service Manual
         (30000-90018, March 1977)
     - Stand-Alone HP 30030B/C Selector Channel Diagnostic
         (30030-90011, July 1978)
     - HP 3000 Series III Engineering Diagrams Set
         (30000-90141, April 1980)


   The HP 30033A Selector Channel Maintenance Board provides the circuitry
   necessary to test the I/O bus signals driven and received by the selector and
   multiplexer channels.  Used with the Stand-Alone Selector Channel and
   Multiplexer Channel diagnostics, the SCMB is used to verify that the correct
   bus signals are driven in response to each of the programmed I/O orders, and
   that the channel responds correctly to the signals returned to it.  The SCMB
   functions as a programmable interface that can log incoming signals and drive
   the outgoing signals, as well as simulate a number of interface hardware
   faults.  Two SCMBs are required to test the multiplexer channel fully, so two
   SCMBs are provided; they are named "SCMB" (or "SCMB1") and "SCMB2".

   In hardware, the SCMB is connected either to the selector channel or
   multiplexer channel buses, and jumper W1 must be set to the SC or MX
   position, depending on the desired diagnostic test.  The device number and
   the service request number jumpers may be configured to use any unassigned
   numbers.

   In simulation, a SET SCMB SC configures the interface for the selector
   channel diagnostic, and a SET SCMB MX configures the interface for the
   multiplexer diagnostic.  If the selector channel diagnostic is run with SET
   SCMB MX, the SCMB itself is tested.  The multiplexer diagnostic requires two
   SCMB cards, so SET SCMB1 MX and SET SCMB2 MX are required.


   The SCMB responds to direct and programmed I/O instructions, as follows:

   Control Word Format (CIO):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M | R | J | V | A | S | load  | H | N | T | C | L |  counter  |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     M = master reset
     R = reset interrupt
     J = set jump met condition
     V = set device end condition
     A = inhibit channel acknowledge
     S = inhibit service request
     H = enable high speed service request
     N = enable special device number
     T = terminate on terminal count
     C = terminate on compare failure
     L = enable device end/clear interface (0/1) on terminate

   Load:
     00 = load the IOAW into the control word
     01 = load the IOCW into the buffer
     10 = load the IOAW into the buffer
     11 = load the IOCW and then the IOAW into the buffer

   Counter:
     000 = counter is disabled
     001 = count READNEXTWD signals
     010 = count PREADSTB signals
     011 = count TOGGLEINXFER signals
     100 = count PWRITESTB signals
     101 = count TOGGLEOUTXFER signals
     110 = count EOT signals
     111 = count CHANSO signals

   The Load field defines how programmed I/O orders will affect the control word
   and buffer.  The Counter field defines which signal occurrences, if any, are
   counted.  If value 000 is selected, the counter does not operate, and the
   buffer value does not change.


   Control Word Format (SIO Control):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 1   0   0 |                 buffer value                  |  word 1
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M | R | J | V | A | S | load  | H | N | T | C | L |  counter  |  word 2
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   If the current control word specifies a Load field value of 01, word 1 is
   loaded into the counter/buffer register.  Otherwise, word 2 is loaded into
   the control word register or counter/buffer register, depending on the Load
   field value.

   If the A bit (inhibit channel acknowledge) is set, CHANACK will be issued for
   this Control order, but all future orders will not be acknowledged.
   Similarly, if the S bit (inhibit service request) is set, a CHANSR will be
   issued for this order but not for future orders.


   Status Word Format (TIO and SIO Status):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | S | D | R | A | X | N | V | E | C | T | I | O | L | 0   0   0 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     S = SIO OK
     D = direct I/O OK (always 1)
     R = interrupt requested
     A = interrupt active
     X = transfer error was asserted
     N = SIO enabled is asserted
     V = device end was asserted
     E = end of transfer was asserted
     C = an end-on-miscompare occurred
     T = an end-on-terminal-count occurred
     I = an input transfer is in progress
     O = an output transfer is in progress
     L = a clear interface has asserted to abort the I/O program

   Note that the Series II Service Manual and the Series III CE Handbook list
   the wrong assignments for status bits 8-11.  The Selector Channel Diagnostic
   manual has the correct assignments.


   Output Data Word Format (WIO and SIO Write):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |               new counter/buffer register value               |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   If the control word C bit (terminate on compare failure) is set, the current
   counter/buffer value is compared to the new value.  If they are not equal,
   the C bit (an end-on-miscompare occurred) is set in the status word.

   The new value is stored in the counter/buffer register only if the control
   word Counter field value is less than 100 (i.e., it is not set to count
   writes).  Otherwise, the value is ignored, but the write is counted.

   If DEVEND is asserted for a selector channel SIO Write, the write is ignored,
   and the PWRITESTB signal is not counted.


   Input Data Word Format (RIO and SIO Read):

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |             current counter/buffer register value             |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   If DEVEND is asserted for a selector channel SIO Read, the read is ignored,
   and the PREADSTB signal is not counted.
*/



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



/* Program constants */

#define SERVICE_DELAY       uS (5)              /* 5 microsecond delay for non-high-speed service request */


/* Unit flags */

#define UNIT_W1_SHIFT       (UNIT_V_UF + 0)     /* jumper W1 */

#define UNIT_W1_SEL         (1u << UNIT_W1_SHIFT)

#define MPX_BUS(card)       ((scmb_unit [card].flags & UNIT_W1_SEL) == 0)
#define SEL_BUS(card)       ((scmb_unit [card].flags & UNIT_W1_SEL) != 0)


/* Debug flags */

#define DEB_CSRW            (1u << 0)           /* trace commands received and status returned */
#define DEB_XFER            (1u << 1)           /* trace channel data reads and writes */
#define DEB_SERV            (1u << 2)           /* trace unit service scheduling calls */
#define DEB_IOB             (1u << 3)           /* trace I/O bus signals and data words */


/* Control word.

     0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
   | M | R | J | V | A | S | load  | H | N | T | C | L |  counter  |
   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/

#define CN_MR               0100000u            /* M = master reset */
#define CN_IRQ_RESET        0040000u            /* R = interrupt reset */
#define CN_JMPMET           0020000u            /* J = set jump met */
#define CN_DEVEND           0010000u            /* V = set device end */
#define CN_NOACK            0004000u            /* A = inhibit channel acknowledge */
#define CN_NOSR             0002000u            /* S = inhibit service request */
#define CN_LOAD_MASK        0001400u            /* load operation mask */
#define CN_HSREQ            0000200u            /* H = high speed service request */
#define CN_DEVNO            0000100u            /* N = special device number */
#define CN_TERM_COUNT       0000040u            /* T = terminate on count */
#define CN_TERM_COMP        0000020u            /* C = terminate on miscompare */
#define CN_CLEAR_IF         0000010u            /* L = clear interface */
#define CN_CNTR_MASK        0000007u            /* counter operation mask */

#define CN_LOAD_SHIFT       8                   /* load operation alignment shift */
#define CN_CNTR_SHIFT       0                   /* counter operation alignment shift */

#define CN_LOAD(c)          ((LOAD_OP) (((c) & CN_LOAD_MASK) >> CN_LOAD_SHIFT))
#define CN_CNTR(c)          ((CNTR_OP) (((c) & CN_CNTR_MASK) >> CN_CNTR_SHIFT))

typedef enum {                                  /* load operations */
    load_cntl_IOAW = 0,
    load_bufr_IOCW = 1,
    load_bufr_IOAW = 2,
    load_bufr_both = 3
    } LOAD_OP;

static const char *const load_names [4] = {     /* indexed by LOAD_OP */
    "load control IOAW",                        /*   00 = load IOAW into control word */
    "load buffer IOCW",                         /*   01 = load IOCW into buffer */
    "load buffer IOAW",                         /*   10 = load IOAW into buffer */
    "load buffer IOCW/AW"                       /*   11 = load IOCW and IOAW into buffer */
    };

typedef enum {                                  /* counter operations */
    count_nothing       = 0,
    count_READNEXTWD    = 1,
    count_PREADSTB      = 2,
    count_TOGGLEINXFER  = 3,
    count_PWRITESTB     = 4,
    count_TOGGLEOUTXFER = 5,
    count_EOT           = 6,
    count_CHANSO        = 7
    } CNTR_OP;

static const char *const count_names [8] = {    /* indexed by CNTR_OP */
    "count nothing",                            /*   000 = counter is disabled */
    "count READNEXTWD",                         /*   001 = count READNEXTWD */
    "count PREADSTB",                           /*   010 = count PREADSTB */
    "count TOGGLEINXFER",                       /*   011 = count TOGGLEINXFER */
    "count PWRITESTB",                          /*   100 = count PWRITESTB */
    "count TOGGLEOUTXFER",                      /*   101 = count TOGGLEOUTXFER */
    "count EOT",                                /*   110 = count EOT */
    "count CHANSO"                              /*   111 = count CHANSO */
    };

static const BITSET_NAME control_names [] = {           /* Control word names */
    "master reset",                                     /*   bit  0 */
    "reset interrupt",                                  /*   bit  1 */
    "set JMPMET",                                       /*   bit  2 */
    "set DEVEND",                                       /*   bit  3 */
    "inhibit CHANACK",                                  /*   bit  4 */
    "inhibit SR",                                       /*   bit  5 */
    NULL,                                               /*   bit  6 */
    NULL,                                               /*   bit  7 */
    "high speed",                                       /*   bit  8 */
    "send DEVNO",                                       /*   bit  9 */
    "end on count",                                     /*   bit 10 */
    "end on miscompare",                                /*   bit 11 */
    "\1end with clear interface\0end with device end"   /*   bit 12 */
    };

static const BITSET_FORMAT control_format =     /* names, offset, direction, alternates, bar */
    { FMT_INIT (control_names, 3, msb_first, has_alt, append_bar) };


/* Status word.

     0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
   | S | D | R | A | X | N | V | E | C | T | I | O | L | 0   0   0 |
   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   NOTE: The Series II Service Manual and the Series III CE Handbook list the
   wrong assignments for status bits 8-11.  The Selector Channel Diagnostic
   manual has the correct assignments.
*/

#define ST_SIO_OK           0100000u            /* S = SIO OK to use */
#define ST_DIO_OK           0040000u            /* D = direct I/O OK to use (always 1) */
#define ST_INTREQ           0020000u            /* R = interrupt requested */
#define ST_INTACT           0010000u            /* A = interrupt active */
#define ST_XFERERR          0004000u            /* X = transfer error is asserted */
#define ST_SIOENABLED       0002000u            /* N = SIO enabled is asserted */
#define ST_DEVEND           0001000u            /* V = device end is asserted */
#define ST_EOT              0000400u            /* E = end of transfer is asserted */
#define ST_END_MISCMP       0000200u            /* C = end on miscompare occurred */
#define ST_END_COUNT        0000100u            /* T = end on terminal count occurred */
#define ST_INXFER           0000040u            /* I = input transfer is asserted */
#define ST_OUTXFER          0000020u            /* O = output transfer is asserted  */
#define ST_CLEAR_IF         0000010u            /* L = clear interface is asserted */

#define END_CONDITION       (ST_END_MISCMP | ST_END_COUNT)

static const BITSET_NAME status_names [] = {    /* Status word names */
    "SIO OK",                                   /*   bit  0 */
    "DIO OK",                                   /*   bit  1 */
    "int request",                              /*   bit  2 */
    "int active",                               /*   bit  3 */
    "transfer error",                           /*   bit  4 */
    "SIO enabled",                              /*   bit  5 */
    "device end",                               /*   bit  6 */
    "end of transfer",                          /*   bit  7 */
    "miscompare",                               /*   bit  8 */
    "terminal count",                           /*   bit  9 */
    "input transfer",                           /*   bit 10 */
    "output transfer",                          /*   bit 11 */
    "clear interface"                           /*   bit 12 */
    };

static const BITSET_FORMAT status_format =      /* names, offset, direction, alternates, bar */
    { FMT_INIT (status_names, 3, msb_first, no_alt, no_bar) };


/* SCMB state */

typedef enum {
    card1,                                      /* first card ID */
    card2                                       /* second card ID */
    } CARD_ID;

typedef struct {
    HP_WORD control_word;                       /* control word register */
    HP_WORD status_word;                        /* status word register */
    HP_WORD counter;                            /* counter/buffer register */
    HP_WORD flags;                              /* status flags */
    uint32  saved_srn;                          /* saved SR number */

    FLIP_FLOP sio_busy;                         /* SIO busy flip-flop */
    FLIP_FLOP channel_sr;                       /* channel service request flip-flop */
    FLIP_FLOP device_sr;                        /* device service request flip-flop */
    FLIP_FLOP input_xfer;                       /* input transfer flip-flop */
    FLIP_FLOP output_xfer;                      /* output transfer flip-flop */

    FLIP_FLOP jump_met;                         /* jump met flip-flop */
    FLIP_FLOP device_end;                       /* device end flip-flop */
    FLIP_FLOP stop_transfer;                    /* stop transfer flip-flop */
    } SCMB_STATE;

static SCMB_STATE scmb [2];                     /* per-card state variables */


/* SCMB local SCP support routines */

static CNTLR_INTRF scmb_interface;
static t_stat      scmb_service   (UNIT   *uptr);
static t_stat      scmb_reset     (DEVICE *dptr);
static t_stat      scmb_set_bus   (UNIT   *uptr, int32 value, char *cptr, void *desc);


/* SCMB local utility routines */

static void sio_reset         (CARD_ID card);
static void clear_logic       (CARD_ID card);
static void increment_counter (CARD_ID card);


/* SCMB SCP interface data structures.


   Implementation notes:

    1. The DIB, UNIT, and DEVICE structures for the two cards must be arrayed so
       that access via card number is possible.

    2. The SCMB interfaces are disabled by default, as they are only used during
       diagnostic testing.
*/


/* Device information blocks */

static DIB scmb_dib [] = {
    { &scmb_interface,                          /* device interface */
      65,                                       /* device number */
      0,                                        /* service request number */
      10,                                       /* interrupt priority */
      INTMASK_UNUSED,                           /* interrupt mask */
      card1                                     /* card index for card 1 */
      },

    { &scmb_interface,                          /* device interface */
      66,                                       /* device number */
      1,                                        /* service request number */
      11,                                       /* interrupt priority */
      INTMASK_UNUSED,                           /* interrupt mask */
      card2                                     /* card index for card 2 */
      }
    };


/* Unit list */

static UNIT scmb_unit [] = {
    { UDATA (&scmb_service, 0, 0), SERVICE_DELAY },     /* unit for card 1 */
    { UDATA (&scmb_service, 0, 0), SERVICE_DELAY }      /* unit for card 2 */
    };


/* Register lists */

static REG scmb1_reg [] = {
/*    Macro   Name    Location                    Width  Offset   Flags  */
/*    ------  ------  --------------------------  -----  ------  ------- */
    { ORDATA (CNTL,   scmb [card1].control_word,   16),                  },
    { ORDATA (STAT,   scmb [card1].status_word,    16),                  },
    { ORDATA (CNTR,   scmb [card1].counter,        16),                  },
    { ORDATA (SRSAVE, scmb [card1].saved_srn,       8),          REG_HRO },

    { FLDATA (SIOBSY, scmb [card1].sio_busy,               0)            },
    { FLDATA (CHANSR, scmb [card1].channel_sr,             0)            },
    { FLDATA (DEVSR,  scmb [card1].device_sr,              0)            },
    { FLDATA (INXFR,  scmb [card1].input_xfer,             0)            },
    { FLDATA (OUTXFR, scmb [card1].output_xfer,            0)            },

    { FLDATA (JMPMET, scmb [card1].jump_met,               0)            },
    { FLDATA (XFRERR, scmb [card1].flags,                 11)            },
    { FLDATA (EOT,    scmb [card1].flags,                  8)            },
    { FLDATA (TRMCNT, scmb [card1].flags,                  6)            },
    { FLDATA (MISCMP, scmb [card1].flags,                  7)            },
    { FLDATA (DEVEND, scmb [card1].device_end,             0)            },
    { FLDATA (STOP,   scmb [card1].stop_transfer,          0)            },

      DIB_REGS (scmb_dib [card1]),

    { NULL }
    };

static REG scmb2_reg [] = {
/*    Macro   Name    Location                    Width  Offset   Flags  */
/*    ------  ------  --------------------------  -----  ------  ------- */
    { ORDATA (CNTL,   scmb [card2].control_word,   16),                  },
    { ORDATA (STAT,   scmb [card2].status_word,    16),                  },
    { ORDATA (CNTR,   scmb [card2].counter,        16),                  },
    { ORDATA (SRSAVE, scmb [card2].saved_srn,       8),          REG_HRO },

    { FLDATA (SIOBSY, scmb [card2].sio_busy,               0)            },
    { FLDATA (CHANSR, scmb [card2].channel_sr,             0)            },
    { FLDATA (DEVSR,  scmb [card2].device_sr,              0)            },
    { FLDATA (INXFR,  scmb [card2].input_xfer,             0)            },
    { FLDATA (OUTXFR, scmb [card2].output_xfer,            0)            },

    { FLDATA (JMPMET, scmb [card2].jump_met,               0)            },
    { FLDATA (XFRERR, scmb [card2].flags,                 11)            },
    { FLDATA (EOT,    scmb [card2].flags,                  8)            },
    { FLDATA (TRMCNT, scmb [card2].flags,                  6)            },
    { FLDATA (MISCMP, scmb [card2].flags,                  7)            },
    { FLDATA (DEVEND, scmb [card2].device_end,             0)            },
    { FLDATA (STOP,   scmb [card2].stop_transfer,          0)            },

      DIB_REGS (scmb_dib [card2]),

    { NULL }
    };


/* Modifier lists */

static MTAB scmb1_mod [] = {
/*    Mask Value   Match Value  Print String  Match String  Validation     Display  Descriptor */
/*    -----------  -----------  ------------  ------------  -------------  -------  ---------- */
    { UNIT_W1_SEL, UNIT_W1_SEL, "W1=SC",      "SC",         &scmb_set_bus, NULL,    NULL       },
    { UNIT_W1_SEL, 0,           "W1=MX",      "MX",         &scmb_set_bus, NULL,    NULL       },

/*    Entry Flags  Value       Print String  Match String  Validation   Display       Descriptor                */
/*    -----------  ----------  ------------  ------------  -----------  ------------  ------------------------- */
    { MTAB_XDV,    VAL_DEVNO,  "DEVNO",      "DEVNO",      &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card1] },
    { MTAB_XDV,    VAL_INTPRI, "INTPRI",     "INTPRI",     &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card1] },
    { MTAB_XDV,    VAL_SRNO,   "SRNO",       "SRNO",       &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card1] },
    { 0 }
    };

static MTAB scmb2_mod [] = {
/*    Mask Value   Match Value  Print String  Match String  Validation     Display  Descriptor */
/*    -----------  -----------  ------------  ------------  -------------  -------  ---------- */
    { UNIT_W1_SEL, UNIT_W1_SEL, "W1=SC",      "SC",         &scmb_set_bus, NULL,    NULL       },
    { UNIT_W1_SEL, 0,           "W1=MX",      "MX",         &scmb_set_bus, NULL,    NULL       },

/*    Entry Flags  Value       Print String  Match String  Validation   Display       Descriptor                */
/*    -----------  ----------  ------------  ------------  -----------  ------------  ------------------------- */
    { MTAB_XDV,    VAL_DEVNO,  "DEVNO",      "DEVNO",      &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card2] },
    { MTAB_XDV,    VAL_INTPRI, "INTPRI",     "INTPRI",     &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card2] },
    { MTAB_XDV,    VAL_SRNO,   "SRNO",       "SRNO",       &hp_set_dib, &hp_show_dib, (void *) &scmb_dib [card2] },
    { 0 }
    };


/* Debugging trace list */

static DEBTAB scmb_deb [] = {
    { "CSRW",  DEB_CSRW },                      /* Interface control, status, read, and write actions */
    { "XFER",  DEB_XFER },                      /* Channel data reads and writes */
    { "SERV",  DEB_SERV },                      /* Unit service scheduling calls */
    { "IOBUS", DEB_IOB  },                      /* Interface I/O bus signals and data words */
    { NULL,    0        }
    };


/* Device descriptors */

DEVICE scmb_dev [] = {
    { "SCMB",                                   /* device name */
      &scmb_unit [card1],                       /* unit array */
      scmb1_reg,                                /* register array */
      scmb1_mod,                                /* modifier array */
      1,                                        /* number of units */
      8,                                        /* address radix */
      PA_WIDTH,                                 /* address width */
      1,                                        /* address increment */
      8,                                        /* data radix */
      DV_WIDTH,                                 /* data width */
      NULL,                                     /* examine routine */
      NULL,                                     /* deposit routine */
      &scmb_reset,                              /* reset routine */
      NULL,                                     /* boot routine */
      NULL,                                     /* attach routine */
      NULL,                                     /* detach routine */
      &scmb_dib [card1],                        /* device information block pointer */
      DEV_DEBUG | DEV_DISABLE | DEV_DIS,        /* device flags */
      0,                                        /* debug control flags */
      scmb_deb,                                 /* debug flag name array */
      NULL,                                     /* memory size change routine */
      NULL                                      /* logical device name */
      },

    { "SCMB2",                                  /* device name */
      &scmb_unit [card2],                       /* unit array */
      scmb2_reg,                                /* register array */
      scmb2_mod,                                /* modifier array */
      1,                                        /* number of units */
      8,                                        /* address radix */
      PA_WIDTH,                                 /* address width */
      1,                                        /* address increment */
      8,                                        /* data radix */
      DV_WIDTH,                                 /* data width */
      NULL,                                     /* examine routine */
      NULL,                                     /* deposit routine */
      &scmb_reset,                              /* reset routine */
      NULL,                                     /* boot routine */
      NULL,                                     /* attach routine */
      NULL,                                     /* detach routine */
      &scmb_dib [card2],                        /* device information block pointer */
      DEV_DEBUG | DEV_DISABLE | DEV_DIS,        /* device flags */
      0,                                        /* debug control flags */
      scmb_deb,                                 /* debug flag name array */
      NULL,                                     /* memory size change routine */
      NULL                                      /* logical device name */
      }
    };



/* SCMB local SCP support routines */



/* Selector Channel Maintenance Board interface.

   The interface is installed on the IOP bus and either the Multiplexer or
   Selector Channel bus and receives direct and programmed I/O commands from the
   IOP and channel, respectively.  In simulation, the asserted signals on the
   buses 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.

   Jumper W1 on the interface PCA must be set to match the bus (multiplexer or
   selector) to which the SCMB is connected.  The multiplexer and selector
   channels have slightly different signal requirements, and this jumper
   configures the logic to account for the difference.

   The diagnostics use direct I/O to configure the SCMB and then use programmed
   I/O to test the channel's interaction with the interface.


   Implementation notes:

    1. In hardware, asserting DSTARTIO sets the Channel SR flip-flop, but the
       output is masked off unless the SCMB is connected to the multiplexer
       channel (the selector channel does not use the Channel SR flip-flop).
       In simulation, setting the flip-flop is inhibited.

    2. In hardware, asserting DEVEND to the selector channel inhibits
       generation of the PREADSTB and PWRITESTB signals.  In simulation, DEVEND
       is returned in response to a PREADSTB or PWRITESTB if the Device End
       flip-flop is set.  As the strobes may cause the counter to increment,
       counting is inhibited in simulation if the Device End flip-flop is set
       and the SCMB is on the selector channel bus.

    3. In hardware, the SCMB does not use ACKSR to reset the Device SR
       flip-flop.  Instead, the flip-flop is preset by PCMD1 or PCONTSTB or if
       the Input Transfer or Output Transfer flip-flop is set; it is clocked to
       zero by the leading edge of CHANSO.  In simulation, the Device SR
       flip-flop is cleared on entry by CHANSO if both the Input and Output
       Transfer flip-flops are clear.  This provides the same action as the
       asynchronous set overriding the synchronous clear in hardware.

    4. If channel acknowledgement is inhibited, the CHANACK signal is not
       returned to the selector channel.  This causes a CHANSO timeout in the
       channel.  Similarly, if channel service requests are inhibited, CHANSR
       will not be returned to the selector channel, which will cause a timeout
       and channel abort.

    5. In hardware, clearing the "enable high speed service request" bit in the
       control word delays SR assertion for five microseconds after the device
       SR flip-flop sets.  In software, the SCMB unit service routine is
       scheduled and the request signal is not returned from the interface; when
       the delay expires, the service routine calls either "mpx_assert_SRn" or
       "sel_assert_CHANSR" to request channel service.

       However, if the "inhibit channel acknowledge" or "inhibit service
       request" bit in the control word is also set, then scheduling of the
       service routine is inhibited.  Otherwise, SR would be asserted after the
       channel had been aborted by the timeout.

    6. In hardware, setting the "enable special device number" bit in the
       control word causes the SCMB to gate bits 8-15 of the counter/buffer
       register onto SR6-13 for the DSTARTIO signal only.  This supplies the
       selector channel with a configurable device number instead of that of the
       SCMB.  For all other operations, e.g., interrupts, the regular SCMB
       device number is used.

       In simulation, the device number is obtained from the DIB passed to the
       "sel_assert_REQ" routine.  If the special bit is set, the device number
       is changed temporarily before calling "sel_assert_REQ" and then restored
       afterward.  This ensures that interrupts in particular are handled
       correctly.  (An alternate method of passing a secondary DIB containing
       the special device number won't work, as the selector channel will use
       the secondary DIB to request an interrupt, but the IOP will use the
       standard DIB to respond to the interrupt.)

    7. 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.

    8. Although support for the "count_CHANSO" option is provided, none of the
       diagnostics (SCMB, MPX, and SEL) test this option.

    9. In simulation, we allow the device number to be changed during a
       simulation stop.  However, the SCMB may be spoofing the device number,
       and it is this spoofed number that must be restored during the channel
       initialization that follows resumption.  This presents no problem to the
       multiplexer channel, which asserts DEVNODB to the interface as part of
       each I/O order execution.  However, the selector channel requests the
       device number once during the REQ assertion that starts the I/O program
       and saves it internally for later use.

       To accommodate changing device numbers while spoofing is enabled, the
       selector channel simulator asserts DEVNODB to the interface during
       initialization.  The SCMB responds to the DEVNODB signal, as it supports
       connection to the multiplexer channel.  Devices that connect only to the
       selector channel will not respond to DEVNODB, causing the initializer to
       use the DIB field to obtain the device number.
*/

static SIGNALS_DATA scmb_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const CARD_ID card = (CARD_ID) (dibptr->card_index);    /* the ID number of the card */
LOAD_OP        load_operation;
FLIP_FLOP      assert_sr;
HP_WORD        saved_devno;
INBOUND_SIGNAL signal;
INBOUND_SET    working_set      = inbound_signals;
HP_WORD        outbound_value   = 0;
OUTBOUND_SET   outbound_signals = NO_SIGNALS;

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

if (inbound_signals & CHANSO                            /* the leading edge of CHANSO */
  && scmb [card].input_xfer == CLEAR                    /*   clears the Device SR flip-flop */
  && scmb [card].output_xfer == CLEAR)                  /*     if not overridden by the Q outputs */
    scmb [card].device_sr = CLEAR;                      /*       of the Input and Output Transfer flip-flops */

while (working_set) {                                   /* while there are signals to process */
    signal = IONEXTSIG (working_set);                   /*   isolate the next signal */

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

        case DWRITESTB:
            tprintf (scmb_dev [card], DEB_CSRW, "Counter/buffer value %06o set\n",
                     inbound_value);

            scmb [card].counter = inbound_value;        /* set the counter/buffer */
            break;


        case DREADSTB:
            outbound_value = scmb [card].counter;       /* return the counter/buffer value */

            tprintf (scmb_dev [card], DEB_CSRW, "Counter/buffer value %06o returned\n",
                     outbound_value);
            break;


        case DCONTSTB:
            tprintf (scmb_dev [card], DEB_CSRW, "Control is %s%s | %s\n",
                     fmt_bitset (inbound_value, control_format),
                     load_names  [CN_LOAD  (inbound_value)],
                     count_names [CN_CNTR (inbound_value)]);

            scmb [card].control_word = inbound_value;       /* save the new control word value */

            if (scmb [card].control_word & CN_MR)           /* if master reset is requested */
                scmb_reset (&scmb_dev [card]);              /*   then perform an I/O reset */

            if (scmb [card].control_word & CN_IRQ_RESET)    /* if reset interrupt is requested */
                dibptr->interrupt_request = CLEAR;          /*   then clear the interrupt request */

            scmb [card].device_end = CLEAR;                 /* clear DEVEND and EOT status */
            scmb [card].flags     &= ~ST_EOT;               /*   in preparation for a new transfer */
            break;


        case DSTATSTB:
        case PSTATSTB:
            scmb [card].status_word = ST_DIO_OK | scmb [card].flags;    /* copy the flags to the status word */

            if (MPX_BUS (card) || sel_is_idle) {            /* if we're on the MPX bus or the SEL is not busy */
                scmb [card].status_word |= ST_SIOENABLED;   /*   then SIO is enabled */

                if (scmb [card].sio_busy == CLEAR)          /* if we're not running an SIO program */
                    scmb [card].status_word |= ST_SIO_OK;   /*   then report that the SCMB is available */
                }

            if (dibptr->interrupt_request == SET)           /* reflect the interrupt request state */
                scmb [card].status_word |= ST_INTREQ;       /*   in the status word */

            if (dibptr->interrupt_active == SET)            /* reflect the interrupt active state */
                scmb [card].status_word |= ST_INTACT;       /*   in the status word */

            if (scmb [card].device_end == SET)              /* reflect the device end flip-flop state */
                scmb [card].status_word |= ST_DEVEND;       /*   in the status word */

            if (scmb [card].input_xfer == SET)              /* reflect the input transfer flip-flop state */
                scmb [card].status_word |= ST_INXFER;       /*   in the status word */

            if (scmb [card].output_xfer == SET)             /* reflect the output transfer flip-flop state */
                scmb [card].status_word |= ST_OUTXFER;      /*   in the status word */

            outbound_value = scmb [card].status_word;       /* return the status word */

            tprintf (scmb_dev [card], DEB_CSRW, "Status is %s\n",
                     fmt_bitset (outbound_value, status_format));
            break;


        case DSETINT:
        case SETINT:
            dibptr->interrupt_request = SET;            /* set the interrupt request flip-flop */
            outbound_signals |= INTREQ;                 /*   and request the interrupt */
            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 DSTARTIO:
            tprintf (scmb_dev [card], DEB_CSRW, "Channel program started\n");

            scmb [card].sio_busy      = SET;            /* set the SIO busy flip-flop */
            scmb [card].stop_transfer = CLEAR;          /*   and clear the stop transfer flip-flop */

            sio_reset (card);                           /* clear in preparation for the new program */

            if (MPX_BUS (card)) {                       /* if the card is configured for the multiplexer channel */
                scmb [card].channel_sr = SET;           /*   then set the channel service request flip-flop */
                mpx_assert_REQ (dibptr);                /*     and request the channel */
                outbound_signals |= SRn;
                }

            else                                            /* otherwise request the selector channel */
                if (scmb [card].control_word & CN_DEVNO) {  /* if the special device number flag is set */
                    saved_devno = dibptr->device_number;    /*   then save the real device number */

                    dibptr->device_number =                 /* use the counter as the device number */
                      LOWER_BYTE (scmb [card].counter);

                    sel_assert_REQ (dibptr);                /* request the channel */

                    dibptr->device_number = saved_devno;    /* restore the real device number */
                    }

                else                                        /* otherwise request the channel */
                    sel_assert_REQ (dibptr);                /*   with the standard device number */
            break;


        case CHANSO:
            if (CN_CNTR (scmb [card].control_word) == count_CHANSO) /* if counting is enabled for this signal */
                increment_counter (card);                           /*   then increment the counter */

            if ((scmb [card].control_word & CN_NOACK) == 0)         /* if CHANACK is not inhibited */
                outbound_signals |= CHANACK;                        /*   then acknowledge the CHANSO signal */
            break;


        case TOGGLESR:
            TOGGLE (scmb [card].channel_sr);            /* set or clear the service request flip-flop */
            break;


        case TOGGLESIOOK:
            TOGGLE (scmb [card].sio_busy);              /* set or clear the SIO busy flip-flop */

            if (scmb [card].sio_busy == CLEAR)          /* if the channel is now idle */
                tprintf (scmb_dev [card], DEB_CSRW, "Channel program ended\n");
            break;


        case TOGGLEINXFER:
            if (CN_CNTR (scmb [card].control_word) == count_TOGGLEINXFER)   /* if counting is enabled for this signal */
                increment_counter (card);                                   /*   then increment the counter */

            TOGGLE (scmb [card].input_xfer);                    /* set or clear the input transfer flip-flop */

            if (scmb [card].input_xfer == SET) {                /* if we're starting a new transfer */
                scmb [card].flags &= ~ST_EOT;                   /*   then clear the EOT flag */

                scmb [card].device_end =                        /* set or clear device end status depending on */
                  D_FF (scmb [card].control_word & CN_DEVEND);  /*   whether an immediate device end is enabled */
                }

            scmb [card].device_sr = SET;                        /* preset the device SR flip-flop */
            break;


        case TOGGLEOUTXFER:
            if (CN_CNTR (scmb [card].control_word) == count_TOGGLEOUTXFER)  /* if counting is enabled for this signal */
                increment_counter (card);                                   /*   then increment the counter */

            TOGGLE (scmb [card].output_xfer);                   /* set or clear the output transfer flip-flop */

            if (scmb [card].output_xfer == SET) {               /* if we're starting a new transfer */
                scmb [card].flags &= ~ST_EOT;                   /*   then clear the EOT flag */

                scmb [card].device_end =                        /* set or clear device end status depending on */
                  D_FF (scmb [card].control_word & CN_DEVEND);  /*   whether an immediate device end is enabled */
                }

            scmb [card].device_sr = SET;                        /* preset the device SR flip-flop */
            break;


        case DEVNODB:
            if (scmb [card].control_word & CN_DEVNO)                    /* if the special device number flag is set */
                outbound_value = LOWER_BYTE (scmb [card].counter) * 4;  /*   then use the counter as the device number */
            else                                                        /* otherwise */
                outbound_value = dibptr->device_number * 4;             /*   use the preset device number */

            outbound_signals = NO_SIGNALS;                              /* clear CHANACK in case SEL issued the signal */
            break;


        case PCMD1:
            if (CN_LOAD (scmb [card].control_word) == load_bufr_IOCW)   /* if buffer load is enabled */
                working_set |= DWRITESTB;                               /*   then set the counter to the inbound value */

            scmb [card].device_sr = SET;                                /* request channel service */
            break;


        case PCONTSTB:
            load_operation = CN_LOAD (scmb [card].control_word);    /* isolate the load operation from the control word */

            if (load_operation == load_cntl_IOAW) {     /* if loading IOAW into the control word is requested */
                working_set |= DCONTSTB;                /*   then set the control word to the inbound value */

                if (inbound_value & CN_NOACK)           /* if the CHANACK timeout will be enabled */
                    outbound_signals |= CHANACK;        /*   then acknowledge the CHANSO signal this time only */
                }

            else if (load_operation != load_bufr_IOCW)  /* otherwise if loading the IOAW into the buffer if enabled */
                working_set |= DWRITESTB;               /*   then set the buffer to the inbound value */

            scmb [card].device_sr = SET;                /* request channel service */
            break;


        case READNEXTWD:
            if (CN_CNTR (scmb [card].control_word) == count_READNEXTWD) /* if counting is enabled for this signal */
                increment_counter (card);                               /*   then increment the counter */
            break;


        case PREADSTB:
            if (scmb [card].device_end == CLEAR || MPX_BUS (card)) {    /* if device end is clear or we're on the MPX bus */
                outbound_value = scmb [card].counter;                   /*   then read the counter/buffer value */

                if (CN_CNTR (scmb [card].control_word) == count_PREADSTB)   /* if counting is enabled for this signal */
                    increment_counter (card);                               /*   then increment the counter */

                tprintf (scmb_dev [card], DEB_XFER, "Counter/buffer value %06o read\n",
                         outbound_value);
                }
            break;


        case PWRITESTB:
            if (scmb [card].device_end == CLEAR || MPX_BUS (card)) {    /* if device end is clear or we're on the MPX bus */
                if (scmb [card].control_word & CN_TERM_COMP) {          /*   then if we're doing a comparison */
                    if (scmb [card].counter != inbound_value)           /*     and the inbound value doesn't match */
                        scmb [card].flags |= ST_END_MISCMP;             /*       then set the miscompare flag */

                    tprintf (scmb_dev [card], DEB_XFER, "Inbound value %06o compared to counter/buffer value %06o\n",
                             inbound_value, scmb [card].counter);
                    }

                else if (CN_CNTR (scmb [card].control_word) < count_PWRITESTB) {    /* otherwise if we're not counting writes */
                    scmb [card].counter = inbound_value;                            /*   then set the counter/buffer */

                    tprintf (scmb_dev [card], DEB_XFER, "Counter/buffer value %06o written\n",
                             inbound_value);
                    }

                if (CN_CNTR (scmb [card].control_word) == count_PWRITESTB)  /* if counting is enabled for this signal */
                    increment_counter (card);                               /*   then increment the counter */
                }
            break;


        case SETJMP:
            if (scmb [card].control_word & CN_JMPMET)   /* if conditional jumps are configured to succeed */
                scmb [card].jump_met = SET;             /*   then set JMPMET status */
            break;


        case EOT:
            if (CN_CNTR (scmb [card].control_word) == count_EOT)    /* if counting is enabled for this signal */
                increment_counter (card);                           /*   then increment the counter */

            scmb [card].flags |= ST_EOT;                            /* set the end of transfer status */
            break;


        case XFERERROR:
            if (scmb [card].stop_transfer == CLEAR) {   /* if we haven't stopped yet */
                clear_logic (card);                     /*   then clear the interface and abort the transfer */

                scmb [card].stop_transfer = SET;                /* inhibit another interface clear */
                scmb [card].flags |= ST_XFERERR | ST_CLEAR_IF;  /* set the transfer error and clear interface status */

                sim_cancel (&scmb_unit [card]);         /* cancel any pending delayed SR assertion */

                dibptr->interrupt_request = SET;        /* set the interrupt request flip-flop */
                outbound_signals |= INTREQ;             /*   and request the interrupt */
                }
            break;


        case DSETMASK:                                  /* not used by this interface */
        case ACKSR:                                     /* not used by this interface */
        case PFWARN:                                    /* not used by this interface */
            break;
        }

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


if (scmb [card].flags & END_CONDITION)                  /* if a termination condition is present */
    if ((scmb [card].control_word & CN_CLEAR_IF) == 0)  /*   then if we want a device end */
        scmb [card].device_end = SET;                   /*     then indicate a device end abort */

    else if (scmb [card].stop_transfer == CLEAR) {      /* otherwise if we haven't stopped yet */
        clear_logic (card);                             /*   then clear the interface and abort the transfer */

        scmb [card].stop_transfer = SET;                /* inhibit another interface clear */
        scmb [card].flags |= ST_CLEAR_IF;               /*   and set the clear interface status */

        dibptr->interrupt_request = SET;                /* set the request flip-flop */
        outbound_signals |= INTREQ;                     /*   and request the interrupt */
        }

if (scmb [card].control_word & CN_HSREQ)                /* if high-speed requests are enabled */
    assert_sr = D_FF (scmb [card].channel_sr            /*   then assert SR immediately if indicated */
                      | scmb [card].device_sr);

else {                                                  /* otherwise assert SR immediately */
    assert_sr = scmb [card].channel_sr;                 /*   only if the channel is requesting service */

    if ((! assert_sr & scmb [card].device_sr)           /* if a delayed device SR assertion is requested */
      && (MPX_BUS (card) || outbound_signals & CHANACK) /*   and we're on the MPX bus or CHANACK is not inhibited */
      && (scmb [card].control_word & CN_NOSR) == 0) {   /*     and channel service is not inhibited */
        sim_activate (&scmb_unit [card],                /*       then schedule SR assertion in 5 microseconds */
                      scmb_unit [card].wait);

        tprintf (scmb_dev [card], DEB_SERV, "Delay %u SR service scheduled\n",
                 scmb_unit [card].wait);
        }
    }


if (assert_sr)                                          /* if a service request is indicated */
    if (MPX_BUS (card))                                 /*   then if we're on the multiplexer bus */
        outbound_signals |= SRn;                        /*     then assert the SRn signal */
    else if ((scmb [card].control_word & CN_NOSR) == 0) /*   otherwise if channel service is not inhibited */
        outbound_signals |= CHANSR;                     /*     then assert the CHANSR signal */

if (scmb [card].jump_met == SET)                        /* if the jump met flip-flop is set */
    outbound_signals |= JMPMET;                         /*   then assert the JMPMET signal */

if (scmb [card].device_end == SET && SEL_BUS (card)) /* if device end is set and we're on the SEL bus */
    outbound_signals |= DEVEND;                         /*   then assert the DEVEND signal */

tprintf (scmb_dev [card], 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 */
}


/* Service the SCMB.

   The service routine delays assertion of channel service request if the SCMB
   is not in high-speed mode.  The delay corresponds to five microseconds.

   It is important that scheduling not be performed if the channel is given an
   abort condition.  Otherwise, SR would be asserted while the channel is idle
   or servicing another device.
*/

static t_stat scmb_service (UNIT *uptr)
{
const CARD_ID card = (CARD_ID) (uptr == &scmb_unit [card2]);    /* the ID number of the card */

tprintf (scmb_dev [card], DEB_SERV, "SR service entered\n");

if (MPX_BUS (card))                                     /* if we're connected to the multiplexer channel */
    mpx_assert_SRn (&scmb_dib [card]);                  /*   then assert the SRn signal */
else                                                    /* otherwise we're connected to the selector channel */
    sel_assert_CHANSR (&scmb_dib [card]);               /*   so assert the CHANSR signal */

return SCPE_OK;
}


/* Reset the SCMB.

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

   For this interface, IORESET is identical to a Programmed Master Reset, which
   corresponds to the internal RST1 signal.

   For a power-on reset, the logical name "SCMB1" is assigned to the first SCMB
   card, so that it may referenced either as that name or as "SCMB" for use
   where only one SCMB is needed.
*/

static t_stat scmb_reset (DEVICE *dptr)
{
const DIB *const dibptr = (DIB *) (dptr->ctxt);         /* the DIB pointer */
const CARD_ID card = (CARD_ID) (dibptr->card_index);    /* the ID number of the card */

if ((sim_switches & SWMASK ('P'))                       /* if this is a power-on reset */
  && card == card1                                      /*   and we're being called for SCMB1 */
  && scmb_dev [card1].lname == NULL)                    /*     and the logical name has not been set yet */
    scmb_dev [card1].lname = strdup ("SCMB1");          /*       then allocate and initialize the name */

scmb [card].counter = 0;                                /* clear the counter/buffer register */
scmb [card].control_word = 0;                           /*   and the control word register */

sio_reset (card);                                       /* reset the remainder */
clear_logic (card);                                     /*   of the card logic */

sim_cancel (dptr->units);                               /* cancel any pending delayed SR assertion */

return SCPE_OK;
}


/* Set the bus connection.

   The SCMB may be connected either to the multiplexer or the selector channel
   bus.  If the interface is being moved from the multiplexer to the selector,
   save the SCMB's current service request number and set it to "unused" so that
   multiplexer initialization won't pick it up by mistake.
*/

static t_stat scmb_set_bus (UNIT *uptr, int32 value, char *cptr, void *desc)
{
const CARD_ID card = (CARD_ID) (uptr == &scmb_unit [card2]);

if (value == UNIT_W1_SEL && MPX_BUS (card)) {                       /* if we're moving from MPX to SEL */
    scmb [card].saved_srn = scmb_dib [card].service_request_number; /*   then save the current SR number */
    scmb_dib [card].service_request_number = SRNO_UNUSED;           /*     for later restoration */
    }

else if (value == 0 && SEL_BUS (card))                              /* otherwise if moving from SEL to MPX */
    scmb_dib [card].service_request_number = scmb [card].saved_srn; /*   then restore the previous SR number */

return SCPE_OK;
}



/* SCMB local utility routines */



/* Reset for a new program.

   This routine is called for an IORESET signal, a Programmed Master Reset, or
   in response to an SIO instruction.  It corresponds in hardware to the
   internal RST2 signal, which is generated to clear the SCMB logic in
   preparation for a new I/O program.
*/

static void sio_reset (CARD_ID card)
{
scmb [card].jump_met   = CLEAR;                         /* clear the JMPMET */
scmb [card].device_end = CLEAR;                         /*   and DEVEND flip-flops */

scmb [card].flags = 0;                                  /* clear the flags */

scmb_dib [card].interrupt_request = CLEAR;              /* clear the interrupt request flip-flop */

return;
}


/* Reset the interface logic.

   This routine is called for an IORESET signal, a Programmed Master Reset, or
   an internal CLRIL signal, which, if enabled, is generated for a condition
   that terminates an I/O program.  It corresponds in hardware to the internal
   RST3 signal.
*/

static void clear_logic (CARD_ID card)
{
scmb [card].sio_busy    = CLEAR;                        /* clear the SIO Busy flip-flop */

scmb [card].channel_sr  = CLEAR;                        /* clear the channel */
scmb [card].device_sr   = CLEAR;                        /*   and device service request flip-flops */

scmb [card].input_xfer  = CLEAR;                        /* clear the input */
scmb [card].output_xfer = CLEAR;                        /*   and output transfer flip-flops */

scmb_dib [card].interrupt_active = CLEAR;               /* clear the interrupt active flip-flop */

if (SEL_BUS (card) && ! sel_is_idle)                    /* if we're connected to the selector channel and it's busy */
    sel_assert_REQ (&scmb_dib [card]);                  /*   then abort the transfer */

return;
}


/* Increment the counter.

   Increment the counter/buffer register in response to an enabled count
   condition.  If the count rolls over, and the "terminate on terminal count"
   condition is enabled, then set the end-on-terminal-count status.
*/

static void increment_counter (CARD_ID card)
{
scmb [card].counter = scmb [card].counter + 1 & R_MASK; /* increment the counter with rollover */

if (scmb [card].counter == 0                            /* if the counter rolled over */
  && scmb [card].control_word & CN_TERM_COUNT)          /*   and termination is enabled */
    scmb [card].flags |= ST_END_COUNT;                  /*     then set the terminal count flag */

return;
}
