/* hp3000_sel.c: HP 3000 30030C Selector Channel 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.

   SEL          HP 3000 Series III Selector Channel

   08-Apr-21    JDB     Expanded bank register width from 4 to 6 bits
   01-Apr-21    JDB     Removed REG_FIT from register definitions (obsolete)
   09-Dec-19    JDB     Replaced debugging macros with tracing macros
   27-Dec-18    JDB     Revised fall through comments to comply with gcc 7
   05-Sep-17    JDB     Changed REG_A (permit any symbolic override) to REG_X
   10-Oct-16    JDB     Renumbered debug flags to start at 0
                        Added port_read_memory, port_write_memory macros
   11-Jul-16    JDB     Change "sel_unit" from a UNIT to an array of one UNIT
   30-Jun-16    JDB     Reestablish active_dib pointer during sel_initialize
   08-Jun-16    JDB     Corrected %d format to %u for unsigned values
   16-May-16    JDB     abort_channel parameter is now a pointer-to-constant
   21-Mar-16    JDB     Changed uint16 types to HP_WORD
   23-Sep-15    JDB     First release version
   27-Jan-15    JDB     Passes the selector channel diagnostic (D429A)
   10-Feb-13    JDB     Created

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


   The HP 30030C Selector Channel provides high-speed data transfer between a
   device and main memory.  While several interfaces may be connected to the
   selector channel bus, only one transfer is active at a time, and the channel
   remains dedicated to that interface until the transfer is complete.  The
   channel contains its own memory port controller, so transfers to and from
   memory bypass the I/O Processor.

   Interfaces must have additional hardware to be channel-capable, as the
   channel uses separate control and data signals from those used for direct
   I/O.  In addition, the multiplexer and selector channels differ somewhat in
   their use of the signals, so interfaces are generally designed for use with
   one or the other (the Selector Channel Maintenance Board is a notable
   exception that uses jumpers to indicate which channel to use).

   The transfer rate of the Series III selector channel is poorly documented.
   Various rates are quoted in different publications: a 1.9 MB/second rate in
   one, a 2.86 MB/second rate in another.  Main memory access time is given as
   300 nanoseconds, and the cycle time is 700 nanoseconds.

   Once started by an SIO instruction, the channel executes I/O programs
   independently of the CPU.  Program words are read, and device status is
   written back, directly via the port controller.

   32-bit I/O program words are formed from a 16-bit I/O control word (IOCW) and
   a 16-bit I/O address word (IOAW) in this general format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | C |   order   | X |       control word 1/word count           |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                 control word 2/status/address                 |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Most orders are fully decoded by bits 1-3, but a few use bit 4 to extend the
   definition where bits 4-15 are not otherwise used.  I/O programs always
   reside in memory bank 0.  The current I/O program pointer resides in word 0
   of the Device Reference Table entry for the active interface.

   The Jump and Jump Conditional orders use this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 0   0   0 | C | -   -   -   -   -   -   -   -   -   -   - |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                      jump target address                      |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   ...where C is 0 for an unconditional jump and 1 for a conditional jump.  An
   unconditional jump is handled entirely within the channel.  A conditional
   jump asserts the SETJMP signal to the interface.  If the interface returns
   JMPMET, the jump will occur; otherwise, execution continues with the next
   program word.

   The Return Residue order uses this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 0   0   1   0 | -   -   -   -   -   -   -   -   -   -   - |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                     residue of word count                     |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The remaining word count from the last transfer will be returned in the IOAW
   as a two's-complement value.  If the transfer completed normally, the
   returned value will be zero.

   The Set Bank order uses this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 0   0   1   1 | -   -   -   -   -   -   -   -   -   -   - |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | -   -   -   -   -   -   -   -   -   - | *   * : *   * : bank  |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The bank number occupies two bits in a Series II or four bits in a Series
   III, although MPE-V/R can accommodate bank numbers up to six bits in length.
   This establishes the memory bank to be used for subsequent Write or Read
   orders.  Program addresses always use bank 0.

   The Interrupt order uses this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 0   1   0 | -   -   -   -   -   -   -   -   -   -   -   - |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   - |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The SETINT signal is asserted to the interface for this order.

   The End and End with Interrupt orders use this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 0   1   1 | I | -   -   -   -   -   -   -   -   -   -   - |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                         device status                         |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   ...where I is 0 for an End and 1 for an End with Interrupt.  The PSTATSTB
   signal is asserted to the interface to obtain the device status, which is
   stored in the IOAW location.  If the I bit is set, SETINT will also be
   asserted,

   The Control order uses this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 1   0   0 |                control word 1                 |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                        control word 2                         |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Both control words are sent to the interface.  The full IOCW containing
   control word 1 is sent with the PCMD1 signal asserted.  It is followed by the
   IOAW with PCONTSTB asserted.

   The Sense order uses this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | - | 1   0   1 | -   -   -   -   -   -   -   -   -   -   -   - |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                         device status                         |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The PSTATSTB signal is asserted to the interface to obtain the device status,
   which is stored in the IOAW location.

   The Write and Read orders use these formats:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | C | 1   1   0 |         negative word count to write          |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | C | 1   1   1 |          negative word count to read          |  IOCW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                       transfer address                        |  IOAW
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The C bit is the "data chain" flag.  If it is set, then this transfer is a
   continuation of a previous Write or Read transfer.  This is used to
   circumvent the transfer size limitation inherent in the 12-bit word count
   allocated in the IOCW.  For single transfers larger than 4K words, multiple
   contiguous Write or Read orders are used, with all but the last order having
   their data chain bits set.

   In simulation, IOCW bits 1-4 are used to index into a 16-element lookup table
   to produce the final I/O order (because some of the orders define IOCW bit 4
   as "don't care", there are only thirteen distinct orders).

   Channel-capable interfaces connect via the selector channel bus and request
   channel service by asserting the CHANSR signal.  An interface may initiate an
   I/O program on the channel only if the channel is not busy with a transfer
   involving another interface.  The SIO instruction microcode tests status bit
   0 ("SIO OK") from the interface before starting the I/O program.  This bit
   reflects the SIOENABLE signal from the channel, which is true only when the
   channel is idle.  If the channel is busy, the SIO instruction sets CCG and
   returns without disturbing the channel.

   The channel uses double-buffering for both data and I/O program words,
   allowing concurrent channel/interface and channel/memory transfers.
   Prefetching of the next I/O program word increases channel speed by reducing
   the wait on memory transfers.

   In simulation, an interface is implicitly connected to the selector channel
   bus by calling the "sel_assert_REQ" routine.  This initiates the transfer
   between the device number of the interface and the channel.  The
   "service_request" field in the DIB is not used (it must be set to the
   SRNO_UNUSED value).

   The channel simulator provides these global objects:

     t_bool sel_is_idle

       TRUE if the selector channel is idle; FALSE otherwise.  Corresponds to
       the hardware SIOENABLE signal and reflects the value of the Selector
       Active flip-flop.  Used by device interfaces to qualify their SIO OK
       status bits.

     sel_assert_REQ (DIB *)

       Called by the device interface while processing a DSTARTIO signal to
       request that the selector channel begin an SIO operation, or called at
       any time while the channel is active to abort the operation.  Corresponds
       to asserting the REQ signal.  If the channel is idle, it initializes the
       channel and starts the sequencer.  If the channel is active, it aborts
       the I/O program execution and idles the channel.

     sel_assert_CHANSR (DIB *)

       Called by the device controller to request service from the selector
       channel asynchronously.  Corresponds to asserting the CHANSR signal.
       Typically called from a device service routine; device controller
       interface routines return the CHANSR signal to request service
       synchronously.  Sets the service_request flag in the DIB and sets
       sel_request.

     t_bool sel_request

       TRUE if an interface is requesting service from the selector channel or
       the channel is servicing an internal request; FALSE otherwise.
       Corresponds to the CHANSR signal received by the channel.  Used by the
       CPU to determine if a request is pending and the selector channel service
       routine should be called.

     sel_initialize (void)

       Called in the instruction execution prelude to allow devices to be
       reassigned or reset.  If a device is under channel control, the routine
       reestablishes the device number for the channel.  It also sets
       sel_request TRUE if the device has a service request pending or FALSE
       otherwise.

     sel_service (uint32)

       Called to service a request from the device interface or an internal
       request from the selector channel.  Executes one or more channel cycles
       for the associated interface.  Used by the CPU to run the selector
       channel.


   Implementation notes:

    1. In hardware, the installed memory size must be set with jumper S3 on the
       Selector Channel Register PCA.  In simulation, the size is determined by
       using the MEMSIZE macro that tests the "capac" field of the CPU unit.

    2. Multiple channels and interleaved memory operation and its associated
       configuration jumpers are not supported.

    3. The selectable trigger/freeze and error logging register features of the
       Selector Channel Control PCA are not supported.  Debug tracing may be
       enabled to determine why an I/O program aborted.

    3. The selector channel must execute more than one I/O order per CPU
       instruction in order to meet the timing requirements of the diagnostic.
       The timing is modeled by establishing a count of channel clock pulses at
       poll entry and then executing orders until the count is exhausted.  If
       the clock count was exceeded, the excess count is saved and then
       subtracted from the next entry's count, so that the typical execution
       time is preserved over a number of entries.
*/



