/* i7094_io.c: IBM 7094 I/O subsystem (channels)

   Copyright (c) 2003-2006, Robert M. Supnik

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

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

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   ROBERT M SUPNIK 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 Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   chana..chanh         I/O channels

   Notes on channels and CTSS.

   - CTSS B-core is supported by the addition of a 16th bit to the current
     address field of the channel command.  Both the channel location counter
     and the channel current address register are widened to 16b.  Thus,
     channel programs can run in B-core, and channel transfers can access B-core.
     CTSS assumes that a channel command which starts a transfer in B-core
     will not access A-core; the 16th bit does not increment.
   - The channel start commands (RCHx and LCHx) incorporate the A-core/B-core
     select as part of effective address generation.  CTSS does not relocate
     RCHx and LCHx target addresses; because the relocation indicator is
     always zero, it's impossible to tell whether the protection indicator
     affects address generation.
   - The CTSS protection RPQ does not cover channel operations.  Thus, CTSS
     must inspect and vet all channel programs initiated by user mode programs,
     notably the background processor FMS.  CTSS inspects in-progress 7607
     channel programs to make sure than either the nostore bit or the B-core
     bit is set; thus, SCHx must store all 16b of the current address.
*/

#include "i7094_defs.h"

#define CHAMASK         ((cpu_model & I_CT)? PAMASK: AMASK) /* chan addr mask */
#define CHAINC(x)       (((x) & ~AMASK) | (((x) + 1) & AMASK))

typedef struct {
    char        *name;
    uint32      flags;
    } DEV_CHAR;

uint32 ch_sta[NUM_CHAN];                                /* channel state */
uint32 ch_dso[NUM_CHAN];                                /* data select op */
uint32 ch_dsu[NUM_CHAN];                                /* data select unit */
uint32 ch_ndso[NUM_CHAN];                               /* non-data select op */
uint32 ch_ndsu[NUM_CHAN];                               /* non-data select unit */
uint32 ch_flags[NUM_CHAN];                              /* flags */
uint32 ch_clc[NUM_CHAN];                                /* chan loc ctr */
uint32 ch_op[NUM_CHAN];                                 /* channel op */
uint32 ch_wc[NUM_CHAN];                                 /* word count */
uint32 ch_ca[NUM_CHAN];                                 /* core address */
uint32 ch_lcc[NUM_CHAN];                                /* control cntr (7909) */
uint32 ch_cnd[NUM_CHAN];                                /* cond reg (7909) */
uint32 ch_sms[NUM_CHAN];                                /* cond mask reg (7909) */
t_uint64 ch_ar[NUM_CHAN];                               /* assembly register */
uint32 ch_idf[NUM_CHAN];                                /* channel input data flags */
DEVICE *ch2dev[NUM_CHAN] = { NULL };
uint32 ch_tpoll = 5;                                    /* channel poll */

extern t_uint64 *M;
extern uint32 cpu_model, data_base;
extern uint32 hst_ch;
extern uint32 ch_req;
extern uint32 chtr_inht, chtr_inhi, chtr_enab;
extern uint32 ind_ioc;
extern uint32 chtr_clk;
extern DEVICE cdr_dev, cdp_dev;
extern DEVICE lpt_dev;
extern DEVICE mt_dev[NUM_CHAN];
extern DEVICE drm_dev;
extern DEVICE dsk_dev;
extern DEVICE com_dev;
extern int32 sim_brk_summ;

t_stat ch_reset (DEVICE *dptr);
t_stat ch6_svc (UNIT *uptr);
t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc);
DEVICE *ch_find_dev (uint32 ch, uint32 unit);
t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta);
t_bool ch6_rd_putw (uint32 ch);
t_stat ch6_wr_getw (uint32 ch, t_bool eorz);
t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld);
t_stat ch6_ioxt (uint32 ch);
void ch6_iosp_cclr (uint32 ch);
t_stat ch9_new_cmd (uint32 ch);
t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir);
t_stat ch9_sel (uint32 ch, uint32 sel);
t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl);
t_stat ch9_rd_putw (uint32 ch);
t_stat ch9_wr_getw (uint32 ch);
void ch9_eval_int (uint32 ch, uint32 iflags);
DEVICE *ch_map_flags (uint32 ch, int32 fl);

extern CTAB *sim_vm_cmd;
extern t_stat ch_bkpt (uint32 ch, uint32 clc);

const uint32 col_masks[12] = {                          /* row 9,8,..,0,11,12 */
    00001, 00002, 00004,
    00010, 00020, 00040,
    00100, 00200, 00400,
    01000, 02000, 04000
    };

const t_uint64 bit_masks[36] = {
    0000000000001, 0000000000002, 0000000000004,
    0000000000010, 0000000000020, 0000000000040,
    0000000000100, 0000000000200, 0000000000400,
    0000000001000, 0000000002000, 0000000004000,
    0000000010000, 0000000020000, 0000000040000,
    0000000100000, 0000000200000, 0000000400000,
    0000001000000, 0000002000000, 0000004000000,
    0000010000000, 0000020000000, 0000040000000,
    0000100000000, 0000200000000, 0000400000000,
    0001000000000, 0002000000000, 0004000000000,
    0010000000000, 0020000000000, 0040000000000,
    0100000000000, 0200000000000, 0400000000000
    };

const DEV_CHAR dev_table[] = {
    { "729", 0 },
    { "TAPE", 0 },
    { "7289", DEV_7289 },
    { "DRUM", DEV_7289 },
    { "7631", DEV_7909|DEV_7631 },
    { "FILE", DEV_7909|DEV_7631 },
    { "7750", DEV_7909|DEV_7750 },
    { "COMM", DEV_7909|DEV_7750 },
    { NULL },
    };

const char *sel_name[] = {
    "UNK", "RDS", "WRS", "SNS", "CTL", "FMT", "UNK", "UNK",
    "WEF", "WBT", "BSR", "BSF", "REW", "RUN", "SDN", "UNK"
    };

/* Channel data structures */

UNIT ch_unit[NUM_CHAN] = {
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) },
    { UDATA (&ch6_svc, 0, 0) }
    };

MTAB ch_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL,
      NULL, &ch_show_type, NULL },
    { MTAB_XTD|MTAB_VDV, 0, NULL, "ENABLED",
      &ch_set_enable, NULL, NULL },
    { MTAB_XTD|MTAB_VDV, 0, NULL, "DISABLED",
      &ch_set_disable, NULL, NULL },
    { 0 }
    };

REG cha_reg[] = {
    { ORDATA (STA, ch_sta[CH_A], 8) },
    { ORDATA (DSC, ch_dso[CH_A], 4) },
    { ORDATA (DSU, ch_dsu[CH_A], 9) },
    { ORDATA (NDSC, ch_ndso[CH_A], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_A], 9) },
    { ORDATA (FLAGS, ch_flags[CH_A], 30) },
    { ORDATA (IDF, ch_idf[CH_A], 2) },
    { ORDATA (OP, ch_op[CH_A], 5) },
    { ORDATA (CLC, ch_clc[CH_A], 16) },
    { ORDATA (WC, ch_wc[CH_A], 15) },
    { ORDATA (CA, ch_ca[CH_A], 16) },
    { ORDATA (AR, ch_ar[CH_A], 36) },
    { ORDATA (CND, ch_cnd[CH_A], 6), REG_HRO },
    { ORDATA (LCC, ch_lcc[CH_A], 6), REG_HRO },
    { ORDATA (SMS, ch_sms[CH_A], 7), REG_HRO },
    { 0 }
    };

REG chb_reg[] = {
    { ORDATA (STATE, ch_sta[CH_B], 8) },
    { ORDATA (DSC, ch_dso[CH_B], 4) },
    { ORDATA (DSU, ch_dsu[CH_B], 9) },
    { ORDATA (NDSC, ch_ndso[CH_B], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_B], 9) },
    { ORDATA (FLAGS, ch_flags[CH_B], 30) },
    { ORDATA (IDF, ch_idf[CH_B], 2) },
    { ORDATA (OP, ch_op[CH_B], 5) },
    { ORDATA (CLC, ch_clc[CH_B], 16) },
    { ORDATA (WC, ch_wc[CH_B], 15) },
    { ORDATA (CA, ch_ca[CH_B], 16) },
    { ORDATA (AR, ch_ar[CH_B], 36) },
    { ORDATA (CND, ch_cnd[CH_B], 6) },
    { ORDATA (LCC, ch_lcc[CH_B], 6) },
    { ORDATA (SMS, ch_sms[CH_B], 7) },
    { 0 }
    };

