/* i7070_chan.c: IBM 7070 Channel simulator

   Copyright (c) 2005-2016, Richard Cornwell

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

   channel

   The system state for the IBM 7070 channel is:
   There are 4 types of channel:
        PIO:            Basic polled mode transfer. Channel only manages
                        status and disconnect of devices.
       7604:            Basic channel.
       7907:            Enhanced channel for disk, hypertape and com controlers.

   Common registers to all but PIO channels.
   ADDR<0:16>           Location to read or write next word from.
   CMD<0:6>             Channel command.
   LIMIT<0:16>          Transfer limit
   ASM<0:44>            Assembled data from devices.
   LOCATION<0:16>       Address of next command.

   Simulation registers to handle device handshake.
   STATUS<0:16>         Simulated register for basic channel status.
   SENSE<0:16>          Additional flags for 7907 channels.
*/

#include "i7070_defs.h"

extern UNIT         cpu_unit;

#define CHAN_DEF        UNIT_DISABLE|CHAN_SET

t_stat              chan_reset(DEVICE * dptr);
void                chan_fetch(int chan);
t_stat              chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
                        const char *cptr);
const char          *chan_description (DEVICE *dptr);


/* Channel data structures

   chan_dev     Channel device descriptor
   chan_unit    Channel unit descriptor
   chan_reg     Channel register list
   chan_mod     Channel modifiers list
*/

uint32              location[NUM_CHAN];         /* Location of RDW instruction*/
uint32              caddr[NUM_CHAN];            /* Channel memory address */
uint8               bcnt[NUM_CHAN];             /* Channel character count */
uint8               cmd[NUM_CHAN];              /* Current command */
uint8               op[NUM_CHAN];               /* Operators for 7907 channel */
uint32              limit[NUM_CHAN];            /* Word count */
t_uint64            assembly[NUM_CHAN];         /* Assembly register */
uint32              chan_flags[NUM_CHAN];       /* Unit status */
uint32              chan_info[NUM_CHAN];        /* Private channel info */
uint8               chan_irq[NUM_CHAN];         /* Channel has a irq pending */
extern uint16       pri_latchs[10];

#define CHAN_OUTDEV     0x010000        /* Type out device */
#define CHAN_PRIO       0x008000        /* Channel has priority pending */
#define CHAN_TWE        0x004000        /* Channel format error */
#define CHAN_SEOR       0x002000        /* Channel saw a eor */
#define CHAN_NORDW      0x020000        /* No RDW for this command */
#define CHAN_SEOS       0x040000        /* Channel saw a end of segment */
#define CHAN_SCLR       0x080000        /* Short record */
#define CHAN_FIRST      0x100000        /* First tranfered word */
#define CHAN_START      0x200000        /* Channel has just started */
#define CHAN_OCTAL      0x400000        /* Octal conversion */

const char     *chan_type_name[] = {
    "Polled", "Unit Record", "7604", "7907", ""};


UNIT                chan_unit[] = {
    /* Puesdo channel for PIO devices */
    {UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_UREC)|UNIT_S_CHAN(CHAN_CHUREC),0)},
    /* Normal channels */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_A)|CHAN_S_TYPE(CHAN_7604),0)},/* A */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_B)|CHAN_S_TYPE(CHAN_7604),0)},/* B */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_C)|CHAN_S_TYPE(CHAN_7604),0)},/* C */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_D)|CHAN_S_TYPE(CHAN_7604),0)},/* D */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_E)|CHAN_S_TYPE(CHAN_7907),0)},/* E */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_F)|CHAN_S_TYPE(CHAN_7907),0)},/* F */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_G)|CHAN_S_TYPE(CHAN_7907),0)},/* G */
    {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_H)|CHAN_S_TYPE(CHAN_7907),0)} /* H */
};

REG                 chan_reg[] = {
    {BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(LIMIT, limit, 10, 18, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(ASM, assembly, 16, 44, NUM_CHAN), REG_VMIO|REG_RO|REG_FIT},
    {BRDATA(LOCATION, location, 10, 18, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT},
    {NULL}
};

MTAB                chan_mod[] = {
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_UREC), "UREC", NULL, NULL,NULL,NULL},
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_7604), "7604", NULL, NULL, NULL, NULL},
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_7907), "7907", NULL, NULL, NULL, NULL},
    {MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL, "Show units on channel"},
    {0}
};

/* Simulator debug controls */
DEBTAB              chn_debug[] = {
    {"CHANNEL", DEBUG_CHAN},
    {"TRAP", DEBUG_TRAP},
    {"CMD", DEBUG_CMD},
    {"DATA", DEBUG_DATA},
    {"DETAIL", DEBUG_DETAIL},
    {"EXP", DEBUG_EXP},
    {"SENSE", DEBUG_SNS},
    {"CH0", 0x0100 << 0},
    {"CH1", 0x0100 << 1},
    {"CH2", 0x0100 << 2},
    {"CH3", 0x0100 << 3},
    {"CH4", 0x0100 << 4},
    {"CHA", 0x0100 << 5},
    {"CHB", 0x0100 << 6},
    {"CHC", 0x0100 << 7},
    {"CHD", 0x0100 << 8},
    {0, 0}
};

DEVICE              chan_dev = {
    "CH", chan_unit, chan_reg, chan_mod,
    NUM_CHAN, 10, 18, 1, 10, 44,
    NULL, NULL, &chan_reset, NULL, NULL, NULL,
    NULL, DEV_DEBUG, 0, chn_debug,
    NULL, NULL, &chan_help, NULL, NULL, &chan_description
};

#define DELTA_CHAR              057
#define SM_CHAR                 037
#define SM_MEM                  0x39
#define RM_CHAR                 0x80

/* Translation tables */
uint8   bcd_mem[64] = {
        /*  ?     1     2     3     4     5     6     7 */
/* 00 */ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
        /*  8     9     0   =/#   !/@     ?     ?    tm */
/* 10 */ 0x98, 0x99, 0x90, 0x45, 0x46, 0x47, 0x48, 0x49,
        /* sp     /     S     T     U     V     W     X */
/* 20 */ 0x60, 0x31, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
        /*  Y     Z    rm     ,   %/(     ?     ?    sm */
/* 30 */ 0x88, 0x89, 0x80, 0x35, 0x36, 0x37, 0x38, 0x39,
        /*  -     J     K     L     M     N     O     P */
/* 40 */ 0x30, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
        /*  Q     R    -0     $     *     ?     ?   del */
/* 50 */ 0x78, 0x79, 0x70, 0x25, 0x26, 0x27, 0x28, 0xFF,
        /*+/&     A     B     C     D     E     F     G */
/* 60 */ 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
        /*  H     I    +0     .    sq     ?     ?    gm */
/* 70 */ 0x68, 0x69, 0x60, 0x15, 0x16, 0x17, 0x18, 0x19
};