#include "hp3000_defs.h"
#include "hp3000_cpu_ims.h"
#include "hp3000_io.h"
#include "hp3000_mem.h"



/* Program constants.

   The selector channel clock period is 175 nanoseconds.  The channel runs
   concurrently with the CPU, which executes instructions in an average of 2.57
   microseconds, so multiple cycles are executed per CPU instruction.

   In simulation, the channel is called from the instruction execution loop
   after every instruction, and sometimes additionally within instructions that
   have long execution times (e.g., MOVE).  The number of event ticks that have
   elapsed since the last call are passed to the channel; this determines the
   number of channel cycles to execute.


   Implementation notes:

    1. The number of cycles consumed by the channel for various operations are
       educated guesses.  There is no documentation available that details the
       cycle timing.

    2. In simulation, the Wait Sequence exists separately from the Transfer
       Sequence only to avoid cancelling the SR wait timer for each word
       transferred.  It is reported as a Transfer Sequence cycle.
*/

#define SR_WAIT_TIMER       mS (1000)           /* 1000 millisecond SR wait timer */

#define NS_PER_CYCLE        175                 /* each clock cycle is 175 nanoseconds */

#define CYCLES_PER_FETCH    6
#define CYCLES_PER_PREFETCH 1
#define CYCLES_PER_EXECUTE  1
#define CYCLES_PER_RELOAD   3
#define CYCLES_PER_READ     4
#define CYCLES_PER_WRITE    4

#define CYCLES_PER_EVENT    (uint32) (USEC_PER_EVENT * 1000 / NS_PER_CYCLE)

#define CNTR_MASK           0007777u            /* word counter count mask */
#define CNTR_MAX            0007777u            /* word counter maximum value */


typedef enum {                                  /* selector channel sequencer state */
    Idle_Sequence,
    Fetch_Sequence,
    Execute_Sequence,
    Wait_Sequence,
    Transfer_Sequence,
    Reload_Sequence
    } SEQ_STATE;

static const char *const seq_name [] = {        /* indexed by SEQ_STATE */
    "Idle",
    "Fetch",
    "Execute",
    "Transfer",                                 /* the wait sequence is reported as a transfer sequence */
    "Transfer",
    "Reload"
    };

static const char *const action_name [] = {     /* indexed by SEQ_STATE */
    NULL,                                       /*   no loading occurs in the Idle_Sequence */
    "loaded",                                   /*   loads occur in the Fetch_Sequence */
    "prefetched",                               /*   prefetches occur in the Execute_Sequence */
    NULL,                                       /*   no loading occurs in the Wait_Sequence */
    "prefetched",                               /*   prefetches occur in the Transfer_Sequence */
    "loaded"                                    /*   loads occur in the Reload_Sequence */
    };


/* Debug flags */

#define DEB_CSRW            (1u << 0)           /* trace channel command initiations and completions */
#define DEB_PIO             (1u << 1)           /* trace programmed I/O commands */
#define DEB_STATE           (1u << 2)           /* trace state changes */
#define DEB_SR              (1u << 3)           /* trace service requests */