REG chc_reg[] = {
    { ORDATA (STATE, ch_sta[CH_C], 8) },
    { ORDATA (DSC, ch_dso[CH_C], 4) },
    { ORDATA (DSU, ch_dsu[CH_C], 9) },
    { ORDATA (NDSC, ch_ndso[CH_C], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_C], 9) },
    { ORDATA (FLAGS, ch_flags[CH_C], 30) },
    { ORDATA (IDF, ch_idf[CH_C], 2) },
    { ORDATA (OP, ch_op[CH_C], 5) },
    { ORDATA (CLC, ch_clc[CH_C], 16) },
    { ORDATA (WC, ch_wc[CH_C], 15) },
    { ORDATA (CA, ch_ca[CH_C], 16) },
    { ORDATA (AR, ch_ar[CH_C], 36) },
    { ORDATA (CND, ch_cnd[CH_C], 6) },
    { ORDATA (LCC, ch_lcc[CH_C], 6) },
    { ORDATA (SMS, ch_sms[CH_C], 7) },
    { 0 }
    };

REG chd_reg[] = {
    { ORDATA (STATE, ch_sta[CH_D], 8) },
    { ORDATA (DSC, ch_dso[CH_D], 4) },
    { ORDATA (DSU, ch_dsu[CH_D], 9) },
    { ORDATA (NDSC, ch_ndso[CH_D], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_D], 9) },
    { ORDATA (FLAGS, ch_flags[CH_D], 30) },
    { ORDATA (IDF, ch_idf[CH_D], 2) },
    { ORDATA (OP, ch_op[CH_D], 5) },
    { ORDATA (CLC, ch_clc[CH_D], 16) },
    { ORDATA (WC, ch_wc[CH_D], 15) },
    { ORDATA (CA, ch_ca[CH_D], 16) },
    { ORDATA (AR, ch_ar[CH_D], 36) },
    { ORDATA (CND, ch_cnd[CH_D], 6) },
    { ORDATA (LCC, ch_lcc[CH_D], 6) },
    { ORDATA (SMS, ch_sms[CH_D], 7) },
    { 0 }
    };

REG che_reg[] = {
    { ORDATA (STATE, ch_sta[CH_E], 8) },
    { ORDATA (DSC, ch_dso[CH_E], 4) },
    { ORDATA (DSU, ch_dsu[CH_E], 9) },
    { ORDATA (NDSC, ch_ndso[CH_E], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_E], 9) },
    { ORDATA (FLAGS, ch_flags[CH_E], 30) },
    { ORDATA (IDF, ch_idf[CH_E], 2) },
    { ORDATA (OP, ch_op[CH_E], 5) },
    { ORDATA (CLC, ch_clc[CH_E], 16) },
    { ORDATA (WC, ch_wc[CH_E], 15) },
    { ORDATA (CA, ch_ca[CH_E], 16) },
    { ORDATA (AR, ch_ar[CH_E], 36) },
    { ORDATA (CND, ch_cnd[CH_E], 6) },
    { ORDATA (LCC, ch_lcc[CH_E], 6) },
    { ORDATA (SMS, ch_sms[CH_E], 7) },
    { 0 }
    };

REG chf_reg[] = {
    { ORDATA (STATE, ch_sta[CH_F], 8) },
    { ORDATA (DSC, ch_dso[CH_F], 4) },
    { ORDATA (DSU, ch_dsu[CH_F], 9) },
    { ORDATA (NDSC, ch_ndso[CH_F], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_F], 9) },
    { ORDATA (FLAGS, ch_flags[CH_F], 30) },
    { ORDATA (IDF, ch_idf[CH_F], 2) },
    { ORDATA (OP, ch_op[CH_F], 5) },
    { ORDATA (CLC, ch_clc[CH_F], 16) },
    { ORDATA (WC, ch_wc[CH_F], 15) },
    { ORDATA (CA, ch_ca[CH_F], 16) },
    { ORDATA (AR, ch_ar[CH_F], 36) },
    { ORDATA (CND, ch_cnd[CH_F], 6) },
    { ORDATA (LCC, ch_lcc[CH_F], 6) },
    { ORDATA (SMS, ch_sms[CH_F], 7) },
    { 0 }
    };

REG chg_reg[] = {
    { ORDATA (STATE, ch_sta[CH_G], 8) },
    { ORDATA (DSC, ch_dso[CH_G], 4) },
    { ORDATA (DSU, ch_dsu[CH_G], 9) },
    { ORDATA (NDSC, ch_ndso[CH_G], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_G], 9) },
    { ORDATA (FLAGS, ch_flags[CH_G], 30) },
    { ORDATA (IDF, ch_idf[CH_G], 2) },
    { ORDATA (OP, ch_op[CH_G], 5) },
    { ORDATA (CLC, ch_clc[CH_G], 16) },
    { ORDATA (WC, ch_wc[CH_G], 15) },
    { ORDATA (CA, ch_ca[CH_G], 16) },
    { ORDATA (AR, ch_ar[CH_G], 36) },
    { ORDATA (CND, ch_cnd[CH_G], 6) },
    { ORDATA (LCC, ch_lcc[CH_G], 6) },
    { ORDATA (SMS, ch_sms[CH_G], 7) },
    { 0 }
    };

REG chh_reg[] = {
    { ORDATA (STATE, ch_sta[CH_H], 8) },
    { ORDATA (DSC, ch_dso[CH_H], 4) },
    { ORDATA (DSU, ch_dsu[CH_H], 9) },
    { ORDATA (NDSC, ch_ndso[CH_H], 4) },
    { ORDATA (NDSU, ch_ndsu[CH_H],9) },
    { ORDATA (FLAGS, ch_flags[CH_H], 30) },
    { ORDATA (IDF, ch_idf[CH_H], 2) },
    { ORDATA (OP, ch_op[CH_H], 5) },
    { ORDATA (CLC, ch_clc[CH_H], 16) },
    { ORDATA (WC, ch_wc[CH_H], 15) },
    { ORDATA (CA, ch_ca[CH_H], 16) },
    { ORDATA (AR, ch_ar[CH_H], 36) },
    { ORDATA (CND, ch_cnd[CH_H], 6) },
    { ORDATA (LCC, ch_lcc[CH_H], 6) },
    { ORDATA (SMS, ch_sms[CH_H], 7) },
    { 0 }
    };

DEVICE ch_dev[NUM_CHAN] = {
    {
    "CHANA", &ch_unit[CH_A], cha_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, 0
    },
    {
    "CHANB", &ch_unit[CH_B], chb_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    },
    {
    "CHANC", &ch_unit[CH_C], chc_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    },
    {
    "CHAND", &ch_unit[CH_D], chd_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    },
    {
    "CHANE", &ch_unit[CH_E], che_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    },
    {
    "CHANF", &ch_unit[CH_F], chf_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    },
    {
    "CHANG", &ch_unit[CH_G], chg_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    },
    {
    "CHANH", &ch_unit[CH_H], chh_reg, ch_mod,
    1, 8, 8, 1, 8, 8,
    NULL, NULL, &ch_reset,
    NULL, NULL, NULL,
    NULL, DEV_DISABLE | DEV_DIS
    }
    };