uint8   mem_bcd[256] = {
        /* sp                                      */
/* 00 */  020, 000, 000, 000, 000, 000, 000, 000,
        /*                                         */
/* 08 */  000, 000, 000, 000, 000, 000, 000, 000,
        /*                               sq    ?   */
/* 10 */  000, 000, 000, 000, 000, 073, 074, 075,
        /*  ?    ?                                 */
/* 18 */  076, 077, 000, 000, 000, 000, 000, 000,
        /*+/-                        $    *    ?   */
/* 20 */  060, 000, 000, 000, 000, 053, 054, 055,
        /*  ?  +/-                                 */
/* 28 */  056, 060, 000, 000, 000, 000, 000, 000,
        /*  -    /                   ,  %/(    ?   */
/* 30 */  040, 021, 000, 000, 000, 033, 034, 035,
        /*  ?   sm                                 */
/* 38 */  036, 037, 000, 000, 000, 000, 000, 000,
        /*                         =/#  !/@    ?   */
/* 40 */  000, 000, 000, 000, 000, 013, 014, 015,
        /*  ?   tm                                 */
/* 48 */  016, 017, 000, 000, 000, 000, 000, 000,
        /*                                         */
/* 50 */  000, 000, 000, 000, 000, 000, 000, 000,
        /*                                         */
/* 58 */  000, 000, 000, 000, 000, 000, 000, 000,
        /* +0    A    B    C    D    E    F    G   */
/* 60 */  072, 061, 062, 063, 064, 065, 066, 067,
        /*  H    I                                 */
/* 68 */  070, 071, 000, 000, 000, 000, 000, 000,
        /* -0    J    K    L    M    N    O    P   */
/* 70 */  052, 041, 042, 043, 044, 045, 046, 047,
        /*  Q    R                                 */
/* 78 */  050, 051, 000, 000, 000, 000, 000, 000,
        /* rm         S    T    U    V    W    X   */
/* 80 */  032, 000, 022, 023, 024, 025, 026, 027,
        /*  Y    Z                                 */
/* 88 */  030, 031, 000, 000, 000, 000, 000, 000,
        /*  0    1    2    3    4    5    6    7   */
/* 90 */  012, 001, 002, 003, 004, 005, 006, 007,
        /*  8    9                                 */
/* 98 */  010, 011, 000, 000, 000, 000, 000, 000
};


t_stat
chan_reset(DEVICE * dptr)
{
    int                 i;

    /* Clear channel assignment */
    for (i = 0; i < NUM_CHAN; i++) {
        chan_flags[i] = 0;
        chan_info[i] = 0;
        caddr[i] = 0;
        cmd[i] = 0;
        bcnt[i] = 10;
        chan_irq[i] = 0;
        limit[i] = 0;
        location[i] = 0;
    }
    return chan_set_devs(dptr);
}

/* Boot from given device */
t_stat
chan_boot(int32 unit_num, DEVICE * dptr)
{
    return SCPE_NOFNC;  /* Not implimented until I know how boot work */
}

t_stat
chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) {
    DEVICE            **dptr;
    DIB                *dibp;
    uint32              j;
    UNIT               *uptr;

    for (dptr = sim_devices; *dptr != NULL; dptr++) {
        int                 r;

        dibp = (DIB *) (*dptr)->ctxt;
        /* If no DIB, not channel device */
        if (dibp == 0)
            continue;
        uptr = (*dptr)->units;
        /* If this is a 79XX device, check it */
        if (dibp->ctype & CH_TYP_79XX) {
            for (j = 0; j < (*dptr)->numunits; j++, uptr++) {
                if ((uptr->flags & UNIT_DIS) == 0 &&
                    UNIT_G_CHAN(uptr->flags) == chan &&
                    (dev == ((UNIT_SELECT & uptr->flags) != 0))) {
                    r = dibp->cmd(uptr, dcmd, dev);
                    if (r != SCPE_NODEV)
                        return r;
                }
            }
        } else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) {
            if (dibp->upc == 1) {
                for (j = 0; j < (*dptr)->numunits; j++) {
                    if ((uptr->flags & UNIT_DIS) == 0 &&
                        UNIT_G_CHAN(uptr->flags) == chan) {
                        r = dibp->cmd(uptr, dcmd, dev);
                        if (r != SCPE_NODEV)
                            return r;
                    }
                    uptr++;
                }
            } else {
                if ((uptr->flags & UNIT_DIS) == 0 &&
                        UNIT_G_CHAN(uptr->flags) == chan) {
                    r = dibp->cmd(uptr, dcmd, dev);
                    if (r != SCPE_NODEV)
                        return r;
                }
            }
        }
    }
    return SCPE_NODEV;
}