/* Memory access macros */

#define port_read_memory(c,o,v)      mem_read  (&sel_dev, c, o, v)
#define port_write_memory(c,o,v)     mem_write (&sel_dev, c, o, v)


/* Channel global state */

t_bool sel_is_idle = TRUE;                      /* TRUE if the channel is idle */
t_bool sel_request = FALSE;                     /* TRUE if the channel sequencer is to be invoked */


/* Channel local state */

static SEQ_STATE sequencer = Idle_Sequence;     /* the current sequencer execution state */
static SIO_ORDER order;                         /* the current SIO order */
static DIB      *active_dib;                    /* a pointer to the participating interface's DIB */
static uint32    device_index;                  /* the index into the device table */
static t_bool    prefetch_control;              /* TRUE if the IOCW should be prefetched */
static t_bool    prefetch_address;              /* TRUE if the IOAW should be prefetched */

static uint32    device_number;                 /* the participating interface's device number */
static uint32    bank;                          /* the transfer bank register */
static uint32    word_count;                    /* the transfer word count register */

static HP_WORD   program_counter;               /* the I/O program counter */
static HP_WORD   control_word;                  /* the current IOCW */
static HP_WORD   control_buffer;                /* the prefetched IOCW */
static HP_WORD   address_word;                  /* the current IOAW */
static HP_WORD   address_buffer;                /* the prefetched IOAW */
static HP_WORD   input_buffer;                  /* the input data word buffer */
static HP_WORD   output_buffer;                 /* the output data word buffer */

static FLIP_FLOP rollover;                      /* SET if the transfer word count rolls over */
static int32     excess_cycles;                 /* the count of cycles in excess of allocation */


/* Channel local SCP support routines */

static t_stat sel_timer (UNIT   *uptr);
static t_stat sel_reset (DEVICE *dptr);

/* Channel local utility routines */

static void         end_channel       (DIB        *dibptr);
static SIGNALS_DATA abort_channel     (const char *reason);
static void         load_control      (HP_WORD    *value);
static void         load_address      (HP_WORD    *value);


/* Channel SCP data structures */


/* Unit list */

static UNIT sel_unit [] = {
    { UDATA (&sel_timer, 0, 0), SR_WAIT_TIMER }
    };

/* Register list */

static REG sel_reg [] = {
/*    Macro   Name    Location         Width  Offset        Flags       */
/*    ------  ------  ---------------  -----  ------  ----------------- */
    { FLDATA (IDLE,   sel_is_idle,              0)                      },
    { FLDATA (SREQ,   sel_request,              0)                      },
    { DRDATA (DEVNO,  device_number,     8),          PV_LEFT           },
    { DRDATA (EXCESS, excess_cycles,    32),          PV_LEFT           },
    { DRDATA (INDEX,  device_index,     32),          PV_LEFT | REG_HRO },

    { DRDATA (SEQ,    sequencer,         3)                             },
    { ORDATA (ORDER,  order,             4)                             },
    { FLDATA (ROLOVR, rollover,                 0)                      },
    { FLDATA (PFCNTL, prefetch_control,         0)                      },
    { FLDATA (PFADDR, prefetch_address,         0)                      },

    { ORDATA (BANK,   bank,              6),          PV_LEFT           },
    { DRDATA (WCOUNT, word_count,       12)                             },

    { ORDATA (PCNTR,  program_counter,  16),                            },
    { ORDATA (CNTL,   control_word,     16),                            },
    { ORDATA (CNBUF,  control_buffer,   16),                            },
    { ORDATA (ADDR,   address_word,     16),                            },
    { ORDATA (ADBUF,  address_buffer,   16),                            },
    { ORDATA (INBUF,  input_buffer,     16),          REG_X             },
    { ORDATA (OUTBUF, output_buffer,    16),          REG_X             },

    { NULL }
    };

/* Debugging trace list */

static DEBTAB sel_deb [] = {
    { "CSRW",  DEB_CSRW  },                     /* channel command initiations and completions */
    { "PIO",   DEB_PIO   },                     /* programmed I/O commands executed */
    { "STATE", DEB_STATE },                     /* channel state changes executed */
    { "SR",    DEB_SR    },                     /* service requests received */
    { "DATA",  DEB_MDATA },                     /* I/O data accesses to memory */
    { NULL,    0         }
    };

/* Device descriptor */

DEVICE sel_dev = {
    "SEL",                                      /* device name */
    sel_unit,                                   /* unit array */
    sel_reg,                                    /* register array */
    NULL,                                       /* modifier array */
    1,                                          /* number of units */
    8,                                          /* address radix */
    PA_WIDTH,                                   /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    16,                                         /* data width */
    NULL,                                       /* examine routine */
    NULL,                                       /* deposit routine */
    &sel_reset,                                 /* reset routine */
    NULL,                                       /* boot routine */
    NULL,                                       /* attach routine */
    NULL,                                       /* detach routine */
    NULL,                                       /* device information block pointer */
    DEV_DEBUG,                                  /* device flags */
    0,                                          /* debug control flags */
    sel_deb,                                    /* debug flag name array */
    NULL,                                       /* memory size change routine */
    NULL                                        /* logical device name */
    };



/* Channel global routines */



/* Initialize the channel.

   This routine is called in the CPU instruction execution prelude to allow the
   device number of the participating interface to be reassigned.  It also sets
   up the service request value from the device DIB.  This allows the device
   state to be changed during a simulation stop.

   Implementation notes:

    1. The active DIB pointer is restored from the device context to support
       resuming after a SAVE and RESTORE is performed.

    2. In simulation, we allow the device number to be changed during a
       simulation stop, so this routine must recover it from the device.
       Normally, the device number register would be reset from the device
       number field in the DIB.  However, the SCMB may be spoofing the device
       number, and it is this spoofed number that must be restored.  To do this,
       we first assert the DEVNODB signal to the interface.  The SCMB will
       respond 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, returning an outbound value of zero.  In
       this case, we use the DIB field to obtain the device number.
*/