/* 7607 channel overview

   Channel variables:

        ch_sta           channel state
        ch_dso, ch_dsu   operation and unit for current data select
        ch_ndso, ch_ndsu operation and unit for current non-data select
        ch_clc           current location counter
        ch_ca            memory addres
        ch_wc            word count
        ch_op            channel opcode (bits <S,1:2,19>)
        ch_flags         channel flags

   States of a channel

   IDLE - channel is not in operation

        RDS, WDS:     -> DSW if device is idle, schedule device
                         device timeout drives next transition
                      -> stall if device is busy
                         repeat until device is idle
        other I/O:    -> NDS if device is idle, schedule device
                         device timeout drives next transition
                      -> stall if device is busy
                         repeat until device is idle
        chan reset:   -> IDLE   

   PDS (PNDS) - channel is polling device to start data (non-data) select

        chan timeout: -> DSW (NDS) if device is idle
                         device timeout drives next transition
                      -> no change if device is busy, schedule channel
        chan reset:   -> IDLE   
        
   DSW - channel is waiting for channel start command
   
        dev timeout:  -> IDLE if no stacked non-data select
                      -> PNDS if stacked non-data select
                         channel timeout drives next transition
        start chan:   -> DSX if chan program transfers data
                         device timeout drives next transition
                      -> IDLE if channel disconnects, no stacked NDS
                      -> PNDS if channel disconnects, stacked NDS
                         channel timeout drives next transition
        chan reset:   -> IDLE   
                     
   DSX - channel is executing data select
   
        dev timeout:  -> DSX if transfer not complete, reschedule device
                         device timeout drives next transition
                      -> DSW if channel command completes, CHF_LDW set
                      -> IDLE if transfer complete, no stacked NDS, or
                         if channel command completes, CHF_LDW clear
                      -> PNDS if channel disconnects, stacked NDS
                         channel timeout drives next transition
        start chan:   -> DSX with CHF_LDW, CPU stall
        chan reset:   -> IDLE   

   NDS - channel is executing non-data select
   
        dev timeout:  -> IDLE if transfer complete, no stacked DS
                      -> PDS if channel disconnects, stacked DS
                         channel timeout drives next transition
        chan reset:   -> IDLE

   The channel has two interfaces to a device. The select routine:

        dev_select (uint32 ch, uint32 sel, uint32 unit)

   Returns can include device errors and ERR_STALL.  If ERR_STALL, the
   device is busy.  For I/O instructions, ERR_STALL stalls execution of
   the instruction until the device is not busy.  For stacked command
   polls, ERR_STALL causes the poll to be repeated after a delay.

   The device write routine is used to place output data in the device
   write buffer.

   Channel transfers are driven by the channel.  When a device needs to
   read or write data, it sets a channel request in ch_req.  The channel
   process transfers the data and updates channel control parameters
   accordingly.  Note that the channel may disconnect; in this case, the
   transfer completes 'correctly' from the point of view of the device.

   The channel transfer commands (IOxT) require the channel to 'hold'
   a new channel command in anticipation of the current transfer.  If
   the channel is currently executing (CH6S_DSX) and a channel start
   is issued by the CPU, a 'start pending' flag is set and the CPU is
   stalled.  When the channel reaches the end of an IOxT command, it
   checks the 'start pending' flag.  If the flag is set, the channel
   sets itself to waiting and then requeues itself for one cycle later.
   The CPU tries the channel start, sees that the channel is waiting,
   and issues the new channel command.

   state        op              device                  channel

   IDLE         RDS,WDS         start I/O               ->DSW

   DSW          LCHx            (timed wait)            ->DSX

   DSX          --              timeout, req svc
                                (timed wait)            transfer word
                                timeout, req svc
                                (timed wait)
                LCHx, stalls            :
                                timeout, EOR/EOC        IOxT: ->DSW, resched
   DSW          LCHx            (timed wait)            ->DSX, etc              

   7909 channel overview

   Channel variables:

        ch_sta          channel state
        ch_clc          current location counter
        ch_ca           memory addres
        ch_wc           word count
        ch_op           channel opcode (bits <S,1:3,19>)
        ch_sms          status mask
        ch_cond         interrupt conditions
        ch_lcc          control counter
        ch_flags        channel flags

   States of a channel

   IDLE - channel is not in operation

        RDCx, SDCx, interrupt -> DSX

   DSX - channel is executing data select

        TWT, WTR -> IDLE

   The 7909 is more capable than the 7607 but also simpler in some ways.
   It has many more instructions, built in counters and status checking,
   and interrupts.  But it has only two states and no concept of records.

   The 7909 read process is driven by the device:

        channel CTLR/SNS: send select
        device: schedule timeout
        device timeout: device to AR, request channel
            channel: AR to memory
        device timeout: device to AR, request channel
            channel: AR to memory
        :
        device timeout: set end, request channel
            channel: disconnect on CPYD, send STOP

   The 7909 write process is also driven by the device:

        channel CTL/CTLW: send select
        device: schedule timeout, request channel
        channel: memory to output buffer
            device timeout: output buffer to device, request channel
        channel: memory to output buffer
            device timeout: output buffer to device, request channel
        :
        channel: memory to output buffer
            device timeout: output buffer to device, set end, request channel
        channel: disconnect on CPYD, send STOP

    For both reads and writes, devices must implement an 'interblock' or
    'interrecord' state that is long enough for the channel to see the 
    end, disconnect, and send a stop signal.
*/

/* Data select - called by RDS or WDS instructions - 7607/7289 only

   - Channel is from address and has been corrected
   - Channel must be an enabled 7607
   - If data select already in use, stall CPU
   - If non-data select is a write end-of-file, stall CPU
   - If channel is busy, stack command
   - Otherwise, start IO, set channel to waiting */

t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit)
{
t_stat r;

if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid arg? */
if (ch_dev[ch].flags & DEV_DIS) return STOP_NXCHN;      /* disabled? stop */
if (ch_dev[ch].flags & DEV_7909) return STOP_7909;      /* 7909? stop */
if (ch_dso[ch]) return ERR_STALL;                       /* DS in use? */
if (ch_ndso[ch] == CHSL_WEF) return ERR_STALL;          /* NDS = WEF? */
if (ch_sta[ch] == CHXS_IDLE) {                          /* chan idle? */
    r = ch6_sel (ch, ds, unit, CH6S_DSW);               /* select device */
    if (r != SCPE_OK) return r;
    }
ch_dso[ch] = ds;                                        /* set command, unit */
ch_dsu[ch] = unit;
ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_CMD);             /* clear flags */
ch_idf[ch] = 0;
return SCPE_OK;
}

/* Non-data select - called by BSR, BSF, WEF, REW, RUN, SDS instructions - 7607 only

   - Channel is from address and has been corrected
   - Channel must be an enabled 7607
   - If non-data select already in use, stall CPU
   - If data select is card or printer, stall CPU
   - If channel is busy, stack command
   - Otherwise, start IO, set channel to waiting */

t_stat ch_op_nds (uint32 ch, uint32 nds, uint32 unit)
{
DEVICE *dptr;
t_stat r;

if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid arg? */
if (ch_dev[ch].flags & DEV_DIS) return STOP_NXCHN;      /* disabled? stop */
if (ch_dev[ch].flags & DEV_7909) return STOP_7909;      /* 7909? stop */
if (ch_ndso[ch]) return ERR_STALL;                      /* NDS in use? */
if (ch_dso[ch] && (dptr = ch_find_dev (ch, ch_dsu[ch])) /* DS, cd or lpt? */
    && (dptr->flags & DEV_CDLP)) return ERR_STALL;
if (ch_sta[ch] == CHXS_IDLE) {                          /* chan idle? */
    r = ch6_sel (ch, nds, unit, CH6S_NDS);              /* select device */
    if (r != SCPE_OK) return r;
    }
ch_ndso[ch] = nds;                                      /* set command, unit */
ch_ndsu[ch] = unit;
return SCPE_OK;
}

/* End of data select - called from channel - 7607/7289 only

   - If executing, set command trap flag
   - Set channel idle
   - If stacked nds, set up immediate channel timeout */

t_stat ch6_end_ds (uint32 ch)
{
if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid arg? */
ch_dso[ch] = ch_dsu[ch] = 0;                            /* no data select */
if (ch_ndso[ch]) {                                      /* stacked non-data sel? */
    sim_activate (ch_dev[ch].units, 0);                 /* immediate poll */
    ch_sta[ch] = CH6S_PNDS;                             /* state = polling */
    }
else ch_sta[ch] = CHXS_IDLE;                            /* else state = idle */
return SCPE_OK;
}

/* End of non-data select - called from I/O device completion - 7607/7289 only

   - Set channel idle
   - If stacked ds, set up immediate channel timeout */

t_stat ch6_end_nds (uint32 ch)
{
if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid arg? */
ch_ndso[ch] = ch_ndsu[ch] = 0;                          /* no non-data select */
if (ch_dso[ch]) {                                       /* stacked data sel? */
    sim_activate (ch_dev[ch].units, 0);                 /* immediate poll */
    ch_sta[ch] = CH6S_PDS;                              /* state = polling */
    }
else ch_sta[ch] = CHXS_IDLE;                            /* else state = idle */
return SCPE_OK;
}

/* Send select to device - 7607/7289 only */

t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta)
{
DEVICE *dptr;
DIB *dibp;
t_stat r;

if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid arg? */
dptr = ch_find_dev (ch, unit);                          /* find device */
if (dptr == NULL) return STOP_NXDEV;                    /* invalid device? */
dibp = (DIB *) dptr->ctxt;
r = dibp->chsel (ch, sel, unit);                        /* select device */
if (r == SCPE_OK) ch_sta[ch] = sta;                     /* set status */
return r;
}

/* Channel unit service - called to start stacked command - 7607 only */

t_stat ch6_svc (UNIT *uptr)
{
uint32 ch = uptr - &ch_unit[0];                         /* get channel */
t_stat r;

if (ch >= NUM_CHAN) return SCPE_IERR;                   /* invalid chan? */
switch (ch_sta[ch]) {                                   /* case on state */

    case CH6S_PDS:                                      /* polling for ds */
        r = ch6_sel (ch, ch_dso[ch], ch_dsu[ch], CH6S_DSW);
        break;

    case CH6S_PNDS:                                     /* polling for nds */
        r = ch6_sel (ch, ch_ndso[ch], ch_ndsu[ch], CH6S_NDS);
        break;

    default:
        return SCPE_OK;
        }

if (r == ERR_STALL) {                                   /* stalled? */
    sim_activate (uptr, ch_tpoll);                      /* continue poll */
    return SCPE_OK;
    }
return r;
}

/* Map channel and unit number to device - all channels */