/* Execute the next channel instruction. */
void
chan_proc()
{
    int                 chan;
    int                 cmask;

    /* Scan channels looking for work */
    for (chan = 0; chan < NUM_CHAN; chan++) {
        /* Skip if channel is disabled */
        if (chan_unit[chan].flags & UNIT_DIS)
            continue;

        cmask = 0x0100 << chan;
        switch (CHAN_G_TYPE(chan_unit[chan].flags)) {
        case CHAN_UREC:
        case CHAN_7604:
            /* If channel is disconnecting, do nothing */
            if (chan_flags[chan] & DEV_DISCO)
                continue;

            /* If device requested attention, abort current command */
            if (chan_flags[chan] & CHS_ATTN) {
                if (chan_dev.dctrl & cmask)
                    sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn\n",
                              chan);
                chan_flags[chan] &=
                    ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT);
                /* Disconnect if selected */
                if (chan_flags[chan] & DEV_SEL)
                    chan_flags[chan] |= (DEV_DISCO);
                continue;
            }

            /* If no select, stop channel */
            if ((chan_flags[chan] & DEV_SEL) == 0
                && (chan_flags[chan] & STA_TWAIT)) {
                t_uint64        temp;
                int             adr;
chan_trap:
                if (chan != 0) {
                    adr = 100 + (chan * 10) + (chan_info[chan] & 0xf);
                    temp = 2;
                    if (chan_info[chan] & CHAN_TWE)
                       temp = 0;
                    else if (chan_flags[chan] & CHS_ERR)
                       temp = 1;
                    else if (chan_flags[chan] & CHS_EOF)
                       temp = 5;
                    else if (chan_info[chan] & CHAN_SEOS)
                       temp = 6;
                    else if (chan_info[chan] & CHAN_SCLR)
                       temp = 7;
                    else if ((chan_info[chan] & CHAN_NORDW) == 0) {
                        if ((chan_info[chan] & CHAN_SEOR) == 0 &&
                                caddr[chan] > limit[chan])
                            temp = 4;
                         else if (caddr[chan] < limit[chan])
                            temp = 3;
                    }
                    chan_flags[chan] &= ~(CHS_ERR|CHS_EOF);
                    temp <<= 32;
                    if (chan_info[chan] & CHAN_NORDW) {
                        temp |= M[adr] & 0xFFFFFFFFLL;
                    } else {
                        upd_idx(&temp, caddr[chan]);
                        bin_dec(&temp, location[chan], 0, 4);
                    }
                    temp |= PSIGN;
                    /* Copy over flag */
                    temp |= M[adr] & 0xF000000000LL;
                    if (chan_dev.dctrl & cmask)
                           sim_debug(DEBUG_TRAP, &chan_dev,
                                 "chan %d Trap: %012llx prio=%d\n\r", chan,
                                         temp, (chan_info[chan]&CHAN_PRIO)?1:0);
                    M[adr] = temp;
                    if ((chan_info[chan] & CHAN_PRIO) ||
                                         ((temp >> 32) & 0xf) != 2)
                         pri_latchs[chan] |= 1 << (chan_info[chan] & 0xF);
                    chan_info[chan] &= ~CHAN_PRIO;
                } else if (chan_dev.dctrl & cmask)
                    sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap %04x\n",
                              chan, chan_info[chan]);
                chan_flags[chan] &=
                    ~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT);
                continue;
            }

            /* No activity, nothing happening here folks, move along */
            if ((chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) == 0) {
                /* This could be a no data command, if pending priorty
                   request, ask device if it is ready */
                if ((cmd[chan] & CHN_SEGMENT) == 0 &&
                     chan_info[chan] & CHAN_PRIO &&
                     chan_issue_cmd(chan, IO_TRS, chan_info[chan]&0xf)
                                                        == SCPE_OK)
                   goto chan_trap;
                continue;
            }

            /* If first time through here, load up channel control */
            if (chan_flags[chan] & STA_ACTIVE && chan_info[chan] & CHAN_START)
                chan_fetch(chan);

            /* Process reading of a segment command */
            if ((cmd[chan] & (CHN_SEGMENT|CHN_RM_FND)) ==
                                (CHN_SEGMENT|CHN_RM_FND)) {
              /* Two backspaces and a read */
              switch (cmd[chan] & (CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS)) {
                 case CHN_RM_FND:
                        if (chan_issue_cmd(chan, IO_BSR,
                                 chan_info[chan]&0xf) == SCPE_OK) {
                            cmd[chan] |= CHN_COMPRESS;
                         if (chan_dev.dctrl & cmask)
                              sim_debug(DEBUG_DETAIL, &chan_dev,
                                 "segment %d bsr 2\n\r", chan);
                        }
                        break;
                 case CHN_RM_FND|CHN_COMPRESS:
                        if (chan_issue_cmd(chan, IO_BSR,
                                 chan_info[chan]&0xf) == SCPE_OK) {
                             cmd[chan] |= CHN_NUM_MODE;
                             if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DETAIL, &chan_dev,
                                             "segment %d bsr 2\n\r", chan);
                             if (chan_flags[chan] & CHS_BOT) {
                                  chan_flags[chan] &= ~(STA_ACTIVE);
                                  goto chan_trap;
                             }
                        }
                        break;
                 case CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS:
                        chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST);
                        if ( chan_issue_cmd(chan, IO_RDS,
                                 chan_info[chan]&0xf) == SCPE_OK) {
                           cmd[chan] &= ~(CHN_NUM_MODE|CHN_COMPRESS|CHN_RM_FND);
                         if (chan_dev.dctrl & cmask)
                              sim_debug(DEBUG_DETAIL, &chan_dev,
                                 "segment %d read\n\r", chan);
                            chan_flags[chan] &= ~(STA_WAIT|DEV_REOR);
                        }
                        break;
                }
                /* Device ready, decide command to issue */
                if (cmd[chan] & CHN_RECORD) {
                    if (chan_flags[chan] & CHS_BOT) {
                        chan_flags[chan] |= STA_TWAIT;
                    }
                    /* Two backspaces and a read */
                } else if (chan_flags[chan] & CHS_EOT) {
                        chan_flags[chan] |= STA_TWAIT;
                }
                continue;
            }

            /* None disabled, active channel is if transfering */
            switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) {
                /* Device has given us a dataword */
            case DEV_FULL:
                /* Process reading of a segment command */
                 if (cmd[chan] & CHN_SEGMENT) {
                     /* Check if hit end of record */
#if 0           /* Check segment operation correct before removing */
                    if (chan_dev.dctrl & cmask)
                                sim_debug(DEBUG_DETAIL, &chan_dev,
                                         "chk segment %d %d data = %012llx",
                                         chan, bcnt[chan], assembly[chan]);

                    if ((chan_flags[chan] & (STA_WAIT|DEV_REOR))
                                == (STA_WAIT|DEV_REOR)) {
                        chan_flags[chan] &= ~(STA_WAIT|DEV_REOR|DEV_FULL);
                        chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST);
                        continue;
                    }

                    if (bcnt[chan] >= 6 && chan_flags[chan] & DEV_REOR) {
                       if (chan_info[chan] & CHAN_SEOS) {
                            if (chan_dev.dctrl & cmask)
                                sim_debug(DEBUG_DETAIL, &chan_dev, " found\n");
                            /* Correct error */
                            chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR);
                            /* What we were looking for? */
                            if (caddr[chan] >= limit[chan]) {
                                chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF);
                                chan_flags[chan] |= STA_TWAIT;
                                chan_info[chan] |= CHAN_SEOR;
                            } else
                                caddr[chan]++;
                        }
                        chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST|CHAN_TWE);
                        chan_flags[chan] &= ~(DEV_FULL|DEV_REOR|CHS_ERR);
                    } else
                        if (chan_dev.dctrl & cmask)
                             sim_debug(DEBUG_DETAIL, &chan_dev, " search\n");
                    /* How about regular record */
                    chan_flags[chan] &= ~DEV_FULL;
                    /* Wait for next record */
                    chan_flags[chan] |= STA_WAIT|DEV_DISCO;
                    if (cmd[chan] & CHN_RECORD)
                        cmd[chan] |= CHN_RM_FND;
                    else
                        cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE;
                    assembly[chan] = 0;
                    bcnt[chan] = 10;