void sel_initialize (void)
{
SIGNALS_DATA outbound;

if (sel_is_idle == FALSE) {                                         /* if the channel is controlling a device */
    active_dib = (DIB *) sim_devices [device_index]->ctxt;          /*   then restore the active DIB pointer */

    outbound = active_dib->io_interface (active_dib, DEVNODB, 0);   /* see if the device responds to DEVNODB */

    if (IODATA (outbound) > 0)                          /* if it does (e.g., the SCMB) */
        device_number = IODATA (outbound) / 4;          /*   then use the returned device number */
    else                                                /* otherwise */
        device_number = active_dib->device_number;      /*   use the device number from the DIB */

    sel_request = active_dib->service_request;          /* restore the service request state */
    }

return;
}


/* Start an I/O program.

   This routine is called by a device interface in response to a Start I/O (SIO)
   instruction to request that the selector channel begin an I/O program.  It
   corresponds in hardware to asserting the REQ signal.

   If REQ is asserted while the channel is servicing the interface, the channel
   aborts the transfer.  This occurs when an interface decides to terminate a
   transfer, for example when an error retry count has expired or a device has
   become not ready.


   Implementation notes:

    1. Both the HP 3000 Series II/III System Reference Manual and the HP 3000
       Series III Reference/Training manuals say that the selector channel
       asserts a DEVNODB signal as part of the REQ processing.  This is
       incorrect; the DEVNODB line is tied inactive by the channel, per the
       Selector Channel Control PCA schematic.  Instead, the channel expects the
       device number, multiplied by four, to be present on the SRn bus during
       REQ signal assertion, when it is loaded into the device number register.
       Selector channel devices gate their device numbers onto SR6-13 when an
       SIO instruction is decoded.
*/

void sel_assert_REQ (DIB *dibptr)
{
if (sel_is_idle) {                                      /* if the channel is idle then set it up */
    tprintf (sel_dev, DEB_CSRW, "Device number %u asserted REQ for channel initialization\n",
             dibptr->device_number);

    sel_is_idle = FALSE;                                /* the channel is now busy */
    sel_request = TRUE;                                 /* set the request flag */

    sequencer = Fetch_Sequence;                         /* initialize the sequencer */
    bank = 0;                                           /* set the bank to bank 0 */

    word_count = 0;                                     /* clear the word counter */
    rollover = CLEAR;                                   /*   and the word count rollover flip-flop */
    excess_cycles = 0;                                  /* clear the excess cycle count */

    device_index = 0;                                           /* find the device index */
                                                                /*   corresponding to */
    while ((DIB *) sim_devices [device_index]->ctxt != dibptr)  /*     the active DIB pointer */
        device_index = device_index + 1;                        /*       to aid later restoration */

    active_dib = dibptr;                                /* save the interface's DIB pointer */
    device_number = dibptr->device_number;              /*   and set the device number register */

    port_read_memory (absolute, device_number * 4,      /* read the initial program counter from the DRT */
                      &program_counter);
    }

else {                                                  /* otherwise abort the transfer in progress */
    tprintf (sel_dev, DEB_CSRW, "Device number %u asserted REQ for channel abort\n",
             device_number);

    end_channel (dibptr);                               /* idle the channel */
    sim_cancel (&sel_unit [0]);                         /*   and cancel the CHANSR timer */
    }

return;
}


/* Request channel service.

   This routine is called by a device interface to request service from the
   channel.  It is called either directly by the interface or indirectly by the
   channel in response to a CHANSR signal returned by the interface.  A direct
   call is needed for asynchronous assertion, e.g., in response to an event
   service call.  Synchronous assertion, i.e., in response to an interface call,
   is made by returning the CHANSR to the channel.  The routine corresponds in
   hardware to asserting the CHANSR signal on the selector channel bus.

   Sets the service_request flag in the DIB and sets the sel_request flag to
   cause the channel sequencer to be invoked.
*/

void sel_assert_CHANSR (DIB *dibptr)
{
tprintf (sel_dev, DEB_SR, "Device number %u asserted CHANSR\n",
         device_number);

dibptr->service_request = TRUE;                         /* set the service request flag in the interface */
sel_request = TRUE;                                     /*   and the selector request flag */

return;
}