DEVICE *ch_find_dev (uint32 ch, uint32 unit)
{
if (ch >= NUM_CHAN) return NULL;                        /* invalid arg? */
if (ch_dev[ch].flags & (DEV_7909|DEV_7289)) return ch2dev[ch];
unit = unit & 0777;
if (((unit >= U_MTBCD) && (unit <= (U_MTBCD + MT_NUMDR))) ||
    ((unit >= U_MTBIN) && (unit <= (U_MTBIN + MT_NUMDR))))
    return ch2dev[ch];
if (ch != 0) return NULL;
if (unit == U_CDR) return &cdr_dev;
if (unit == U_CDP) return &cdp_dev;
if ((unit == U_LPBCD) || (unit == U_LPBIN)) return &lpt_dev;
return NULL;
}

/* Start channel - channel is from opcode

   7607: channel should have a data select operation pending (DSW state)
   7909: channel should be idle (IDLE state) */

t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset)
{
t_uint64 ir;
t_stat r;

clc = clc | data_base;                                  /* add A/B select */
if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid argument? */
if (ch_dev[ch].flags & DEV_DIS) return STOP_NXCHN;      /* disabled? stop */
if (ch_dev[ch].flags & DEV_7909) {                      /* 7909? */
    if (ch_sta[ch] != CHXS_IDLE) return ERR_STALL;      /* must be idle */
    if (reset) {                                        /* RDCx? */
        ch_cnd[ch] = 0;                                 /* clear conditions */
        ch_clc[ch] = clc;                               /* set clc */
        }
    else {                                              /* SDCx */
        if (BIT_TST (chtr_enab, CHTR_V_TWT + ch) &&     /* pending trap? */
            (ch_flags[ch] & CHF_TWT)) return ERR_STALL;
        ch_clc[ch] = ch_ca[ch] & CHAMASK;               /* finish WTR, TWT */
        }
    ch_flags[ch] &= ~CHF_CLR_7909;                      /* clear flags, not IP */
    ch_idf[ch] = 0;
    ch_sta[ch] = CHXS_DSX;                              /* set state */
    return ch9_new_cmd (ch);                            /* start executing */
    }
                                                        /* 7607, 7289 */
if (reset) {                                            /* reset? */
    if (ch_sta[ch] == CHXS_DSX) ch_sta[ch] = CH6S_DSW;
    ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_TRC|CHF_CMD);
    ch_idf[ch] = 0;
    }

switch (ch_sta[ch]) {                                   /* case on chan state */

    case CHXS_IDLE:                                     /* idle */
        ind_ioc = 1;                                    /* IO check */
        ir = ReadP (clc);                               /* get chan word */
        ch_clc[ch] = CHAINC (clc);                      /* incr chan pc */
        ch_wc[ch] = GET_DEC (ir);                       /* get word cnt */
        ch_ca[ch] = ((uint32) ir) & CHAMASK;            /* get address */
        ch_op[ch] = (GET_OPD (ir) << 1) |               /* get opcode */
            ((((uint32) ir) & CH6I_NST)? 1: 0);         /* plus 'no store' */
        break;

    case CH6S_PNDS:                                     /* NDS polling */
    case CH6S_PDS:                                      /* DS polling */
    case CH6S_NDS:                                      /* NDS executing */
        return ERR_STALL;                               /* wait it out */

    case CH6S_DSW:                                      /* expecting command */
        ch_sta[ch] = CHXS_DSX;                          /* update state */
        if (ch_dev[ch].flags & DEV_7289) {              /* drum channel? */
            ir = ReadP (clc);                           /* read addr */
            ch_clc[ch] = CHAINC (clc);                  /* incr chan pc */
            if (r = ch9_wr (ch, ir, 0)) return r;       /* write to dev */
            }
        else ch_clc[ch] = clc;                          /* set clc */
        return ch6_new_cmd (ch, TRUE);                  /* start channel */

    case CHXS_DSX:                                      /* executing */
        ch_flags[ch] = ch_flags[ch] | CHF_LDW;          /* flag pending LCH */
        return ERR_STALL;                               /* stall */
        }

return SCPE_OK;
}

/* Store channel 

   7607/7289 stores op,ca,nostore,clc
   7909 stores clc,,ca */

t_stat ch_op_store (uint32 ch, t_uint64 *dat)
{
if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS)) return STOP_NXCHN;
if (ch_dev[ch].flags & DEV_7909)
    *dat = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) |
        (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_ADDR);
else *dat = (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_DEC) |
    (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_ADDR) |
    (((t_uint64) (ch_op[ch] & 1)) << 16) |
    (((t_uint64) (ch_op[ch] & 016)) << 32);
return SCPE_OK;
}

/* Store channel diagnostic 

   7607 is undefined
   7289 stores IOC+???
   7909 stores 7909 lcc+flags */

t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat)
{
if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS)) return STOP_NXCHN;
if (ch_flags[ch] & DEV_7289) *dat = ind_ioc? SIGN: 0;
else if (ch_flags[ch] & DEV_7909) *dat =
    (((t_uint64) (ch_lcc[ch] & CHF_M_LCC)) << CHF_V_LCC) | 
    (ch_flags[ch] & CHF_SDC_7909);
else *dat = 0;
return SCPE_OK;
}

/* Reset data channel 

   7607 responds to RDC
   7909 responds to RIC */

t_stat ch_op_reset (uint32 ch, t_bool ch7909)
{
DEVICE *dptr;

if (ch >= NUM_CHAN) return STOP_NXCHN;                  /* invalid argument? */
if (ch_dev[ch].flags & DEV_DIS) return SCPE_OK;         /* disabled? ok */
if (ch_dev[ch].flags & DEV_7909) {                      /* 7909? */
    if (!ch7909) return SCPE_OK;                        /* wrong reset is NOP */
    dptr = ch2dev[ch];                                  /* get device */
    }
else {                                                  /* 7607, 7289 */
    if (ch7909) return STOP_NT7909;                     /* wrong reset is err */
    dptr = ch_find_dev (ch, ch_ndsu[ch]);               /* find device */
    }
ch_reset (&ch_dev[ch]);                                 /* reset channel */
if (dptr && dptr->reset) dptr->reset (dptr);            /* reset device */
return SCPE_OK;
}

/* Channel process - called from main CPU loop.  If the channel is unable
   to get a valid command, it will reschedule itself for the next cycle.

   The read process is basically synchronous with the device timeout routine.
   The device requests the channel and supplies the word to be stored in memory.
   In the next time slot, the channel stores the word in memory. */