#endif
                    continue;
                }

                /* If we are not waiting EOR save it in memory */
                if ((chan_flags[chan] & STA_WAIT) == 0) {
                    if (chan_dev.dctrl & cmask)
                        sim_debug(DEBUG_DATA, &chan_dev,
                                  "chan %d data < %011llx\n",
                                  chan, assembly[chan]);
                    if (caddr[chan] < MEMSIZE)
                        M[caddr[chan]] = assembly[chan];

                    if (bcnt[chan] != 0)
                        chan_info[chan] |= CHAN_SCLR;
                    else
                        chan_info[chan] &= ~CHAN_SCLR;
                    /* Check if last transfer before final */
                    if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) {
                        chan_flags[chan] &= ~(STA_ACTIVE);
                        chan_flags[chan] |= STA_TWAIT|STA_WAIT;
                    } else
                        caddr[chan]++;

                    /* Update channel status word */
                    if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) {
                        int     adr = 100 + (chan * 10) + (chan_info[chan]&0xf);
                        upd_idx(&M[adr], caddr[chan]);
                        bin_dec(&M[adr], location[chan], 0, 4);
                    }

                    /* Check for record mark */
                    if ((cmd[chan] & CHN_RECORD) &&
                        (assembly[chan] & SMASK) == ASIGN &&
                        (assembly[chan] & 0xFF) == RM_CHAR) {
                        if (cmd[chan] & CHN_LAST) {
                            chan_flags[chan] &= ~(STA_ACTIVE);
                            chan_flags[chan] |= STA_TWAIT|STA_WAIT;
                        } else
                            chan_fetch(chan);
                    }
                    bcnt[chan] = 10;
                    assembly[chan] = 0;
                }
                chan_info[chan] |= CHAN_FIRST;  /* Saved one char */
                chan_flags[chan] &= ~DEV_FULL;

                /* Device does not need a word and has not given us one */
            case 0:
                if (chan_flags[chan] & DEV_REOR) {
                    /* Check EOR at end of segment */
                    if (cmd[chan] & CHN_SEGMENT) {
                        if ((chan_info[chan] & CHAN_FIRST) == 0 &&
                           bcnt[chan] == 8 &&
                          assembly[chan] == (ASIGN|(((t_uint64)SM_MEM) << 32))){
                            if (chan_dev.dctrl & cmask)
                                sim_debug(DEBUG_DETAIL, &chan_dev,
                                 "chk segment %d %d data = %012llx found\n\r",
                                         chan, bcnt[chan], assembly[chan]);

                            /* What we were looking for? */
                            if (caddr[chan] >= limit[chan]) {
                                chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF);
                                chan_flags[chan] |= STA_TWAIT;
                            } else
                                caddr[chan]++;
                        } else
                            /* Check if hit end of record */
                            if (chan_dev.dctrl & cmask)
                                sim_debug(DEBUG_DETAIL, &chan_dev,
                                 "chk segment %d %d data = %012llx search\n\r",
                                         chan, bcnt[chan], assembly[chan]);
                        /* Correct error */
                        chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR);

                        assembly[chan] = 0;
                        bcnt[chan] = 10;
                        chan_flags[chan] &= ~(DEV_REOR|DEV_FULL);
                        /* Wait for next record */
                        chan_flags[chan] |= STA_WAIT|DEV_DISCO;
                        if (cmd[chan] & CHN_RECORD)
                            cmd[chan] |= CHN_RM_FND;
                        else
                            cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE;
                        continue;
                    }
                /* Device idle, expecting data from it */
                /* Check if got EOR */
                    chan_flags[chan] &= ~(DEV_REOR|STA_ACTIVE|STA_WAIT);
                    chan_flags[chan] |= STA_TWAIT|DEV_DISCO;
                    chan_info[chan] |= CHAN_SEOR;
                    if (chan_dev.dctrl & cmask)
                        sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR< %o\n",
                                  chan, cmd[chan]);
                    continue;
                }
                if (caddr[chan] > limit[chan]
                    && (chan_flags[chan] & STA_WAIT) == 0) {
                    if (chan_dev.dctrl & cmask)
                        sim_debug(DEBUG_EXP, &chan_dev,
                                  "chan %d < WC0 %o\n", chan, cmd[chan]);
                    if (cmd[chan] & CHN_LAST) {
                        chan_flags[chan] &= ~(STA_ACTIVE);
                        chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_DISCO;
                        chan_info[chan] &= ~CHAN_SEOR;
                        if (chan_dev.dctrl & cmask)
                            sim_debug(DEBUG_EXP, &chan_dev,
                                      "chan %d < DISCO\n", chan);
                    } else
                        chan_fetch(chan);
                }
                break;

                /* Device has word, but has not taken it yet */
            case DEV_WRITE | DEV_FULL:
                continue;       /* Do nothing if no data xfer pending */

                /* Device needs a word of data */
            case DEV_WRITE:     /* Device needs data word */
                /* If we are waiting on EOR, do nothing */
                if (chan_flags[chan] & STA_WAIT)
                     continue;

                /* Special for write segment mark command */
                if (cmd[chan] & CHN_SEGMENT) {
                     /* Send one char */
                     assembly[chan] = SM_MEM;
                     bcnt[chan] = 2;
                     caddr[chan] = limit[chan]+1;
                     chan_flags[chan] &= ~(STA_ACTIVE);
                     chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_FULL|DEV_WEOR;
                     cmd[chan] = CHN_ALPHA|CHN_SEGMENT;
                     chan_info[chan] |= CHAN_NORDW;
                     continue;
                }
                /* Give device new word if we have one */
                if (caddr[chan] <= limit[chan]) {
                    /* Check if got EOR */
                    if (chan_flags[chan] & DEV_REOR) {
                            chan_flags[chan] &= ~(STA_WAIT | DEV_REOR
                                                        | STA_ACTIVE);
                            chan_flags[chan] |= STA_TWAIT;
                            if (chan_dev.dctrl & cmask)
                                sim_debug(DEBUG_EXP, &chan_dev,
                                    "chan %d EOR> %o\n", chan, cmd[chan] & 070);
                            continue;
                    }

                    if (caddr[chan] < MEMSIZE)
                        assembly[chan] = M[caddr[chan]];
                    if (chan_dev.dctrl & cmask)
                        sim_debug(DEBUG_DATA, &chan_dev,
                                      "chan %d data > %011llx\n", chan,
                              assembly[chan]);
                    bcnt[chan] = 10;
                    chan_flags[chan] |= DEV_FULL;

                    /* Check if last transfer before final */
                    if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) {
                        chan_flags[chan] &= ~(STA_ACTIVE);
                        chan_flags[chan] |= STA_TWAIT|STA_WAIT;
                    } else
                        caddr[chan]++;

                    /* Update channel status word */
                    if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) {
                        int     adr = 100 + (chan * 10) + (chan_info[chan]&0xf);
                        upd_idx(&M[adr], caddr[chan]);
                        bin_dec(&M[adr], location[chan], 0, 4);
                    }

                   /* Check for record mark */
                    if ((cmd[chan] & CHN_RECORD) &&
                        (assembly[chan] & SMASK) == ASIGN &&
                        (assembly[chan] & 0xFF) == RM_CHAR) {
                        if (cmd[chan] & CHN_LAST) {
                            chan_flags[chan] &= ~(STA_ACTIVE);
                            chan_flags[chan] |= STA_TWAIT|STA_WAIT;
                        } else
                            chan_fetch(chan);
                    }

                    continue;   /* Don't start next command until data taken */
                }

                /* Wait for device to recognize EOR */
                if (chan_flags[chan] & DEV_WEOR)
                    continue;

                /* Get here if passed limit */
                if (chan_dev.dctrl & cmask)
                    sim_debug(DEBUG_EXP, &chan_dev, "chan %d > WC0 %o stat=%x\n",
                              chan, cmd[chan] & 070, chan_flags[chan]);

                if (cmd[chan] & CHN_LAST) {
                    chan_flags[chan] |= DEV_DISCO | DEV_WEOR | STA_TWAIT;
                    chan_flags[chan] &= ~(STA_START | STA_ACTIVE);
                    if (chan_dev.dctrl & cmask)
                        sim_debug(DEBUG_EXP, &chan_dev,
                                  "chan %d > DISCO\n", chan);
                 } else
                    chan_fetch(chan);
            }
            break;
        case CHAN_7907:
            /* If channel is disconnecting, just hold on */
            if (chan_flags[chan] & DEV_DISCO)
                continue;

            /* If no select, stop channel */
            if ((chan_flags[chan] & DEV_SEL) == 0
                && (chan_flags[chan] & STA_TWAIT)) {
                t_uint64        temp;

                temp = 2;
                if (chan_info[chan] & CHAN_TWE)
                   temp = 1;
                else if (chan_flags[chan] & SNS_UEND)
                   temp = 5;
                else if ((chan_info[chan] & CHAN_SEOR) == 0 && op[chan] == 1)
                   temp = 4;
                else if (caddr[chan] <= limit[chan] &&
                        (op[chan] == 1 || op[chan] == 3))
                   temp = 3;
                temp <<= 36;
                chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2);
                temp |= (chan_irq[chan])?MSIGN:PSIGN;
                chan_flags[chan] &= ~(SNS_UEND|CTL_CNTL|CTL_SNS|CTL_READ|
                                        CTL_WRITE|CTL_PREAD|CTL_PWRITE);
                upd_idx(&temp, caddr[chan]);
                bin_dec(&temp, location[chan], 0, 4);
                if (chan_dev.dctrl & cmask)
                    sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap: %012llx\n",
                              chan, temp);
                M[(chan - 4) + 300] = temp;
                if ((chan_info[chan] & CHAN_PRIO) || ((temp >> 36) & 0xf) != 2)
                      pri_latchs[8] |= 1<<(4-chan);
                chan_flags[chan] &=
                    ~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT);
                chan_info[chan] &= ~CHAN_PRIO;
                continue;
            }

            /* Check if device raised attention */
            if ((chan_flags[chan] & (STA_ACTIVE|DEV_SEL|STA_TWAIT)) == 0 &&
                (chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2))) {
                t_uint64        temp;

                if (chan_dev.dctrl & cmask)
                    sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Attn Trap\n",
                              chan);
                temp = 2;
                if (chan_flags[chan] & SNS_UEND)
                    temp = 5;
                temp <<= 36;
                temp |= MSIGN;
                chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2);
                chan_flags[chan] &= ~(SNS_ATTN1|SNS_ATTN2|SNS_UEND);
                upd_idx(&temp, caddr[chan]);
                bin_dec(&temp, location[chan], 0, 4);
                M[(chan - 4) + 300] = temp;
                pri_latchs[9] |= 1 << (4 - chan);
                continue;
            }

            /* Nothing more to do if not active. */
            if (chan_flags[chan] & STA_ACTIVE) {
                t_uint64        temp;

                /* Execute the next command */
                switch (op[chan]) {
                case 6: /* Tranfer in channel */
                        /* I am not sure if this is correct, but it passes
                           diagnostics */
                        location[chan] = limit[chan];
                        break;
                case 0: /* Write status */
                        temp = PSIGN|(2LL << 36);
                        upd_idx(&temp, caddr[chan]);
                        bin_dec(&temp, location[chan], 0, 4);
                        M[caddr[chan]] = temp;
                        break;
                case 1: /* Read */
                        /* Check if in other mode */
                        if (chan_flags[chan] & (CTL_CNTL)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d read busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_REOR) {
                                chan_flags[chan] &=
                                        ~(CTL_CNTL|DEV_REOR|DEV_WRITE);
                            } else
                                continue;
                        }
                        /* Check if last command not finished */
                        if (chan_flags[chan] & (CTL_SNS|CTL_WRITE)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d read busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_SEL) {
                                chan_flags[chan] |= DEV_DISCO;
                                chan_flags[chan] &= ~(CTL_SNS|CTL_WRITE);
                            }
                            continue;
                        }

                        /* If not already in read mode, set read mode */
                        if ((chan_flags[chan] & CTL_READ) == 0) {
                            chan_flags[chan] |= CTL_READ;
                            chan_flags[chan] &= ~(DEV_FULL|DEV_WRITE|DEV_REOR);
                            chan_info[chan] &= ~CHAN_SEOR;
                            bcnt[chan] = 10;
                            assembly[chan] = 0;
                            continue;
                        }

                        /* Has device given us a word */
                        if (chan_flags[chan] & DEV_FULL) {
                            /* Check if record mark */
                            if ((cmd[chan] & CHN_RECORD) &&
                                (assembly[chan] & SMASK) == ASIGN &&
                                (assembly[chan] & 0xFF) == RM_CHAR) {
                                break;
                            }


                            /* Check if ready to transfer something */
                            if (caddr[chan] <= limit[chan]) {
                                M[caddr[chan]] = assembly[chan];
                                if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d data > %012llx\n",
                                              chan, assembly[chan]);
                                caddr[chan]++;
                                bcnt[chan] = 10;
                                assembly[chan] = 0;
                                chan_flags[chan] &= ~DEV_FULL;
                                /* Check if last word transfered */
                                if (caddr[chan] > limit[chan])
                                    break;
                            }

                            /* Check if we still have a select signal */
                            if ((chan_flags[chan] & DEV_SEL) == 0) {
                                chan_info[chan] |= CHAN_TWE;
                                chan_flags[chan] &=
                                        ~(CTL_WRITE|CTL_END|STA_ACTIVE);
                                break;
                            }

                            /* Device gave us a EOR, get next word */
                            if (chan_flags[chan] & DEV_REOR) {
                                chan_info[chan] |= CHAN_SEOR;
                                chan_flags[chan] &= ~DEV_REOR;
                                break;
                            }
                            continue;
                        }

                        /* Abort if we get control end */
                        if (chan_flags[chan] & CTL_END) {
                            /* Disconnect channel if select still active */
                            if (chan_flags[chan] & DEV_SEL) {
                                chan_flags[chan] |= (DEV_DISCO);
                            }
                            if (chan_flags[chan] & DEV_REOR)
                                chan_info[chan] |= CHAN_SEOR;
                            chan_flags[chan] &=
                                 ~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END);
                            break;
                        }
                        continue;

                case 3: /* Write */
                        /* Check if in other mode */
                        if (chan_flags[chan] & (CTL_CNTL)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d write busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_REOR) {
                                chan_flags[chan] &=
                                         ~(CTL_CNTL|DEV_REOR|DEV_WRITE);
                            } else
                                continue;
                        }
                        /* Check if last command not finished */
                        if (chan_flags[chan] & (CTL_SNS|CTL_READ)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d write busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_SEL) {
                                chan_flags[chan] |= DEV_DISCO;
                                chan_flags[chan] &= ~(CTL_READ|CTL_SNS);
                            }
                            continue;
                        }

                        /* If not in write, flag as write */
                        if ((chan_flags[chan] & CTL_WRITE) == 0) {
                            chan_flags[chan] |= CTL_WRITE|DEV_WRITE;
                        }

                        /* Command finished?*/
                        if (chan_flags[chan] & CTL_END) {
                            /* Disconnect channel if select still active */
                            if (chan_flags[chan] & DEV_SEL) {
                                chan_flags[chan] |= (DEV_DISCO);
                            }
                            chan_flags[chan] &=
                                 ~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END);
                            break;
                        }

                        /* Check if we still have a select signal */
                        if ((chan_flags[chan] & DEV_SEL) == 0 &&
                                    caddr[chan] < limit[chan]) {
                            chan_info[chan] |= CHAN_TWE;
                            chan_flags[chan] &= ~(CTL_WRITE|CTL_END|STA_ACTIVE);
                            break;
                        }

                        /* Check if device needs data */
                        if ((chan_flags[chan] & DEV_FULL) == 0) {
                            /* Got EOR? */
                            if (chan_flags[chan] & DEV_REOR) {
                                if (caddr[chan] > limit[chan]) {
                                    chan_info[chan] |= CHAN_SEOR;
                                }
                                chan_flags[chan] |= DEV_DISCO;
                                chan_flags[chan] &= ~DEV_REOR;
                                break;
                            }


                            /* Check if ready to transfer something */
                            if (caddr[chan] <= limit[chan]) {
                                assembly[chan] = M[caddr[chan]];
                                if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d data > %012llx\n",
                                              chan, assembly[chan]);
                                caddr[chan]++;
                                bcnt[chan] = 10;
                                chan_flags[chan] |= DEV_FULL;
                                /* Check if record mark */
                                if ((cmd[chan] & CHN_RECORD) &&
                                    (assembly[chan] & SMASK) == ASIGN &&
                                    (assembly[chan] & 0xFF) == RM_CHAR) {
                                    chan_flags[chan] |= DEV_WEOR;
                                    break;
                                }
                                continue;
                            }
                            chan_info[chan] |= CHAN_SEOR;
                            break;
                        }
                        continue;

                case 5: /* Sense */
                        /* Check if in other mode */
                        if (chan_flags[chan] & (CTL_CNTL)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d sense busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_REOR) {
                                chan_flags[chan] &=
                                        ~(CTL_CNTL|DEV_REOR|DEV_WRITE);
                            } else
                                continue;
                        }
                        if (chan_flags[chan] & CTL_SNS) {
                            /* Check if we still have a select signal */
                            if ((chan_flags[chan] & DEV_SEL) == 0) {
                                chan_info[chan] |= CHAN_TWE;
                                chan_flags[chan] &= ~(CTL_SNS|STA_ACTIVE);
                                chan_flags[chan] |= STA_TWAIT;
                                break;
                            }

                            /* If device ended, quit transfer */
                            if ((chan_flags[chan] & DEV_FULL) == 0) {
                                if (chan_flags[chan] & CTL_END) {
                                /* Disconnect channel if select still active */
                                    if (chan_flags[chan] & DEV_SEL) {
                                        chan_flags[chan] |= (DEV_DISCO);
                                    }
                                    if (chan_flags[chan] & DEV_REOR) {
                                        chan_info[chan] |= CHAN_SEOR;
                                        chan_flags[chan] &= ~DEV_REOR;
                                    }
                                    chan_flags[chan] &= ~(CTL_SNS);
                                    break;
                                }

                                /* Check if last word transfered */
                                if (caddr[chan] > limit[chan]) {
                                    if (chan_flags[chan] & SNS_UEND) {
                                        chan_flags[chan] |=
                                            (DEV_DISCO | DEV_WEOR);
                                        chan_flags[chan] &= ~(DEV_SEL);
                                    } else {
                                        if (chan_flags[chan] & DEV_REOR) {
                                            chan_flags[chan] &= ~DEV_REOR;
                                            chan_info[chan] |= CHAN_SEOR;
                                        }
                                    }
                                    chan_flags[chan] &= ~(CTL_SNS);
                                    break;
                                }
                            } else {
                                /* Device has given us a dataword */
                                 if (chan_dev.dctrl & cmask)
                                     sim_debug(DEBUG_DATA, &chan_dev,
                                               "chan %d data < %012llx\n",
                                               chan, assembly[chan]);
                                 M[caddr[chan]] = assembly[chan];
                                 assembly[chan] = 0;
                                 bcnt[chan] = 10;
                                 chan_flags[chan] &= ~DEV_FULL;
                                 if (caddr[chan] >= limit[chan])
                                     break;
                                 caddr[chan]++;
                            }
                            /* Handle EOR on sense */
                            if (chan_flags[chan] & DEV_REOR) {
                                chan_flags[chan] &= ~(CTL_SNS|DEV_REOR);
                                chan_info[chan] |= CHAN_SEOR;
                                break;
                            }
                            continue;
                        }

                        /* Check if in other mode */
                        if (chan_flags[chan] & (CTL_CNTL|CTL_READ|CTL_WRITE)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d sense busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_SEL) {
                               chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT;
                            }
                            chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE);
                            continue;
                        }

                        /* Start sense command */
                        chan_flags[chan] |= CTL_SNS;
                        chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL);
                        switch(chan_issue_cmd(chan,0,chan_test(chan, CTL_SEL))){
                        case SCPE_IOERR:
                        case SCPE_NODEV:
                                chan_info[chan] |= CHAN_TWE;
                                chan_flags[chan] &= ~STA_ACTIVE;
                        case SCPE_BUSY: /* Device not ready yet, wait */
                                chan_flags[chan] &= ~(CTL_SNS);
                                continue;
                        case SCPE_OK:
                                        /* Device will be waiting for command */
                                break;
                        }
                        /* Get channel ready to transfer */
                        chan_flags[chan] &= ~DEV_WRITE;
                        chan_flags[chan] |= DEV_SEL;
                        continue;

                case 4: /* Transfer command */
                        if (chan_flags[chan] & CTL_CNTL)
                            goto xfer;
                        if (chan_flags[chan] & (CTL_READ|CTL_WRITE|CTL_SNS)) {
                            if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_DATA, &chan_dev,
                                              "chan %d control busy %04x\n",
                                              chan, chan_flags[chan]);
                            if (chan_flags[chan] & DEV_SEL) {
                               chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT;
                            }
                            chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE);
                            continue;
                        }
                        chan_flags[chan] |= CTL_CNTL;
                        /* Get channel ready to transfer */
                        chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL);

                        switch(chan_issue_cmd(chan,0,chan_stat(chan,CTL_SEL))){
                        case SCPE_IOERR:
                        case SCPE_NODEV:
                                chan_info[chan] |= CHAN_TWE;
                                chan_flags[chan] &=
                                                ~(CTL_SNS|CTL_CNTL|STA_ACTIVE);
                                continue;
                            case SCPE_BUSY:
                                /* Device not ready yet, wait */
                                continue;
                            case SCPE_OK:
                                 /* Device will be waiting for command */
                                break;
                        }

                        chan_flags[chan] |= DEV_WRITE;
                      xfer:
                        /* Check if comand tranfer done */
                        if (chan_flags[chan] & DEV_REOR) {
                            chan_flags[chan] &=
                                      ~(DEV_WRITE|DEV_REOR|DEV_FULL|CTL_CNTL);
                            chan_info[chan] |= CHAN_SEOR;
                            break;
                        }

                        /* Wait for device to grab the command */
                        if (chan_flags[chan] & DEV_FULL)
                            continue;

                        /* Check if device ready for next command word */
                        if ((chan_flags[chan] & (DEV_WRITE | DEV_FULL)) ==
                            DEV_WRITE) {
                            if (caddr[chan] <= limit[chan]) {
                                assembly[chan] = M[caddr[chan]];
                                chan_flags[chan] |= DEV_FULL;
                                bcnt[chan] = 10;
                                if (chan_dev.dctrl & cmask)
                                    sim_debug(DEBUG_CMD, &chan_dev,
                                          "chan %d cmd > %012llx\n",
                                          chan, assembly[chan]);
                                if (caddr[chan] < limit[chan])
                                    caddr[chan]++;
                                continue;
                            }
                        }
                        break;

                case 2: /* Read Backwards */
                        /* Unknown commands at moment */
                case 7:
                case 8:
                case 9:
                        chan_info[chan] |= CHAN_TWE;
                        if (chan_flags[chan] & DEV_SEL)
                             chan_flags[chan] |= DEV_DISCO;
                        chan_flags[chan] &=
                             ~(STA_ACTIVE|CTL_WRITE|CTL_READ|CTL_CNTL|CTL_SNS);
                        break;

                    }

                    /* If last all done */
                    if (cmd[chan] & CHN_LAST || chan_flags[chan] & (SNS_UEND)
                        || chan_info[chan] & CHAN_TWE) {
                        if (chan_flags[chan] & DEV_SEL)
                            chan_flags[chan] |= DEV_DISCO;
                        chan_flags[chan] &= ~(STA_ACTIVE);
                        chan_flags[chan] |= STA_TWAIT;
                    } else
                        chan_fetch(chan);
                }
                continue;
        }
    }
}