/* Invoke the channel sequencer in response to a service request.

   This routine is called in the CPU instruction execution loop to service a
   channel request, asserted either by the participating interface, or
   generated internally by the channel.  It executes one or more channel cycles
   for the associated device interface and resets the service request flag in
   the DIB before exiting.  The routine is called after every instruction, and
   sometimes additionally within instructions that have long execution times
   (e.g., MOVE).  The number of event ticks that have elapsed since the last
   call are passed in; this determines the number of channel cycles available to
   execute.

   The selector channel clock period is 175 nanoseconds.  The channel runs
   concurrently with the CPU, which executes instructions in an average of 2.57
   microseconds, so multiple cycles are executed per CPU instruction.

   On entry, the routine executes the next state in the transfer for the
   currently participating interface.  The number of channel clock counts
   consumed for the specified state execution is subtracted from the number of
   clock counts available.  If more time remains, and the service request is
   still active, another channel cycle is run for the interface.

   The actions for the orders are:

                          Transfer  Last
     SIO Order  State       Type    Word  Signals Asserted
     ---------  --------  --------  ----  -------------------------------
     sioJUMP    Execute      --      --   (none)

     sioJUMPC   Execute      --      --   SETJMP

     sioRTRES   Execute      --      --   (none)

     sioSBANK   Execute      --      --   (none)

     sioINTRP   Execute      --      --   SETINT

     sioEND     Execute      --      --   TOGGLESIOOK | PSTATSTB

     sioENDIN   Execute      --      --   TOGGLESIOOK | PSTATSTB | SETINT

     sioCNTL    Execute      --      --   PCMD1 | CHANSO
        "       Transfer     --      --   PCONTSTB

     sioSENSE   Execute      --      --   PSTATSTB

     sioWRITE   Execute      --      --   TOGGLEOUTXFER
                Transfer   Normal    No   PWRITESTB
                Transfer   Normal   Yes   PWRITESTB | EOT
                Transfer   DEVEND    No   EOT | TOGGLEOUTXFER
                Transfer   DEVEND   Yes   (none)

     sioWRITEC  Execute      --      --   TOGGLEOUTXFER
                Transfer   Normal    No   PWRITESTB
                Transfer   Normal   Yes   PWRITESTB | EOT | TOGGLEOUTXFER
                Transfer   DEVEND    No   EOT
                Transfer   DEVEND   Yes   (none)

     sioREAD    Execute      --      --   TOGGLEINXFER | READNEXTWD
                Transfer   Normal    No   PREADSTB | READNEXTWD
                Transfer   Normal   Yes   PREADSTB | EOT | READNEXTWD
                Transfer   Devend    No   EOT | TOGGLEINXFER
                Transfer   DEVEND   Yes   (none)

     sioREADC   Execute      --      --   TOGGLEINXFER | READNEXTWD
                Transfer   Normal    No   PREADSTB | READNEXTWD
                Transfer   Normal    Yes  PREADSTB | EOT | TOGGLEINXFER
                Transfer   Devend    No   EOT
                Transfer   DEVEND    Yes  (none)

   A fundamental difference between the multiplexer and selector channels is
   that the latter needs an external service request (i.e., CHANSR assertion)
   only for operations on the interface.  All other channel operations apply an
   internal service request and so occur automatically without CHANSR being
   asserted.  In simulation, sel_request is set TRUE during channel
   initialization and is only cleared when the channel is waiting for a response
   from the interface during a control, read, or write operation.  The following
   signal assertions to the interface must return CHANSR to permit the channel
   sequencer to advance:

     - PCMD1
     - PCONTSTB
     - TOGGLEINXFER | READNEXTWD
     - PREADSTB
     - TOGGLEOUTXFER
     - PWRITESTB

   Because the channel is dedicated to an interface for the duration of a
   transfer, a non-responding interface would tie up the channel forever.  To
   prevent this, the channel starts a one-millisecond timer whenever it is
   waiting for the interface to assert CHANSR.  If the timer expires, the
   transfer is aborted, and the channel is freed.  The channel also checks for
   CHANACK in response to CHANSO assertion to the interface and will terminate
   (but not abort) the transfer if the interface fails to return it.

   To maintain the maximum transfer rate across chained read or write transfers,
   the channel will attempt to prefetch the next set of I/O Control and Address
   words during the current data transfer.  The two memory reads are interleaved
   between successive channel data transfers, but only if the input or output
   data buffers are both empty (read) or full (write), respectively.  This will
   normally occur unless the device is using all available channel bandwidth;
   the SCMB in high speed mode is an example of a device that asserts SR
   continuously.

   The selector channel diagnostic checks for the expected prefetching.  With
   the SCMB in slow mode, both the control and address prefetches will occur
   within three (read) or five (write) I/O cycles.  In fast mode, prefetches are
   locked out during a write operation, and only the control word prefetch
   occurs during a read operation.


   Implementation notes:

    1. Conceptually, the selector channel has three internal states: initiate,
       fetch, and execute.  The initiator sequence begins with a REQ
       assertion from the interface and sets up the I/O program for execution.
       The fetch sequence obtains the IOCW and IOAW.  The execute sequence
       generates the signals needed for each I/O order.

       In hardware, the Selector Channel Sequencer PCA contains several state
       machines (initiator, fetch, prefetch, buffer transfer, etc.) that provide
       substates.  In simulation, the prefetch, transfer, and reload actions are
       given separate states, rather than making them substates of the execute
       state.

    2. In hardware, a ten-microsecond CHANACK timer is started when CHANSO is
       asserted to the interface.  In simulation, a timer is not needed;
       instead, the interface must return CHANACK to avoid a CHANACK timeout
       error.

    3. In hardware, the CHANACK and CHANSR timers run on every channel cycle.
       In simulation, CHANACK is tested only during a read or write transfer,
       and CHANSR is tested only at the beginning of a read or write transfer,
       and at the beginning and end of a control transfer.  This limits overhead
       while passing the diagnostic and providing some measure of protection.

    4. In hardware, there are two input and two output buffers.  This enables
       memory transfers to overlap with interface data transfers.  In
       simulation, only one of each buffer is implemented, and memory transfers
       occur synchronously with interface transfers.

    5. In hardware, prefetches of the IOCW and IOAW are interleaved with channel
       memory accesses and only occur when the port controller is idle.  In
       simulation, prefetches are not counted against the sequencer execution
       time.  Normal fetches, by contrast, wait for memory access and so the
       time taken is counted.

    6. The default label in the Execute Sequence switch statement is necessary
       to quiet a warning that inbound_signals may be used uninitialized, even
       though all cases are covered.
*/