t_stat ch_proc (uint32 ch)
{
t_stat r;

if (ch >= NUM_CHAN) return SCPE_IERR;                   /* bad channel? */
ch_req &= ~REQ_CH (ch);                                 /* clear request */
if (ch_dev[ch].flags & DEV_DIS) return SCPE_IERR;       /* disabled? */
if (ch_dev[ch].flags & DEV_7909) {                      /* 7909 */

    t_uint64 sr;
    uint32 csel, sc, tval, mask, ta;
    t_bool xfr;

    if (ch_flags[ch] & CHF_IRQ) {                       /* interrupt? */
        ta = CHINT_CHA_SAV + (ch << 1);                 /* save location */
        if (ch_sta[ch] == CHXS_IDLE)                    /* waiting? */
            sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) |
                ((t_uint64) ch_clc[ch] & CHAMASK);      /* save CLC */
        else sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) |
            ((t_uint64) CHAINC (ch_clc[ch]));           /* no, save CLC+1 */
        ch_sta[ch] = CHXS_DSX;                          /* set running */
        ch_flags[ch] = (ch_flags[ch] | CHF_INT) &       /* set intr state */
            ~(CHF_IRQ|CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS); /* clr flags */
        WriteP (ta, sr);                                /* write ca,,clc */
        sr = ReadP (ta + 1);                            /* get chan cmd */
        return ch9_exec_cmd (ch, sr);                   /* exec cmd */
        }

    switch (ch_op[ch] & CH9_OPMASK) {                   /* switch on op */

    case CH9_TWT:                                       /* transfer of TWT */
    case CH9_WTR:                                       /* transfer of WTR */
    case CH9_TCH:                                       /* transfer */
        ch_clc[ch] = ch_ca[ch] & CHAMASK;               /* change CLC */
        break;

    case CH9_TDC:                                       /* decr & transfer */
        if (ch_lcc[ch] != 0) {                          /* counter != 0? */
            ch_lcc[ch]--;                               /* decr counter */
            ch_clc[ch] = ch_ca[ch] & CHAMASK;           /* change CLC */
            }
        break;

    case CH9_TCM:                                       /* transfer on cond */
        csel = CH9D_COND (ch_wc[ch]);
        mask = CH9D_MASK (ch_wc[ch]);
        if (csel == 7) xfr = (mask == 0);               /* C = 7? mask mbz */
        else {                                          /* C = 0..6 */
            if (csel == 0) tval = ch_cnd[ch];           /* C = 0? test cond */
            else tval = (uint32) (ch_ar[ch] >> (6 * (6 - csel))) & 077;
            if (ch_wc[ch] & CH9D_B11)
                xfr = ((tval & mask) == mask);
            else xfr = (tval == mask);
            }
        if (xfr) ch_clc[ch] = ch_ca[ch] & CHAMASK;      /* change CLC */
        break;

    case CH9_LIP:                                       /* leave interrupt */
        ta = CHINT_CHA_SAV + (ch << 1);                 /* save location */
        ch_flags[ch] &= ~(CHF_INT|CHF_IRQ);             /* clear intr */
        ch_cnd[ch] = 0;                                 /* clear channel cond */
        ch_clc[ch] = (uint32) ReadP (ta) & CHAMASK;
        break;

    case CH9_LIPT:                                      /* leave intr, transfer */
        ch_flags[ch] &= ~(CHF_INT|CHF_IRQ);             /* clear intr */
        ch_cnd[ch] = 0;                                 /* clear channel cond */
        ch_clc[ch] = ch_ca[ch] & CHAMASK;               /* change CLC */
        break;

    case CH9_LAR:                                       /* load assembly reg */
        ch_ar[ch] = ReadP (ch_ca[ch]);
        break;

    case CH9_SAR:                                       /* store assembly reg */
        WriteP (ch_ca[ch], ch_ar[ch]);
        break;

    case CH9_SMS:                                       /* load SMS reg */
        ch_sms[ch] = CH9A_SMS (ch_ca[ch]);              /* from eff addr */
        if (!(ch_sms[ch] & CHSMS_IATN1) &&              /* atn inhbit off */
            (ch_flags[ch] & CHF_ATN1))                  /* and atn pending? */
            ch9_eval_int (ch, 0);                       /* force int eval */
        break;

    case CH9_LCC:                                       /* load control cntr */
        ch_lcc[ch] = CH9A_LCC (ch_ca[ch]);              /* from eff addr */
        break;

    case CH9_ICC:                                       /* insert control cntr */
    case CH9_ICCA:
        csel = CH9D_COND (ch_wc[ch]);                   /* get C */
        if (csel == 0) ch_ar[ch] =                      /* C = 0? read SMS */
            (ch_ar[ch] & 0777777770000) | ((t_uint64) ch_sms[ch]);
        else if (csel < 7) {                            /* else read cond cntr */
            sc = 6 * (6 - csel);
            ch_ar[ch] = (ch_ar[ch] & ~(((t_uint64) 077) << sc)) |
                (((t_uint64) ch_lcc[ch]) << sc);
            }
        break;

    case CH9_XMT:                                       /* transmit */
        if (ch_wc[ch] == 0) break;
        sr = ReadP (ch_clc[ch]);                        /* next word */
        WriteP (ch_ca[ch], sr);
        ch_clc[ch] = CHAINC (ch_clc[ch]);               /* incr pointers */
        ch_ca[ch] = CHAINC (ch_ca[ch]);
        ch_wc[ch] = ch_wc[ch] - 1;                      /* decr count */
        ch_req |= REQ_CH (ch);                          /* go again */
        return SCPE_OK;

    case CH9_SNS:                                       /* sense */
        if (r = ch9_sel (ch, CHSL_SNS)) return r;       /* send sense to dev */
        ch_flags[ch] |= CHF_PRD;                        /* prepare to read */
        break;                                          /* next command */

    case CH9_CTL:
    case CH9_CTLR:
    case CH9_CTLW:                                      /* control */
        if (((ch_wc[ch] & CH9D_NST) == 0) &&            /* N = 0 and */
            !(ch_flags[ch] & CHF_EOR)) {                /* end not set? */
            sr = ReadP (ch_ca[ch]);
            ch_ca[ch] = CHAINC (ch_ca[ch]);             /* incr ca */
            return ch9_wr (ch, sr, 0);                  /* write ctrl wd */
            }
        ch_flags[ch] &= ~CHF_EOR;                       /* clear end */
        if (ch_op[ch] == CH9_CTLR) {                    /* CTLR? */
            if (r = ch9_sel (ch, CHSL_RDS)) return r;   /* send read sel */
            ch_flags[ch] |= CHF_PRD;                    /* prep to read */
            ch_idf[ch] = 0;
            }
        else if (ch_op[ch] == CH9_CTLW) {               /* CTLW? */
            if (r = ch9_sel (ch, CHSL_WRS)) return r;   /* end write sel */
            ch_flags[ch] |= CHF_PWR;                    /* prep to write */
            }
        break;

    case CH9_CPYD:                                      /* copy & disc */
        if ((ch_wc[ch] == 0) || (ch_flags[ch] & CHF_EOR)) { /* wc == 0 or EOR? */
            if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) {
                ch_flags[ch] &= ~(CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS);
                if (r = ch9_wr (ch, 0, CH9DF_STOP)) return r; /* send stop */
                }
            if (ch_flags[ch] & CHF_EOR) {               /* EOR? */
                ch_flags[ch] &= ~CHF_EOR;               /* clear flag */
                break;                                  /* new command */
                }
            return SCPE_OK;                             /* wait for end */
            }   
        if (ch_flags[ch] & CHF_RDS)                     /* read? */
            return ch9_rd_putw (ch);
        return ch9_wr_getw (ch);                        /* no, write */

    case CH9_CPYP:                                      /* anything to do? */
        if (ch_wc[ch] == 0) break;                      /* (new, wc = 0) next */
        if (ch_flags[ch] & CHF_EOR)                     /* end? */
            ch_flags[ch] &= ~CHF_EOR;                   /* ignore */
        else if (ch_flags[ch] & CHF_RDS)                /* read? */
            ch9_rd_putw (ch);
        else if (r = ch9_wr_getw (ch)) return r;        /* no, write */
        if (ch_wc[ch] == 0) break;                      /* done? get next */
        return SCPE_OK;                                 /* more to do */
            
    default:
        return STOP_ILLIOP;
        }

    return ch9_new_cmd (ch);                            /* next command */
    }

else if (ch_flags[ch] & CHF_RDS) {                      /* 7607 read? */

    if (ch_sta[ch] != CHXS_DSX) return ch6_end_ds (ch); /* chan exec? no, disc */
    switch (ch_op[ch] & CH6_OPMASK) {                   /* switch on op */

    case CH6_TCH:                                       /* transfer */
        ch_clc[ch] = ch_ca[ch] & CHAMASK;               /* change clc */
        return ch6_new_cmd (ch, FALSE);                 /* unpack new cmd */

    case CH6_IOCD:                                      /* IOCD */
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (ch6_rd_putw (ch)) return SCPE_OK;       /* store; more? cont */
            }
        return ch6_end_ds (ch);                         /* no, disconnect */

    case CH6_IOCP:                                      /* IOCP */
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (ch6_rd_putw (ch)) return SCPE_OK;       /* store; more? cont */
            }
        return ch6_new_cmd (ch, FALSE);                 /* unpack new cmd */

    case CH6_IOCT:                                      /* IOCT */
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (ch6_rd_putw (ch)) return SCPE_OK;       /* store; more? cont */
            }
        return ch6_ioxt (ch);                           /* unstall or disc */

    case CH6_IOSP:                                      /* IOSP */
        if (ch_flags[ch] & CHF_EOR) {                   /* (new) EOR set? */
            ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;     /* clear flag */
            return ch6_new_cmd (ch, FALSE);             /* get next cmd */
            }
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR))
                 return SCPE_OK;                        /* yes, store; more? */
            ch6_iosp_cclr (ch);                         /* cond clear eor */
            }
        return ch6_new_cmd (ch, FALSE);                 /* next cmd */

    case CH6_IOST:                                      /* IOST */
         if (ch_flags[ch] & CHF_EOR) {                   /* (new) EOR set? */
            ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;     /* clear flag */
            return ch6_ioxt (ch);                       /* get next cmd */
            }
       if (ch_wc[ch]) {                                /* wc > 0? */
            if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR))
                 return SCPE_OK;                        /* yes, store; more? */
            ch6_iosp_cclr (ch);                         /* cond clear eor */
            }
        return ch6_ioxt (ch);                           /* unstall or disc */

    case CH6_IORP:                                      /* IORP */
        if (ch_flags[ch] & CHF_EOR) {                   /* (new) EOR set? */
            ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;     /* clear flag */
            return ch6_new_cmd (ch, FALSE);             /* get next cmd */
            }
        ch6_rd_putw (ch);                               /* store wd; ignore wc */ 
        if (ch_flags[ch] & CHF_EOR) {                   /* EOR? */
            ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;     /* clear flag */
            return ch6_new_cmd (ch, FALSE);             /* get next cmd */
            }
        return SCPE_OK;                                 /* done */
        
    case CH6_IORT:                                      /* IORT */
        if (ch_flags[ch] & CHF_EOR) {                   /* (new) EOR set? */
            ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;     /* clear flag */
            return ch6_ioxt (ch);                       /* get next cmd */
            }
        ch6_rd_putw (ch);                               /* store wd; ignore wc */ 
        if (ch_flags[ch] & CHF_EOR) {                   /* EOR? */
            ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;     /* clear flag */
            return ch6_ioxt (ch);                       /* unstall or disc */
            }
        return SCPE_OK;                                 /* done */

    default:
        return SCPE_IERR;
        }                                               /* end case */
    }                                                   /* end if read */