void
chan_fetch(int chan)
{
    uint32              loc = location[chan];
    t_uint64            temp;

    sim_interval--;
    chan_info[chan] &= ~CHAN_START;
    if (loc < MEMSIZE)
        temp = M[loc];
    else {
        cmd[chan] |= CHN_LAST;
        return;
    }
    location[chan] = (loc + 1);
    if ((temp & SMASK) == MSIGN)
        cmd[chan] |= CHN_LAST;
    get_rdw(temp, &caddr[chan], &limit[chan]);
    op[chan] = (temp >> 36) & 0xf;
    if (chan_dev.dctrl & (0x0100 << chan))
        sim_debug(DEBUG_CHAN, &chan_dev,
          "chan %d fetch adr=%05d op=%d cmd=%03o caddr=%05d limit=%05d\n",
          chan, loc, op[chan], cmd[chan], caddr[chan], limit[chan]);
}

void chan_set_attn_a(int chan) {
    pri_latchs[0] |= 0x002;
}

void chan_set_attn_b(int chan) {
    pri_latchs[0] |= 0x004;
}

void chan_set_attn_inq(int chan) {
    if (chan == CHAN_UREC)
        pri_latchs[0] |= 0x080;
    else
        pri_latchs[0] |= 0x100;
}

void chan_clear_attn_inq(int chan) {
    if (chan == CHAN_UREC)
        pri_latchs[0] &= ~0x080;
    else
        pri_latchs[0] &= ~0x100;
}