void sel_service (uint32 ticks_elapsed)
{
HP_WORD      inbound_data, outbound_data;
INBOUND_SET  inbound_signals;
SIGNALS_DATA outbound;
int32        cycles;
uint32       return_address;

cycles = CYCLES_PER_EVENT - excess_cycles;              /* decrease the cycles available by any left over */

while (sel_request && cycles > 0) {                     /* execute as long as a request and cycles remain */
    outbound = IORETURN (NO_SIGNALS, 0);                /* initialize in case we don't call the interface */

    tprintf (sel_dev, DEB_STATE, "Channel entered the %s sequence with %d clock cycles remaining\n",
             seq_name [sequencer], cycles);

    switch (sequencer) {                                /* dispatch based on the selector state */

        case Idle_Sequence:                             /* if the selector is idle */
            sel_request = FALSE;                        /*   then the request is invalid */
            break;


        case Fetch_Sequence:
            sim_cancel (&sel_unit [0]);                 /* cancel the CHANSR timer */

            load_control (&control_word);               /* load the IOCW */
            load_address (&address_word);               /*   and the IOAW */
            cycles = cycles - 2 * CYCLES_PER_READ       /*     and count the accesses */
                       - CYCLES_PER_FETCH;              /*       and the fetch sequence */

            order = IOCW_ORDER (control_word);          /* save the current order */

            if (control_word & IOCW_DC                          /* if the data chain bit is set */
              && order != sioREADC && order != sioWRITEC)       /*   but the order isn't a chained order */
                outbound = abort_channel ("an illegal order");  /*     then abort the channel program */

            else                                                /* otherwise the order is valid */
                sequencer = Execute_Sequence;                   /*   and execution is next */
            break;


        case Execute_Sequence:
            switch (order) {                            /* dispatch based on the I/O order */

                case sioJUMPC:
                    inbound_signals = SETJMP | CHANSO;
                    break;

                case sioRTRES:
                    inbound_signals = NO_SIGNALS;               /* no interface call is needed */

                    if (rollover == SET)                        /* if the count terminated */
                        outbound = IORETURN (NO_SIGNALS, 0);    /*   then return a zero count */
                    else                                        /* otherwise return the two's-complement remainder */
                        outbound = IORETURN (NO_SIGNALS,
                                             IOCW_COUNT (word_count));
                    break;

                case sioINTRP:
                    inbound_signals = SETINT | CHANSO;
                    break;

                case sioEND:
                    inbound_signals = TOGGLESIOOK | PSTATSTB | CHANSO;
                    break;

                case sioENDIN:
                    inbound_signals = TOGGLESIOOK | PSTATSTB | SETINT | CHANSO;
                    break;

                case sioCNTL:
                    inbound_signals = PCMD1 | CHANSO;

                    sel_request = FALSE;                /* wait until the interface requests the next word */
                    break;

                case sioSENSE:
                    inbound_signals = PSTATSTB | CHANSO;
                    break;

                case sioWRITE:
                case sioWRITEC:
                    inbound_signals = TOGGLEOUTXFER | CHANSO;

                    word_count = IOCW_WCNT (control_word);  /* load the word count */
                    sel_request = FALSE;                    /* wait until the interface requests the next word */
                    break;

                case sioREAD:
                case sioREADC:
                    inbound_signals = TOGGLEINXFER | READNEXTWD | CHANSO;

                    word_count = IOCW_WCNT (control_word);  /* load the word count */
                    sel_request = FALSE;                    /* wait until the interface requests the next word */
                    break;

                default:                                /* needed to quiet warning about inbound_signals */
                case sioJUMP:                           /* these orders do not need */
                case sioSBANK:                          /*   to call the interface */
                    inbound_signals = NO_SIGNALS;
                    break;
                }                                       /* end switch */

            if (inbound_signals) {                                  /* if there are signals to assert */
                outbound = active_dib->io_interface (active_dib,    /*   then pass them to the interface */
                                                     inbound_signals,
                                                     control_word);

                if ((outbound & CHANACK) == NO_SIGNALS) {   /* if CHANACK was not returned */
                    tprintf (sel_dev, DEB_SR, "Device number %u CHANACK timeout\n",
                             device_number);

                    end_channel (active_dib);               /* terminate the channel program */

                    tprintf (sel_dev, DEB_CSRW, "Channel program ended\n");
                    break;
                    }
                }

            switch (order) {                            /* dispatch based on the I/O order */

                case sioJUMP:
                    program_counter = address_word;     /* load the program counter with the new address */
                    sequencer = Fetch_Sequence;         /* next state is Fetch */
                    break;

                case sioJUMPC:
                    if (outbound & JMPMET)              /* if the jump condition is true */
                        program_counter = address_word; /*   then load the program counter with the new address */

                    sequencer = Fetch_Sequence;         /* the next state is Fetch */
                    break;

                case sioRTRES:
                case sioEND:
                case sioENDIN:
                case sioSENSE:
                    outbound_data = IODATA (outbound);              /* get the status or residue to return */
                    return_address = program_counter - 1 & LA_MASK; /* point at the second of the program words */

                    port_write_memory (absolute, return_address, outbound_data);    /* save the word */
                    cycles = cycles - CYCLES_PER_WRITE;                             /*   and count the access */

                    tprintf (sel_dev, DEB_PIO, "Channel stored IOAW %06o to address %06o\n",
                             outbound_data, return_address);

                    if (order == sioEND || order == sioENDIN) {     /* if it's an End or End with Interrupt order */
                        end_channel (active_dib);                   /*   then terminate the program */

                        tprintf (sel_dev, DEB_CSRW, "Channel program ended\n");
                        }

                    else                                /* otherwise the program continues */
                        sequencer = Fetch_Sequence;     /*   with the fetch state */
                    break;

                case sioSBANK:
                    bank = IOAW_BANK (address_word);    /* set the bank number register */
                    sequencer = Fetch_Sequence;         /* the next state is Fetch */
                    break;

                case sioINTRP:
                    sequencer = Fetch_Sequence;         /* the next state is Fetch */
                    break;

                case sioCNTL:
                    prefetch_control = FALSE;                   /* prefetching is not used */
                    prefetch_address = FALSE;                   /*   for the Control order */

                    sim_activate (&sel_unit [0], sel_unit [0].wait);    /* start the SR timer */
                    sequencer = Wait_Sequence;                          /*   and check for a timeout */
                    break;

                case sioWRITE:
                case sioWRITEC:
                    prefetch_control = (order == sioWRITEC);    /* enable prefetching */
                    prefetch_address = (order == sioWRITEC);    /*   if the order is chained */

                    sim_activate (&sel_unit [0], sel_unit [0].wait);    /* start the SR timer */
                    sequencer = Wait_Sequence;                          /*   and check for a timeout */
                    break;

                case sioREAD:
                case sioREADC:
                    prefetch_control = (order == sioREADC);     /* enable prefetching */
                    prefetch_address = (order == sioREADC);     /*   if the order is chained */

                    if (prefetch_control) {                     /* if control word prefetching is enabled */
                        load_control (&control_buffer);         /*   then prefetch the next IOCW into the buffer */
                        cycles = cycles - CYCLES_PER_PREFETCH;  /*     and count the sequencer time */
                        prefetch_control = FALSE;               /* mark the job done */
                        }

                    sim_activate (&sel_unit [0], sel_unit [0].wait);    /* start the SR timer */
                    sequencer = Wait_Sequence;                          /*   and check for a timeout */
                    break;
                }                                               /* end switch */

            cycles = cycles - CYCLES_PER_EXECUTE;               /* count the sequencer time */
            break;


        case Wait_Sequence:
            sim_cancel (&sel_unit [0]);                 /* cancel the SR timer */

            sequencer = Transfer_Sequence;              /* continue with the transfer sequence */

        /* fall through into the Transfer_Sequence */


        case Transfer_Sequence:
            if (order == sioCNTL) {                     /* if this is a Control order */
                inbound_data = address_word;            /*   then supply the control word */
                inbound_signals = PCONTSTB | CHANSO;    /*     to the interface */
                sel_request = FALSE;                    /* wait until the interface confirms receipt */
                }

            else if (order == sioREAD || order == sioREADC) {   /* otherwise if this is a Read or Read Chained order */
                inbound_data = 0;                               /*   then no value is needed */
                inbound_signals = PREADSTB | CHANSO;            /*     by the interface */

                if (word_count == CNTR_MAX)                     /* if the word count is exhausted */
                    if (order == sioREADC)                      /*   then if the order is chained */
                        inbound_signals |= EOT | READNEXTWD;    /*     then continue the transfer block */
                    else                                        /*   otherwise */
                        inbound_signals |= EOT | TOGGLEINXFER;  /*     end the transfer block */

                else                                            /* otherwise the transfer continues */
                    inbound_signals |= READNEXTWD;              /*   with the next word */

                sel_request = FALSE;                            /* wait until the interface confirms receipt */
                }

            else {                                                  /* otherwise it's a Write or Write Chained order */
                if (port_read_memory (dma,                          /* if the memory read */
                                      TO_PA (bank, address_word),   /*   from the specified bank and offset */
                                      &input_buffer)) {             /*     succeeds */
                    cycles = cycles - CYCLES_PER_READ;              /*       then count the access */

                    inbound_data = input_buffer;                    /* get the word to supply */
                    inbound_signals = PWRITESTB | CHANSO;           /*   to the interface */

                    if (word_count == CNTR_MAX)                     /* if the word count is exhausted */
                        if (order == sioWRITEC)                     /*   then if the order is chained */
                            inbound_signals |= EOT;                 /*     then continue the transfer block */
                        else                                        /*   otherwise */
                            inbound_signals |= EOT | TOGGLEOUTXFER; /*     end the transfer block */

                    sel_request = FALSE;                            /* wait until the interface confirms receipt */
                    }

                else {                                                  /* otherwise the memory read failed */
                    outbound = abort_channel ("a memory read error");   /*   so abort the transfer */
                    break;                                              /*     and skip the interface call */
                    }
                }

            cycles = cycles - CYCLES_PER_EXECUTE;               /* count the sequencer time */

            outbound = active_dib->io_interface (active_dib, inbound_signals, inbound_data);    /* call the interface */

            if (sel_is_idle)                            /* if the interface aborted the transfer */
                break;                                  /*   then terminate processing now */

            if ((outbound & CHANSR) == NO_SIGNALS)          /* if the interface did not assert a service request */
                if (prefetch_control) {                     /*   then if control word prefetching is enabled */
                    load_control (&control_buffer);         /*     then prefetch the next IOCW into the buffer */
                    cycles = cycles - CYCLES_PER_PREFETCH;  /*       and count the sequencer time */
                    prefetch_control = FALSE;               /* mark the job done */
                    }

                else if (prefetch_address) {                /*   otherwise if address word prefetching is enabled */
                    load_address (&address_buffer);         /*     then prefetch the next IOAW into the buffer */
                    cycles = cycles - CYCLES_PER_PREFETCH;  /*       and count the sequencer time */
                    prefetch_address = FALSE;               /* mark the job done */
                    }

            if (order == sioCNTL) {                                 /* if this is a Control order */
                sim_activate (&sel_unit [0], sel_unit [0].wait);    /*   then start the SR timer */
                sequencer = Fetch_Sequence;                         /*     and the next state is Fetch */
                }

            else {                                              /* otherwise it's a Write or Read (Chained) order */
                if (outbound & DEVEND) {                        /* if the device ended the transfer */
                    if (word_count < CNTR_MAX) {                /*   then if the transfer is incomplete */
                        inbound_signals = EOT | CHANSO;         /*     then assert EOT to end the transfer */

                        if (order == sioREAD)                   /* if the order is Read and not chained */
                            inbound_signals |= TOGGLEINXFER;    /*   then terminate the input block */

                        else if (order == sioWRITE)             /* otherwise if the order is Write and not chained */
                            inbound_signals |= TOGGLEOUTXFER;   /*   then terminate the output block */

                        active_dib->io_interface (active_dib, inbound_signals, 0);  /* tell the interface */
                        }

                    sequencer = Reload_Sequence;                /* the next state is Reload */
                    }

                else if (order == sioREAD               /* otherwise the transfer was successful */
                  || order == sioREADC) {               /*   and if this is a Read or Read Chained order */
                    output_buffer = IODATA (outbound);  /*     then pick up the returned data word */

                    if (port_write_memory (dma,                         /* if the memory write */
                                           TO_PA (bank, address_word),  /*   to the specified bank and offset */
                                           output_buffer))              /*     succeeds */
                        cycles = cycles - CYCLES_PER_WRITE;             /*       then count the access */

                    else {                                                  /* otherwise the memory write failed */
                        outbound = abort_channel ("a memory write error");  /*   so abort the transfer */
                        break;                                              /*     and skip the address and count update */
                        }
                    }

                address_word = address_word + 1 & LA_MASK;  /* increment the transfer address */
                word_count = word_count + 1 & CNTR_MASK;    /*   and the word count */

                if (word_count == 0) {                      /* if the word count is exhausted */
                    rollover = SET;                         /*   then set the rollover flip-flop */
                    sequencer = Reload_Sequence;            /*     and load the next I/O program word */
                    }
                }

            break;


        case Reload_Sequence:
            if (order == sioWRITEC || order == sioREADC) {  /* if the current order is chained */
                if (prefetch_control) {                     /*   and the IOCW has not been prefetched yet */
                    load_control (&control_buffer);         /*     then load it now */
                    cycles = cycles - CYCLES_PER_READ;      /*       and count the memory access */
                    }

                if (prefetch_address) {                     /* if the IOAW has not be prefetched yet */
                    load_address (&address_buffer);         /*   then load it now */
                    cycles = cycles - CYCLES_PER_READ;      /*       and count the memory access */
                    }

                if (prefetch_control || prefetch_address)   /* if both words had not been prefetched */
                    cycles = cycles - CYCLES_PER_FETCH;     /*   then count as a fetch sequence */
                else                                        /* otherwise */
                    cycles = cycles - CYCLES_PER_RELOAD;    /*   count as a reload sequence */

                if ((control_word ^ control_buffer) & IOCW_SIO_MASK)        /* if the next order isn't the same type */
                    outbound = abort_channel ("an invalid chained order");  /*   then an invalid order abort occurs */

                else {                                      /* otherwise the next order is OK */
                    control_word = control_buffer;          /*   so copy the control and address values */
                    address_word = address_buffer;          /*     from the buffers */

                    order = IOCW_ORDER (control_word);      /* get the new order */
                    word_count = IOCW_WCNT (control_word);  /*   and word count */

                    rollover = CLEAR;                       /* clear the word count rollover flip-flop */

                    prefetch_control = (control_word & IOCW_DC) != 0;   /* enable prefetching */
                    prefetch_address = (control_word & IOCW_DC) != 0;   /*   if the new order is chained */

                    sequencer = Transfer_Sequence;          /* the next state is Transfer */
                    }
                }

            else                                        /* otherwise an unchained order ends the transfer */
                sequencer = Fetch_Sequence;             /*   so proceed directly to Fetch */

            break;
        }                                               /* end switch */


    if (outbound & INTREQ)                              /* if an interrupt request was asserted */
        iop_assert_INTREQ (active_dib);                 /*   then set it up */

    if (sel_is_idle == FALSE)                           /* if the channel is still running */
        if (outbound & CHANSR)                          /*   then if the interface requested service */
            sel_assert_CHANSR (active_dib);             /*     then set it up */
        else                                            /*   otherwise */
            active_dib->service_request = FALSE;        /*     clear the current service request */

    else                                                /* otherwise the channel has stopped */
        sim_cancel (&sel_unit [0]);                     /*   so cancel the CHANSR timer */

    }                                                   /* end while */


if (cycles > 0)                                         /* if we exited to wait for a service request */
    excess_cycles = 0;                                  /*   then do a full set of cycles next time */
else                                                    /* otherwise we ran over our allotment */
    excess_cycles = - cycles;                           /*   so reduce the next poll by the overage */

return;
}