else {                                                  /* 7607 write */

    if (ch_sta[ch] != CHXS_DSX) return ch6_end_ds (ch); /* chan exec? no, disc */
    switch (ch_op[ch] & CH6_OPMASK) {                   /* switch on op */

    case CH6_TCH:                                       /* transfer */
        ch_clc[ch] = ch_ca[ch] & CHAMASK;               /* change clc */
        return ch6_new_cmd (ch, FALSE);                 /* unpack new cmd */

    case CH6_IOCD:                                      /* IOCD */
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (r = ch6_wr_getw (ch, TRUE)) return r;   /* send wd to dev; err? */
            if (ch_wc[ch]) return SCPE_OK;              /* more to do? */
            }
        return ch6_end_ds (ch);                         /* disconnect */

    case CH6_IOCP:                                      /* IOCP */
    case CH6_IOSP:                                      /* IOSP */
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (r = ch6_wr_getw (ch, FALSE)) return r;  /* send wd to dev; err? */
            if (ch_wc[ch]) return SCPE_OK;              /* more to do? */
            }
        return ch6_new_cmd (ch, FALSE);                 /* get next cmd */

    case CH6_IOCT:                                      /* IOCT */
    case CH6_IOST:                                      /* IOST */
        if (ch_wc[ch]) {                                /* wc > 0? */
            if (r = ch6_wr_getw (ch, FALSE)) return r;  /* send wd to dev; err? */
            if (ch_wc[ch]) return SCPE_OK;              /* more to do? */
            }
        return ch6_ioxt (ch);                           /* get next cmd */

    case CH6_IORP:                                      /* IORP */
        if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) {   /* not EOR? (cdp, lpt) */
            if (r = ch6_wr_getw (ch, TRUE)) return r;   /* send wd to dev; err? */
            if (ch_wc[ch]) return SCPE_OK;              /* more to do? */
            }
        ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;         /* clear EOR */
        return ch6_new_cmd (ch, FALSE);                 /* get next cmd */

    case CH6_IORT:                                      /* IORT */
        if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) {   /* not EOR? (cdp, lpt) */
            if (r = ch6_wr_getw (ch, TRUE)) return r;   /* send wd to dev; err? */
            if (ch_wc[ch]) return SCPE_OK;              /* more to do? */
            }
        ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;         /* clear EOR */
        return ch6_ioxt (ch);                           /* unstall or disc */

    default:
        return SCPE_IERR;
        }                                               /* end switch */
    }                                                   /* end else write */
}

/* 7607 channel support routines */

/* 7607 channel input routine - put one word to memory */

t_bool ch6_rd_putw (uint32 ch)
{
if (ch_idf[ch] & CH6DF_EOR) ch_flags[ch] |= CHF_EOR;    /* eor from dev? */
else ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;            /* set/clr chan eor */
ch_idf[ch] = 0;                                         /* clear eor, valid */
if (ch_wc[ch]) {                                        /* wc > 0? */
    if ((ch_op[ch] & 1) == 0) {                         /* do store? */
        WriteP (ch_ca[ch], ch_ar[ch]);
        ch_ca[ch] = CHAINC (ch_ca[ch]);                 /* incr ca */
        }
    ch_wc[ch] = ch_wc[ch] - 1;
    }
return (ch_wc[ch]? TRUE: FALSE);
}

/* 7607 channel output routine - get one word from memory */

t_stat ch6_wr_getw (uint32 ch, t_bool eorz)
{
DEVICE *dptr;
DIB *dibp;
uint32 eorfl;

ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;                 /* clr eor */
if (ch_wc[ch]) {
    ch_ar[ch] = ReadP (ch_ca[ch]);                      /* get word */
    ch_ca[ch] = CHAINC (ch_ca[ch]);                     /* incr ca */
    ch_wc[ch] = ch_wc[ch] - 1;
    }
else ch_ar[ch] = 0;
if (eorz && (ch_wc[ch] == 0)) eorfl = 1;                /* eor on wc = 0? */
else eorfl = 0;
dptr = ch_find_dev (ch, ch_dsu[ch]);                    /* find device */
if (dptr &&                                             /* valid device? */
    (dibp = (DIB *) dptr->ctxt) &&                      /* with DIB? */
    dibp->write)                                        /* and write routine? */
    return dibp->write (ch, ch_ar[ch], eorfl);
return SCPE_IERR;                                       /* huh? */
}

/* 7607 channel new command - on channel load, check for disconnects

   The protocol for new commands is as follows:
   - If IOCD 0,,0, disconnect immediately
   - If IOCT 0,,0 or IOST 0,,0 and loaded by RCHA, disconnect immediately
   - If an effective NOP (TCH, IOCx 0,,0, IOSx 0,,0), force a channel
     cycle to retire the channel comand as quickly as possible.
   - If an IORx and EOR is set, force a channel cycle to retire the
     channel command as quickly as possible.
*/

t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld)
{
t_uint64 ir;
uint32 op, t;

ir = ReadP (t = ch_clc[ch]);                            /* read cmd */
ch_wc[ch] = GET_DEC (ir);                               /* get word cnt */
ch_ca[ch] = ((uint32) ir) & CHAMASK;                    /* get address */
op = GET_OPD (ir) << 1;                                 /* get opcode */
ch_op[ch] = op | ((((uint32) ir) & CH6I_NST)? 1: 0);    /* plus 'no store' */
if ((ir & CHI_IND) && (ch_wc[ch] ||                     /* indirect? */
    ((op != CH6_IOCP) && (op != CH6_IOSP)))) {          /* wc >0, or !IOxP? */
    t_uint64 sr = ReadP (ch_ca[ch] & AMASK);            /* read indirect */
    ch_ca[ch] = ((uint32) sr) & ((cpu_model & I_CT)? PAMASK: AMASK);
    }
if (hst_ch) cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0);
ch_clc[ch] = (ch_clc[ch] + 1) & AMASK;                  /* incr chan pc */

switch (op) {                                           /* case on opcode */

    case CH6_IOCD:                                      /* IOCD */
        if (ch_wc[ch] == 0) ch6_end_ds (ch);            /* wc 0? end now */
        break;

    case CH6_IOST:                                      /* IOST */
        if (ch_flags[ch] & CHF_EOR)                     /* EOR set? immed ch req */
            ch_req |= REQ_CH (ch);
    case CH6_IOCT:                                      /* IOCT */
        if (ch_wc[ch] == 0) {                           /* wc 0? */
            if (ch_ld) ch6_end_ds (ch);                 /* load? end now */
            else ch_req |= REQ_CH (ch);                 /* else immed ch req */
            }
        break;

    case CH6_IOSP:                                      /* IOSP */
        if (ch_flags[ch] & CHF_EOR)                     /* EOR set? immed ch req */
            ch_req |= REQ_CH (ch);
    case CH6_IOCP:                                      /* IOCP */
        if (ch_wc[ch] == 0) ch_req |= REQ_CH (ch);      /* wc 0? immed ch req */
        break;

    case CH6_IORT:                                      /* IORT */
    case CH6_IORP:                                      /* IORP */
        if (ch_flags[ch] & CHF_EOR)                     /* EOR set? immed ch req */
            ch_req |= REQ_CH (ch);
        break;

    case CH6_TCH:                                       /* TCH */
        ch_req |= REQ_CH (ch);                          /* immed ch req */
        break;

    default:                                            /* all others */
        break;
    }                                                   /* end case */

if (sim_brk_summ && sim_brk_test (t, SWMASK ('E')))
    return ch_bkpt (ch, t);
return SCPE_OK;
}

/* 7607 channel IOxT: if LCH stall, set state back to DSW; else disconnect and trap */

t_stat ch6_ioxt (uint32 ch)
{
if (ch_flags[ch] & CHF_LDW) {                           /* LCH cmd pending? */
    ch_flags[ch] &= ~CHF_LDW;                           /* clr pending flag */
    ch_sta[ch] = CH6S_DSW;                              /* unstall pending LCH */
    }
else {
    ch_flags[ch] |= CHF_CMD;                            /* set cmd trap flag */
    ch6_end_ds (ch);                                    /* disconnect */
    }
return SCPE_OK;
}

/* 7607 conditionally clear EOR on IOSx completion */