/* Issue a command to a channel */
int
chan_cmd(uint16 dev, uint16 dcmd, uint16 addr)
{
    uint32              chan;
    int                 prio;
    t_stat              r;

    /* Find device on given channel and give it the command */
    chan = (dev >> 8) & 0xf;
    /* If no channel device, quick exit */
    if (chan_unit[chan].flags & UNIT_DIS)
        return SCPE_IOERR;
    /* Unit is busy doing something, wait */
    if (chan_flags[chan] & (DEV_SEL|DEV_DISCO|STA_TWAIT|STA_WAIT|STA_ACTIVE))
        return SCPE_BUSY;
    /* Ok, try and find the unit */
    prio = (dev & 0x1000)? 1: 0;
    dev &= 0xff;
    location[chan] = addr;
    cmd[chan] = dcmd & 0xff;
    dcmd >>= 8;
    chan_info[chan] = (dev & 0xf) | (chan << 4);
    chan_info[chan] |= CHAN_START;
    /* Special check for console */
    if (chan == 0 && dev == 0)
        chan_info[chan] |= CHAN_OUTDEV;

    /* Check for octal translation */
    if (chan == 1 && dev & 020)
        chan_info[chan] |= CHAN_OCTAL;

    /* Enable priority */
    if (prio)
        chan_info[chan] |= CHAN_PRIO;
    assembly[chan] = 0;
    bcnt[chan] = 10;

    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7907) {
        chan_flags[chan] |= STA_ACTIVE;
        if (dev & 1)
           chan_flags[chan] |= CTL_SEL;
        else
           chan_flags[chan] &= ~CTL_SEL;
        chan_fetch(chan);
        return SCPE_OK;
    }
    r = chan_issue_cmd(chan, dcmd, dev);
    if (r != SCPE_OK) {
        /* No device, kill active */
        chan_flags[chan] &= ~(STA_ACTIVE);
    } else {
        extern uint32   IC;
        /* If transfering data, activate channel */
        if (chan_flags[chan] & DEV_SEL)
                chan_flags[chan] |= STA_ACTIVE;

        if (chan_dev.dctrl & (0x0100 << chan))
             sim_debug(DEBUG_CMD, &chan_dev,
                "chan %d cmd=%o IC=%05d addr=%05d\n\r", chan, dcmd, IC, addr);
    }
    return r;
}