/* Channel local SCP support routines */



/* Service the channel service request timer.

   The CHANSR timer is started whenever the channel is waiting for a service
   request from the participating interface.  Because the selector channel is
   dedicated to a single interface until the end of the I/O program, if that
   interface were to malfunction and not respond, the channel would be tied up
   forever.

   Normally, the timer is cancelled as soon as CHANSR is returned from the
   interface.  If this service routine is entered, it means that CHANSR is
   taking too long, so the I/O program is aborted, and the channel is idled, so
   that it is available for other devices.
*/

static t_stat sel_timer (UNIT *uptr)
{
SIGNALS_DATA outbound;

outbound = abort_channel ("a CHANSR timeout");          /* abort the transfer in progress */

if (outbound & INTREQ)                                  /* if an interrupt request was asserted */
    iop_assert_INTREQ (active_dib);                     /*   then set it up */

return SCPE_OK;
}


/* Device reset.

   This routine is called for a RESET or RESET SEL 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 the internal Clear Logic signal.

   A reset does not clear any of the registers.
*/

static t_stat sel_reset (DEVICE *dptr)
{
rollover = CLEAR;                                       /* clear the word count rollover flip-flop */

sel_is_idle = TRUE;                                     /* the channel is now inactive */
sel_request = FALSE;                                    /* clear the request flag */

sequencer = Idle_Sequence;                              /* stop the sequencer */

return SCPE_OK;
}