void ch6_iosp_cclr (uint32 ch)
{
uint32 i, op;

if (ch_wc[ch] == 0) {                                   /* wc = 0? */
    uint32 ccnt = 5;                                    /* allow 5 for CPU */
    for (i = 0; i < NUM_CHAN; i++) {                    /* test channels */
        if (ch_sta[ch] != CHXS_DSX) continue;           /* idle? skip */
        op = ch_op[ch] & ~1;                            /* get op */
        ccnt++;                                         /* 1 per active ch */
        if ((op == CH6_IOCP) || (op == CH6_IORP) ||     /* 1 per proceed */
            (op == CH6_IOSP)) ccnt++;
        }
    if (ccnt <= 11) return;                             /* <= 11? ok */
    }
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR;                 /* clear eor */
return;
}

/* 7607 external interface routines */

/* Input - store word, request channel input service */

t_stat ch6_req_rd (uint32 ch, uint32 unit, t_uint64 val, uint32 fl)
{
if (ch6_qconn (ch, unit)) {                             /* ch conn to caller? */
    if (ch_idf[ch] & CH6DF_VLD) ind_ioc = 1;            /* overrun? */
    ch_idf[ch] = CH6DF_VLD;                             /* set ar valid */
    if (fl) ch_idf[ch] |= CH6DF_EOR;                    /* set eor if requested */
    ch_req |= REQ_CH (ch);                              /* request chan */
    ch_flags[ch] |= CHF_RDS;
    ch_ar[ch] = val & DMASK;                            /* save data */
    }
return SCPE_OK;
}

/* Disconnect on error */

t_stat ch6_err_disc (uint32 ch, uint32 unit, uint32 fl)
{
if (ch6_qconn (ch, unit)) {                             /* ch conn to caller? */
    ch_flags[ch] |= fl;                                 /* set flag */
    return ch6_end_ds (ch);                             /* disconnect */
    }
return SCPE_OK;
}

/* Output - request channel output service */

t_bool ch6_req_wr (uint32 ch, uint32 unit)
{
if (ch6_qconn (ch, unit)) {                             /* ch conn to caller? */
    ch_req |= REQ_CH (ch);
    ch_flags[ch] &= ~CHF_RDS;
    }
return SCPE_OK;
}

/* Set/read channel flags */

uint32 ch6_set_flags (uint32 ch, uint32 unit, uint32 flags)
{
if (ch6_qconn (ch, unit)) {                             /* ch conn to caller? */
    ch_flags[ch] = ch_flags[ch] | flags;
    return ch_flags[ch];
    }
return 0;
}

/* Channel connected to unit? */

t_bool ch6_qconn (uint32 ch, uint32 unit)
{
if ((ch < NUM_CHAN) &&                                  /* valid chan */
    (ch_dsu[ch] == unit)) return TRUE;                  /* for right unit? */
return FALSE;
}

/* 7909 channel support routines */

/* 7909 channel input routine - put one word to memory */

t_stat ch9_rd_putw (uint32 ch)
{
ch_idf[ch] = 0;                                         /* invalidate */
if (ch_wc[ch]) {                                        /* wc > 0? */
    WriteP (ch_ca[ch], ch_ar[ch]);
    ch_ca[ch] = CHAINC (ch_ca[ch]);
    ch_wc[ch] = ch_wc[ch] - 1;
    }
return SCPE_OK;
}

/* 7909 channel output routine - get one word from memory */

t_stat ch9_wr_getw (uint32 ch)
{
if (ch_wc[ch]) {
    ch_ar[ch] = ReadP (ch_ca[ch]);                      /* get word */
    ch_ca[ch] = CHAINC (ch_ca[ch]);
    ch_wc[ch] = ch_wc[ch] - 1;
    }
else ch_ar[ch] = 0;
return ch9_wr (ch, ch_ar[ch], 0);                       /* write to device */
}

/* 7909 send select to device */

t_stat ch9_sel (uint32 ch, uint32 sel)
{
DEVICE *dptr = ch2dev[ch];
DIB *dibp;

if (dptr == NULL) return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp && dibp->chsel) return dibp->chsel (ch, sel, 0);
return SCPE_IERR;
}

/* 7909 send word to device */

t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl)
{
DEVICE *dptr = ch2dev[ch];
DIB *dibp;

if (dptr == NULL) return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp && dibp->write) return dibp->write (ch, dat, fl);
return SCPE_IERR;
}

/* 7909 channel new command */

t_stat ch9_new_cmd (uint32 ch)
{
t_uint64 ir;
uint32 t;
t_stat r;

ir = ReadP (t = ch_clc[ch]);                            /* read cmd */
r = ch9_exec_cmd (ch, ir);                              /* exec cmd */
if (ch_sta[ch] != CHXS_IDLE)                            /* chan running? */
    ch_clc[ch] = CHAINC (ch_clc[ch]);                   /* incr chan pc */
if ((r == SCPE_OK) && sim_brk_summ && sim_brk_test (t, SWMASK ('E')))
    return ch_bkpt (ch, t);
return r;
}

t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir)
{
uint32 op;

ch_wc[ch] = GET_DEC (ir);                               /* get word cnt */
ch_ca[ch] = ((uint32) ir) & CHAMASK;                    /* get address */
op = (GET_OPD (ir) << 2);                               /* get opcode */
ch_op[ch] = op | ((((uint32) ir) & 0200000)? 1: 0) |    /* plus bit<19> */
    (((op & 010) && (ch_wc[ch] & 040000))? 2: 0);       /* plus bit 3 if used */
if (ir & CHI_IND) {                                     /* indirect? */
    t_uint64 sr = ReadP (ch_ca[ch] & CHAMASK);          /* read indirect */
    ch_ca[ch] = ((uint32) sr) & CHAMASK;                /* get address */
    }
if (hst_ch)
    cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0);

switch (ch_op[ch]) {                                    /* check initial cond */

    case CH9_LAR:                                       /* misc processing */
    case CH9_SAR:
    case CH9_ICC:
    case CH9_ICCA:
    case CH9_XMT:
    case CH9_LCC:
    case CH9_SMS:
        if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))
            ch9_eval_int (ch, CHINT_SEQC);              /* not during data */
                                                        /* fall through */
    case CH9_TCM:                                       /* jumps */
    case CH9_TCH:
    case CH9_TDC:
    case CH9_LIPT:
    case CH9_LIP:
        ch_req |= REQ_CH (ch);                          /* process in chan */
        break;

    case CH9_CTL:                                       /* control */
    case CH9_CTLR:
    case CH9_CTLW:
        if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))
            ch9_eval_int (ch, CHINT_SEQC);              /* not during data */
        ch_flags[ch] &= ~CHF_EOR;
        if (ch_wc[ch] & CH9D_NST) ch_req |= REQ_CH (ch); /* N set? proc in chan */
        else return ch9_sel (ch, CHSL_CTL);             /* sel, dev sets ch_req! */
        break;

    case CH9_SNS:                                       /* sense */
        if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))
            ch9_eval_int (ch, CHINT_SEQC);
        ch_flags[ch] &= ~CHF_EOR;
        ch_req |= REQ_CH (ch);                          /* process in chan */
        break;  

    case CH9_CPYD:                                      /* data transfers */
    case CH9_CPYP:
        if ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) == 0)
            ch9_eval_int (ch, CHINT_SEQC);              /* not unless data */
        if (ch_flags[ch] & CHF_PRD) ch_flags[ch] |= CHF_RDS;
        else if (ch_flags[ch] & CHF_PWR) ch_flags[ch] |= CHF_WRS;
        ch_flags[ch] &= ~(CHF_EOR|CHF_PRD|CHF_PWR);
        if ((ch_op[ch] == CH9_CPYP) && (ch_wc[ch] == 0))
            ch_req |= REQ_CH (ch);                      /* CPYP x,,0? */
        break;                                          /* dev sets ch_req! */

    case CH9_WTR:                                       /* wait */
        ch_sta[ch] = CHXS_IDLE;                         /* stop */
        break;

    case CH9_TWT:                                       /* trap and wait */
        ch_sta[ch] = CHXS_IDLE;                         /* stop */
        ch_flags[ch] |= CHF_TWT;                        /* set trap */
        break;

    default:
        return STOP_ILLIOP;
        }

return SCPE_OK;
}

/* 7909 external interface routines */

/* Input - store word, request channel input service */

t_stat ch9_req_rd (uint32 ch, t_uint64 val)
{
if (ch < NUM_CHAN) {                                    /* valid chan? */
    if (ch_idf[ch] & CH9DF_VLD) ch9_set_ioc (ch);       /* prev still valid? io chk */
    ch_idf[ch] = CH9DF_VLD;                             /* set ar valid */
    ch_req |= REQ_CH (ch);                              /* request chan */
    ch_ar[ch] = val & DMASK;                            /* save data */
    }
return SCPE_OK;
}

/* Set attention */

void ch9_set_atn (uint32 ch)
{
if (ch < NUM_CHAN) ch9_eval_int (ch, CHINT_ATN1);
return;
}