/*
 * Write a word to the assembly register.
 */
int
chan_write(int chan, t_uint64 * data, int flags)
{
    return TIME_ERROR;
}

/*
 * Read next word from assembly register.
 */
int
chan_read(int chan, t_uint64 * data, int flags)
{
    return TIME_ERROR;
}

/*
 * Write a char to the assembly register.
 */
int
chan_write_char(int chan, uint8 * data, int flags)
{
    uint8       ch = *data;
    /* Check if last data still not taken */
    if (chan_flags[chan] & DEV_FULL) {
        /* Nope, see if we are waiting for end of record. */
        if (chan_flags[chan] & DEV_WEOR) {
            chan_flags[chan] &= ~DEV_WEOR;
            chan_flags[chan] |= DEV_REOR;
            return END_RECORD;
        }
        if (chan_flags[chan] & STA_ACTIVE) {
            chan_flags[chan] |= CHS_ATTN;       /* We had error */
        }
        if (chan == 0) {
            chan_flags[chan] |= DEV_DISCO;
        }
        return TIME_ERROR;
    }

#if 0  /* Check segment working before removing */
    /* Check for segment mark */
    if (flags & DEV_REOR && (chan_info[chan] & CHAN_FIRST) == 0) {
        if (ch == SM_CHAR)
            chan_info[chan] |= CHAN_SEOS;
        assembly[chan] |= ASIGN|(((t_uint64)bcd_mem[ch]) << 32);
        if (cmd[chan] & CHN_ALPHA)
            chan_flags[chan] |= DEV_FULL|DEV_REOR;
        else
            chan_flags[chan] |= DEV_REOR;
        chan_flags[chan] &= ~DEV_WRITE;
        chan_proc();
        return END_RECORD;
    }

    /* Clear first char in record flag */
    chan_info[chan] |= CHAN_FIRST;
#endif

    if (ch == DELTA_CHAR && (cmd[chan] & CHN_ALPHA) == 0) {
        if (bcnt[chan] == 10)
            cmd[chan] ^= CHN_NUM_MODE;
        else
            chan_info[chan] |= CHAN_TWE;
    } else {
        if (chan_flags[chan] & CTL_SNS) {
            /* Set sign based on attention signal */
            if (bcnt[chan] == 10) {
                if (chan_irq[chan] & (SNS_ATTN1 >> (chan_info[chan] & 1)))
                    assembly[chan] = PSIGN;
                else
                    assembly[chan] = MSIGN;
                chan_irq[chan] &= ~(SNS_ATTN1 >> (chan_info[chan] & 1));
            }
            /* Store character */
            ch &= 0x17;
            if (ch & 0x04)
                ch ^= 0x24;     /* Bit move */
            ch |= 0x44;
            bcnt[chan]-=2;
            assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan]);
        } else if (cmd[chan] & CHN_NUM_MODE) {
            ch &= 0xf;
            if (ch == 0 || ch > 10)
              chan_info[chan] |= CHAN_TWE;
            else if (ch == 10)
                ch = 0;
            bcnt[chan]--;
            assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan])|PSIGN;
            /* Check for sign digit */
            switch(*data & 060) {
            case 0:     /* Normal digit */
            case 020:   /* error */
                      if (bcnt[chan] == 0)
                          chan_info[chan] |= CHAN_TWE;
                      break;
            case 040:
                      if (bcnt[chan] > 5)
                          chan_info[chan] |= CHAN_TWE;
                      assembly[chan] &= DMASK;
                      /* Put number in right location */
                      while(bcnt[chan] != 0) {
                         bcnt[chan]--;
                         assembly[chan] >>= 4;
                      }
                      assembly[chan] |= MSIGN;
                      break;
            case 060:
                      if (bcnt[chan] > 5)
                          chan_info[chan] |= CHAN_TWE;
                      assembly[chan] &= DMASK;
                      /* Put number in right location */
                      while(bcnt[chan] != 0) {
                         bcnt[chan]--;
                         assembly[chan] >>= 4;
                      }
                      assembly[chan] |= PSIGN;
                      break;
            }
        } else {
            if (chan_info[chan] & CHAN_OCTAL)
                ch = ((ch & 070) << 1) | (ch & 07);
            else
                ch = bcd_mem[ch];
            if (ch == 0xFF) {
                chan_info[chan] |= CHAN_TWE;
                ch = 0;
            }
            bcnt[chan] -= 2;
            assembly[chan] |= ((t_uint64)ch) << (8 * (bcnt[chan] / 2));
            assembly[chan] |= (chan_info[chan] & CHAN_OCTAL)?PSIGN:ASIGN;
        }
    }

    if (flags & DEV_REOR) {
        chan_flags[chan] |= DEV_FULL|DEV_REOR;
        chan_flags[chan] &= ~DEV_WRITE;
        if (bcnt[chan] != 0 && ((cmd[chan] & (CHN_NUM_MODE)) == 0 ||
                (cmd[chan] & (CHN_ALPHA)) != 0))
           chan_info[chan] |= CHAN_SCLR;
        chan_info[chan] |= CHAN_SEOR;
        chan_proc();
        return END_RECORD;
    } else if (bcnt[chan] == 0) {
        chan_flags[chan] |= DEV_FULL;
        chan_flags[chan] &= ~DEV_WRITE;
        chan_proc();
    }

    /* If Writing end of record, abort */
    if (flags & DEV_WEOR) {
        chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR);
        return END_RECORD;
    }

    return DATA_OK;
}