/* Channel local utility routines */



/* End the channel I/O program.

   The channel program ends, either normally via an sioEND or sioENDIN order, or
   abnormally via a REQ or timeout abort.  The program counter is written back
   to the DRT, and the channel is idled by performing a Clear Logic operation.


   Implementation notes:

    1. The memory write cycle time need not be counted, as the channel will be
       terminating unconditionally.
*/

static void end_channel (DIB *dibptr)
{
port_write_memory (absolute, device_number * 4,         /* write the program counter back to the DRT */
                   program_counter);

dibptr->service_request = FALSE;                        /* clear any outstanding device service request */

sel_reset (&sel_dev);                                   /* perform a Clear Logic operation */

return;
}


/* Abort the transfer in progress.

   If an internal channel error occurs (e.g., a memory read or write failure,
   due to an invalid address), the channel asserts the XFERERROR signal to the
   interface.  The interface will clear its logic and assert REQ to the channel
   to complete the abort.
*/

static SIGNALS_DATA abort_channel (const char *reason)
{
tprintf (sel_dev, DEB_CSRW, "Channel asserted XFERERROR for %s\n",
         reason);

return active_dib->io_interface (active_dib, XFERERROR | CHANSO, 0);    /* tell the interface that the channel has aborted */
}


/* Load the I/O Control Word.

   The first of the two I/O program words is loaded into the channel register
   pointed to by "value".  The program counter points at the location to read
   and is incremented after retrieving the value.  This routine is called both
   for a normal fetch and for a prefetch.
*/

static void load_control (HP_WORD *value)
{
port_read_memory (absolute, program_counter, value);    /* read the IOCW from memory */

tprintf (sel_dev, DEB_PIO, "Channel %s IOCW %06o (%s) from address %06o\n",
         action_name [sequencer], *value,
         sio_order_name [IOCW_ORDER (*value)], program_counter);

program_counter = program_counter + 1 & LA_MASK;        /* increment the program counter */

return;
}


/* Load the I/O Address Word.

   The second of the two I/O program words is loaded into the channel register
   pointed to by "value".  The program counter points at the location to read
   and is incremented after retrieving the value.  This routine is called both
   for a normal fetch and for a prefetch.
*/

static void load_address (HP_WORD *value)
{
port_read_memory (absolute, program_counter, value);    /* read the IOAW from memory */

tprintf (sel_dev, DEB_PIO, "Channel %s IOAW %06o from address %06o\n",
         action_name [sequencer], *value, program_counter);

program_counter = program_counter + 1 & LA_MASK;        /* increment the program counter */

return;
}