/* Set IO check - UEND will occur at end - not recognized in int mode */

void ch9_set_ioc (uint32 ch)
{
if ((ch < NUM_CHAN) && !(ch_flags[ch] & CHF_INT)) {
    ind_ioc = 1;                                        /* IO check */
    ch_flags[ch] |= CHF_IOC;                            /* ch IOC for end */
    }
return;
}

/* Set end */

void ch9_set_end (uint32 ch, uint32 iflags)
{
if (ch < NUM_CHAN) {                                    /* valid chan? */
    ch_flags[ch] |= CHF_EOR;
    ch9_eval_int (ch, iflags);
    }
return;
}

/* Test connected */

t_bool ch9_qconn (uint32 ch)
{
if ((ch < NUM_CHAN) && (ch_sta[ch] == CHXS_DSX)) return TRUE;
return FALSE;
}

/* Evaluate interrupts 

   - Interrupt requests set flags in the channel flags word
   - If an interrupt is not in progress, interrupt requests are evaluated
   - If an interrupt request is found, the interruptable flags are
     transferred to the channel condition register and cleared in
     the channel flags

   This provides an effective stage of buffering for interrupt requests
   that are not immediately serviced */

void ch9_eval_int (uint32 ch, uint32 iflags)
{
uint32 ireq;

ch_flags[ch] |= (iflags << CHF_V_COND);                 /* or into chan flags */
if ((ch_flags[ch] & CHF_INT) == 0) {                    /* int not in prog? */
    ireq = ((ch_flags[ch] >> CHF_V_COND) & CHF_M_COND) &
        ~(((ch_sms[ch] & CHSMS_IUEND)? CHINT_UEND: 0) |
          ((ch_sms[ch] & CHSMS_IATN1)? CHINT_ATN1: 0) |
          ((ch_sms[ch] & CHSMS_IATN2)? CHINT_ATN2: 0) |
          ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))? CHINT_SEQC: 0));
    if (ireq) {                                         /* int pending? */
        ch_cnd[ch] = ireq;                              /* set cond reg */
        ch_flags[ch] &= ~(ireq << CHF_V_COND);          /* clear chan flags */
        ch_flags[ch] |= CHF_IRQ;                        /* set int req */
        ch_req |= REQ_CH (ch);                          /* request channel */
        }
    }
return;
}

/* Test for all channels idle */

t_bool ch_qidle (void)
{
uint32 i;

for (i = 0; i < NUM_CHAN; i++) {
    if (ch_sta[i] != CHXS_IDLE) return FALSE;
    }
return TRUE;
}

/* Evaluate/execute channel traps */

uint32 chtr_eval (uint32 *decr)
{
uint32 i, cme;

if (!chtr_inht && !chtr_inhi && chtr_enab) {
    if (BIT_TST (chtr_enab, CHTR_V_CLK) && chtr_clk) {  /* clock trap? */
        if (decr) {                                     /* exec? */
            chtr_clk = 0;                               /* clr flag */
            *decr = 0;
            }
        return CHTR_CLK_SAV;
        }
    for (i = 0; i < NUM_CHAN; i++) {                    /* loop thru chan */
        cme = BIT_TST (chtr_enab, CHTR_V_CME + i);      /* cmd/eof enab? */
        if (cme && (ch_flags[i] & CHF_CMD)) {           /* cmd enab and set? */
            if (decr) {                                 /* exec? */
                ch_flags[i] &= ~CHF_CMD;                /* clr flag */
                *decr = CHTR_F_CMD;
                }
            return (CHTR_CHA_SAV + (i << 1));
            }
        if (cme && (ch_flags[i] & CHF_EOF)) {           /* eof enab and set? */
            if (decr) {                                 /* exec? */
                ch_flags[i] &= ~CHF_EOF;                /* clr flag */
                *decr = CHTR_F_EOF;
                }
            return (CHTR_CHA_SAV + (i << 1));
            }
        if (BIT_TST (chtr_enab, CHTR_V_TRC + i) &&      /* trc enab? */
            (ch_flags[i] & CHF_TRC)) {                  /* trc flag? */
            if (decr) {                                 /* exec? */
                ch_flags[i] &= ~CHF_TRC;                /* clr flag */
                *decr = CHTR_F_TRC;
                }
            return (CHTR_CHA_SAV + (i << 1));
            }                                           /* end if BIT_TST */
        }                                               /* end for */
    }                                                   /* end if !chtr_inht */
if (decr) *decr = 0;
return 0;
}

/* Channel reset */

t_stat ch_reset (DEVICE *dptr)
{
uint32 ch = dptr - &ch_dev[0];                          /* get channel */

if (ch == CH_A) ch2dev[ch] = &mt_dev[0];                /* channel A fixed */
ch_sta[ch] = 0;
ch_flags[ch] = 0;
ch_idf[ch] = 0;
ch_dso[ch] = 0;
ch_dsu[ch] = 0;
ch_ndso[ch] = 0;
ch_ndsu[ch] = 0;
ch_op[ch] = 0;
ch_clc[ch] = 0;
ch_wc[ch] = 0;
ch_ca[ch] = 0;
ch_ar[ch] = 0;
ch_sms[ch] = 0;
ch_cnd[ch] = 0;
ch_lcc[ch] = 0;
sim_cancel (&ch_unit[ch]);
return SCPE_OK;
}

/* Show channel type */

t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc)
{
DEVICE *dptr;

dptr = find_dev_from_unit (uptr);
if (dptr == NULL) return SCPE_IERR;
if (dptr->flags & DEV_7909) fputs ("7909", st);
else if (dptr->flags & DEV_7289) fputs ("7289", st);
else fputs ("7607", st);
return SCPE_OK;
}

/* Enable channel, assign device */

t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc)
{
DEVICE *dptr, *dptr1;
char gbuf[CBUFSIZE];
uint32 i, ch;

dptr = find_dev_from_unit (uptr);
if (dptr == NULL) return SCPE_IERR;
ch = dptr - &ch_dev[0];
if ((ch == 0) || !(dptr->flags & DEV_DIS)) return SCPE_ARG;
if (cptr == NULL) cptr = "TAPE";
get_glyph (cptr, gbuf, 0);
for (i = 0; dev_table[i].name; i++) {
    if (strcmp (dev_table[i].name, gbuf) == 0) {
        dptr1 = ch_map_flags (ch, dev_table[i].flags);
        if (!dptr1 || !(dptr1->flags & DEV_DIS)) return SCPE_ARG;
        dptr->flags &= ~(DEV_DIS|DEV_7909|DEV_7289|DEV_7750|DEV_7631);
        dptr->flags |= dev_table[i].flags;
        dptr1->flags &= ~DEV_DIS;
        ch2dev[ch] = dptr1;
        return reset_all (0);
        }
    }
return SCPE_ARG;
}

/* Map device flags to device pointer */

DEVICE *ch_map_flags (uint32 ch, int32 fl)
{
if (fl & DEV_7289) return &drm_dev;
if (!(fl & DEV_7909)) return &mt_dev[ch];
if (fl & DEV_7631) return &dsk_dev;
if (fl & DEV_7750) return &com_dev;
return NULL;
}

/* Set up channel map */

void ch_set_map (void)
{
uint32 i;

for (i = 0; i < NUM_CHAN; i++) {
    if (ch_dev[i].flags & DEV_DIS) ch2dev[i] = NULL;
    else ch2dev[i] = ch_map_flags (i, ch_dev[i].flags);
    }
return;
}       

/* Disable channel, deassign device */

t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc)
{
DEVICE *dptr, *dptr1;
UNIT *uptr1;
uint32 i, ch;

dptr = find_dev_from_unit (uptr);
if (dptr == NULL) return SCPE_IERR;
ch = dptr - &ch_dev[0];
if ((ch == 0) || (dptr->flags & DEV_DIS) || (cptr != NULL)) return SCPE_ARG;
dptr1 = ch2dev[ch];
if (dptr1 == NULL) return SCPE_IERR;
if (dptr1->units) {
    for (i = 0; i < dptr1->numunits; i++) {
        uptr1 = dptr1->units + i;
        if (dptr1->detach) dptr1->detach (uptr1);
        else detach_unit (uptr1);
        }
    }
dptr->flags &= ~(DEV_7909|DEV_7289);
dptr->flags |= DEV_DIS;
dptr1->flags |= DEV_DIS;
return reset_all (0);
}

/* Show channel that device is on (tapes, 7289, 7909 only) */

t_stat ch_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)
{
DEVICE *dptr;
uint32 i;

dptr = find_dev_from_unit (uptr);
if (dptr) {
    for (i = 0; i < NUM_CHAN; i++) {
        if (ch2dev[i] == dptr) {
            fprintf (st, "channel %c", 'A' + i);
            return SCPE_OK;
            }
        }
    }
fprintf (st, "not assigned to channel");
return SCPE_OK;
}