/*
 * Read next char from assembly register.
 */
int
chan_read_char(int chan, uint8 * data, int flags)
{
    uint8       ch;
    /* Return END_RECORD if requested */
    if (flags & DEV_WEOR) {
        chan_flags[chan] &= ~(DEV_WEOR /*| STA_WAIT*/);
        return END_RECORD;
    }

    chan_proc();

    /* Check if he write out last data */
    if ((chan_flags[chan] & DEV_FULL) == 0) {
        if (chan_flags[chan] & DEV_WEOR) {
            chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT | DEV_WRITE);
            chan_flags[chan] |= DEV_REOR|DEV_DISCO;
            return END_RECORD;
        }
        if (chan_flags[chan] & STA_ACTIVE) {
            chan_flags[chan] |= CHS_ATTN;
        }
        if (chan == 0) {
            chan_flags[chan] |= DEV_DISCO;
        }
        return TIME_ERROR;
    }

    /* Send control words differently */
    if (chan_flags[chan] & CTL_CNTL) {
        if ((assembly[chan] & SMASK) == ASIGN) {
            bcnt[chan] -= 2;
            ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff;
            *data = mem_bcd[ch];
        } else {
            bcnt[chan]--;
            ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf;
            if (ch == 0)
               ch = 10;
            *data = ch;
        }
        goto done;
    }

    /* Handle console type out */
    if (chan_info[chan] & CHAN_OUTDEV) {
        if (bcnt[chan] == 10 && (cmd[chan] & CHN_NUM_MODE) == 0) {
            switch(assembly[chan] & SMASK) {
            case ASIGN: break;
            case PSIGN: *data = 060;
                        cmd[chan] |= CHN_NUM_MODE;
                        return SCPE_OK;
            case MSIGN: *data = 040;
                        cmd[chan] |= CHN_NUM_MODE;
                        return SCPE_OK;
            }
        }
        if (cmd[chan] & CHN_NUM_MODE) {
            bcnt[chan]--;
            ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf;
            if (ch == 0)
               ch = 10;
            if (bcnt[chan] == 0)
                cmd[chan] &= ~CHN_NUM_MODE;
            *data = ch;
        } else {
            bcnt[chan] -= 2;
            ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff;
            *data = mem_bcd[ch];
        }
        goto done;
    }

    /* Check for mode change */
    if (bcnt[chan] == 10  && (cmd[chan] & (CHN_ALPHA)) == 0) {
         if (((assembly[chan] & SMASK) == ASIGN &&
                          (cmd[chan] & CHN_NUM_MODE) == CHN_NUM_MODE)
             ||((assembly[chan] & SMASK) != ASIGN &&
                          (cmd[chan] & CHN_NUM_MODE) == CHN_ALPHA_MODE)) {
             *data = DELTA_CHAR;
             cmd[chan] ^= CHN_NUM_MODE;
             return DATA_OK;
        }
        /* Handle zero compression here */
        if ((cmd[chan] & (CHN_NUM_MODE|CHN_COMPRESS)) ==
                        (CHN_NUM_MODE|CHN_COMPRESS)) {
            while((assembly[chan] >> (4 * bcnt[chan]) & 0xf) == 0 &&
                bcnt[chan] < 5)
                bcnt[chan]--;
        }
    }

    /* If in number mode, dump as number */
    if (cmd[chan] & CHN_NUM_MODE) {
        bcnt[chan]--;
        ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf;
        if (ch == 0)
           ch = 10;
        if (bcnt[chan] == 0)
           ch |= ((assembly[chan] & SMASK) == MSIGN)? 040: 060;
        *data = ch;
    } else {
        bcnt[chan] -= 2;
        ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff;
        *data = mem_bcd[ch];
    }

done:
    if (bcnt[chan] == 0) {
        chan_flags[chan] &= ~DEV_FULL;
        bcnt[chan] = 10;
    }
    /* If end of record, don't transfer any data */
    if (flags & DEV_REOR) {
        chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL);
        chan_flags[chan] |= DEV_REOR;
        chan_proc();
    } else
        chan_flags[chan] |= DEV_WRITE;
    return DATA_OK;
}

void
chan_set_load_mode(int chan)
{
    cmd[chan] &= ~CHN_ALPHA;
    cmd[chan] |= CHN_NUM_MODE;
}

void
chan9_set_error(int chan, uint32 mask)
{
    if (chan_flags[chan] & mask)
        return;
    chan_flags[chan] |= mask;
}

t_stat
chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
   fprintf (st, "%s\n\n", chan_description(dptr));
   fprintf (st, "The 7070 supports up to 8 channels. Channel models include\n\n");
   fprintf (st, "        7604            standard multiplexor channel\n");
   fprintf (st, "        7907            advanced capabilities channel\n\n");
   fprintf (st, "Channels are fixed on the 7070.\n\n");
   fprintf (st, "Channel * is for unit record devices.\n");
   fprint_set_help(st, dptr);
   fprint_show_help(st, dptr);
   return SCPE_OK;
}

const char *
chan_description(DEVICE *dptr)
{
    return "IBM 7070 channel controller";
}


