/* i7090_chan.c: IBM 7090 Channel simulator

   Copyright (c) 2005, 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
   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.

   channel		

   The system state for the IBM 7090 channel is:
   There are 4 types of channel:
	704:		Basic polled mode transfer. Channel only manages
			status and disconnect of devices.
       7607:		Basic channel.
       7909:		Enhanced channel for disk, hypertape and com controlers.
       7289:		Special CTSS channel, like 7607, but first command
			is drum address.

   Common registers to all but 704 channels.
   ADDR<0:16>		Address of next command.
   CMD<0:6>		Channel command.
   WC<0:15>		Word count remaining.
   ASM<0:35>		Assembled data from devices.
   LOCATION<0:16>       Location to read or write next word from.

   7909 adds following two registers.
   SMS<0:6>		Select register.
   COUNT<0:6>		Counter.

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

extern DEVICE      *sim_devices[];
extern uint16       iotraps;
extern uint8        iocheck;
extern uint8        dualcore;
extern uint32       sim_interval;
extern UNIT         cpu_unit;
extern uint16       IC;
extern t_uint64     MQ;
extern uint32       drum_addr;
extern uint32       hsdrm_addr;

t_stat              chan_reset(DEVICE * dptr);
void                chan_fetch(int chan);
void		    chan9_seqcheck(int chan);
uint32              dly_cmd(UNIT *, uint16, uint16);  

/* Channel data structures

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

uint16              caddr[NUM_CHAN];	      /* Channel memory address */
uint8		    bcnt[NUM_CHAN];	      /* Channel character count */
uint8               cmd[NUM_CHAN];	      /* Current command */
uint16              wcount[NUM_CHAN];	      /* Word count */
t_uint64            assembly[NUM_CHAN];	      /* Assembly register */
uint16              location[NUM_CHAN];	      /* Pointer to next opcode */
uint32              chan_flags[NUM_CHAN];    /* Unit status */
uint16              chan_info[NUM_CHAN];      /* Private channel info */
uint8               counter[NUM_CHAN];	      /* Channel counter */
uint8               sms[NUM_CHAN];	      /* Channel mode infomation */
uint8               chan_irq[NUM_CHAN];	      /* Channel has a irq pending */

/* 7607 channel commands */
#define IOCD	000
#define TCH	010
#define IORP	020
#define IORT	030
#define IOCP	040
#define IOCT	050
#define IOSP	060
#define IOST	070

/* 7909 channel commands */
#define WTR     000
#define WTRX    004
#define XMT	001
#define XMTX	005
#define TCH9	010
#define TCHX	014
#define LIPT	011
#define LIPTX	015
#define CTL 	020
#define CTLR	021
#define CTLW	024
#define SNS	025
#define LAR     030
#define SAR     031
#define TWT     034
#define XXXX    035
#define CPYP	040
#define CPYP2	041
#define CPYP3	044
#define CPYP4	045
#define CPYD	050
#define TCM	051
#define CPYDX	054
#define TCMX	055
#define XXXZ    060
#define LIP     061
#define TDC     064
#define LCC     065
#define SMS     070
#define ICC	071
#define ICCX	075

/* Values for chan_info */
#define CHAINF_START	1	/* Channel started */
#define CHAINF_RUN	2	/* Transfer in progress */

#define nxt_chan_addr(chan) caddr[chan] = \
			((dualcore) ? (0100000 & caddr[chan]) : 0) | \
				((caddr[chan] + 1) & (MEMMASK));

/* Delay device for IOD instruction */
DIB                 dly_dib =
#ifdef I701
    { CH_TYP_PIO, 1, 2052, 07777, &dly_cmd, NULL };
#else
    { CH_TYP_PIO, 1, 0333, 07777, &dly_cmd, NULL };
#endif                                                     


UNIT                chan_unit[] = {
    /* Puesdo channel for 704 devices */
    {UDATA(NULL, UNIT_DISABLE | CHAN_SET | 
			CHAN_S_TYPE(CHAN_PIO)|UNIT_S_CHAN(0), 0)},
    /* Normal channels */
#if NUM_CHAN > 1
    {UDATA(NULL, CHAN_AUTO | CHAN_SET | CHAN_S_TYPE(CHAN_7607)|
					UNIT_S_CHAN(CHAN_A), 0)},	/* A */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_B), 0)},	/* B */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_C), 0)},	/* C */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_D), 0)},	/* D */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_E), 0)},	/* E */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_F), 0)},	/* F */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_G), 0)},	/* G */
    {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_H), 0)}	/* H */
#endif
};

REG                 chan_reg[] = {
    {BRDATA(ADDR, caddr, 8, 16, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(WC, wcount, 8, 15, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(ASM, assembly, 8, 36, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(LOCATION, location, 8, 16, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT},
    {BRDATA(COUNTER, counter, 8, 6, NUM_CHAN), REG_RO|REG_FIT },
    {BRDATA(SMS,  sms, 2, 6, NUM_CHAN), REG_RO|REG_FIT},
    {NULL}
};

MTAB                chan_mod[] = {
#ifdef I7090
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_PIO), "704 Channel", NULL, NULL, NULL, NULL},
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_7607), "7607", "7607", NULL, NULL, NULL},
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_7909), "7909", "7909", NULL, NULL, NULL},
    {CHAN_MODEL, CHAN_S_TYPE(CHAN_7289), "7289", "7289", NULL, NULL, NULL},
    {CHAN_AUTO, 0, "FIXED", "FIXED", NULL, NULL, NULL},
    {CHAN_AUTO, CHAN_AUTO, "AUTO", "AUTO", NULL, NULL, NULL},
    {CHAN_SET, CHAN_SET, "set", NULL, NULL, NULL, NULL},
    {MTAB_VUN, 0,  "Units",  NULL, NULL, &print_chan, NULL},
#endif
    {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},
    {"CHA", 0x0100 << 1},
    {"CHB", 0x0100 << 2},
    {"CHC", 0x0100 << 3},
    {"CHD", 0x0100 << 4},
    {"CHE", 0x0100 << 5},
    {"CHF", 0x0100 << 6},
    {"CHG", 0x0100 << 7},
    {"CHH", 0x0100 << 8},
    {0, 0}
};

DEVICE              chan_dev = {
    "CH", chan_unit, chan_reg, chan_mod,
    NUM_CHAN, 8, 15, 1, 8, 36,
    NULL, NULL, &chan_reset, NULL, NULL, NULL,
    &dly_dib, DEV_DEBUG, 0, chn_debug
};


/* Nothing special to do, just return true if cmd is write and we got here */
uint32 dly_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
{
    if (cmd == IO_WRS) 
        return SCPE_OK;
    return SCPE_NODEV;
}
                            
    

t_stat
chan_reset(DEVICE * dptr)
{
    int                 i;

    /* Clear channel assignment */
    for (i = 0; i < NUM_CHAN; i++) {
	if (chan_unit[i].flags & CHAN_AUTO)
	    chan_unit[i].flags &= ~CHAN_SET;
	else
	    chan_unit[i].flags |= CHAN_SET;
	chan_flags[i] = 0;
	chan_info[i] = 0;
	caddr[i] = 0;
	cmd[i] = 0;
	sms[i] = 0;
	bcnt[i] = 6;
	chan_irq[i] = 0;
	wcount[i] = 0;
	location[i] = 0;
	counter[i] = 0;
    }
    return chan_set_devs(dptr);
}

/* Boot from given device */
t_stat
chan_boot(int32 unit_num, DEVICE * dptr)
{
    /* Tell device to do a read, 3 records */
    /* Set channel address = 0, wc = 3, location = 0, CMD=0 */
    /* Set M[1] = TCO? 1, IC = 1 */
    UNIT               *uptr = &dptr->units[unit_num];
    int                 chan = UNIT_G_CHAN(uptr->flags);

    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) {
	IC = 0;
    } else {
	IC = 1;
	/* Grab next channel command */
	location[chan] = 0;
	chan_fetch(chan);
    }
    chan_flags[chan] |= STA_ACTIVE;
    chan_flags[chan] &= ~STA_PEND;
    return SCPE_OK;
}

/* Preform BCD to binary translation for 7909 channel */
void
bcd_xlat(int chan, int direction)
{
    int                 i;
    t_uint64            na = 0;

    for (i = 30; i >= 0; i -= 6) {
	uint8               ch = (uint8)(assembly[chan] >> i) & 077;

	if (direction) {	/* D->M Read */
	    switch (ch & 060) {
	    case 000:
		if (ch == 0)
		    ch = 060;
		else if (ch == 012)
		    ch = 0;
		break;
	    case 020:	
	    case 060:
		ch ^= 040;
	    case 040:
		break;
	    }
	} else {		/* M->D Write */
	    switch (ch & 060) {
	    case 000:
		if (ch == 0)
		    ch = 012;
		else if (ch == 012)
		    ch = 020;
		break;
	    case 060:
		if (ch == 060)
		    ch = 060;	/* => 000 */
	    case 020:
		ch ^= 040;
	    case 040:
		break;
	    }
	}
	na |= ((t_uint64) ch) << i;
    }
    assembly[chan] = na;
}

/* 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;

	/* If channel is disconnecting, do nothing */
	if (chan_flags[chan] & DEV_DISCO)
	    continue;

	cmask = 0x0100 << chan;
	switch (CHAN_G_TYPE(chan_unit[chan].flags)) {
	case CHAN_PIO:
	    if (chan_flags[chan] & CHS_ATTN) {
		chan_flags[chan] &=
		    ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT);
	    }
	    if ((chan_flags[chan] & (DEV_REOR|DEV_SEL|DEV_FULL)) == 
			(DEV_SEL|DEV_REOR))  {
	        sim_debug(DEBUG_DETAIL, &chan_dev, "chan got EOR\n", chan);
		chan_flags[chan] |= (DEV_DISCO);
	    }

	    break;
#ifdef I7090
	case CHAN_7289:	/* Special channel for HS drum */
	    /* On first command, copy it to drum address and load another */
	    if ((chan_info[chan] & (CHAINF_RUN | CHAINF_START)) ==
		CHAINF_START) {
		hsdrm_addr = (int)M[location[chan] - 1];
		chan_info[chan] |= CHAINF_RUN;
		chan_fetch(chan);
		continue;
	    }
	    if ((chan_info[chan] & CHAINF_START) == 0)
		continue;
	    /* Fall through and behave like 7607 from now on */
	case CHAN_7607:
	    /* If no select, stop channel */
	    if ((chan_flags[chan] & DEV_SEL) == 0
		&& (chan_flags[chan] & STA_TWAIT)) {
		if (chan_dev.dctrl & cmask)
		    sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap\n",
			      chan);
		iotraps |= 1 << chan;
		chan_flags[chan] &=
		    ~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT);
		chan_info[chan] = 0;
		continue;
	    }

	    /* If device requested attention, abort current command */
	    if (chan_flags[chan] & CHS_ATTN) {
		if (chan_flags[chan] & DEV_SEL)
		    chan_flags[chan] |= (DEV_DISCO);
		chan_flags[chan] &=
		    ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT);
		chan_info[chan] = 0;
		if (chan_dev.dctrl & cmask)
		     sim_debug(DEBUG_DETAIL, &chan_dev, 
			"chan %d attn< %o\n", chan, cmd[chan] & 070);
		continue;
	    }

	    /* If we are waiting and get EOR, then continue along */
	    if ((chan_flags[chan] & (STA_WAIT|DEV_REOR|DEV_FULL)) == 
			(STA_WAIT|DEV_REOR))  {
		chan_flags[chan] &= ~(STA_WAIT|DEV_WEOR);
		if (chan_dev.dctrl & cmask)
	            sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d clr wait EOR\n",
				 chan);
	    }

	    /* All done if waiting for EOR */
	    if (chan_flags[chan] & STA_WAIT)
		continue;

	    /* No activity, nothing happening here folks, move along */
	    if ((chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) == 0) {
	        /* Check if Trap wait and no pending LCHx, force disconnect */
	        if ((chan_flags[chan] & (STA_TWAIT|STA_PEND|DEV_SEL))
			 == (STA_TWAIT|DEV_SEL)) 
		    chan_flags[chan] |= DEV_DISCO|DEV_WEOR;
		continue;
	    }

	    /* If command is a transfer, Do transfer */
	    if ((cmd[chan] & 070) == TCH) {
		location[chan] = caddr[chan];
		chan_fetch(chan);
		/* Give up bus if next command is a tranfer. */
	        if ((cmd[chan] & 070) == TCH) 
		    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:
		/* If we are not waiting EOR save it in memory */
//		if ((chan_flags[chan] & STA_WAIT) == 0) {
		    if ((cmd[chan] & 1) == 0) {
		        if (chan_dev.dctrl & cmask)
			     sim_debug(DEBUG_DATA, &chan_dev,
				       "chan %d data < %012llo\n",
				       chan, assembly[chan]);
			M[caddr[chan]] = assembly[chan];
		    } else {
		        if (chan_dev.dctrl & cmask)
			     sim_debug(DEBUG_DATA, &chan_dev,
				       "chan %d data * %012llo\n",
				       chan, assembly[chan]);
		    }
		    nxt_chan_addr(chan);
		    assembly[chan] = 0;
		    bcnt[chan] = 6;
		    wcount[chan]--;
		    chan_flags[chan] &= ~DEV_FULL;
//		}

		/* Device does not need a word and has not given us one */
	    case 0:
		/* Device idle, expecting data from it */

		/* Check if got EOR */
		if (chan_flags[chan] & DEV_REOR) {
		    switch (cmd[chan] & 070) {
		    case IORP:
		    case IOSP:
			chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR/* | STA_WAIT*/);
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev,
					 "chan %d EOR< %o\n",
				  chan, cmd[chan] & 070);
			chan_fetch(chan);
			chan_flags[chan] |= STA_ACTIVE;
			continue;	/* Handle new command next time */
		    case IORT:
		    case IOST:
			chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR);
			chan_flags[chan] &= ~(STA_ACTIVE/*|STA_WAIT*/);
			chan_flags[chan] |= STA_TWAIT;
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
					"chan %d EOR< %o\n",
				  	chan, cmd[chan] & 070);
			continue;
		    }
		}

		/* Done with transfer */
		if (wcount[chan] == 0
		   /* && (chan_flags[chan] & STA_WAIT) == 0*/) {
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_DETAIL, &chan_dev,
				  "chan %d < WC0 %o\n", chan,
				  cmd[chan] & 070);
		    switch (cmd[chan] & 070) {
		    case IOCD:	/* Transfer and disconnect */
			chan_flags[chan] |= DEV_DISCO | DEV_WEOR;
			chan_flags[chan] &=
			    ~(STA_START | STA_ACTIVE | STA_PEND);
			if (CHAN_G_TYPE(chan_unit[chan].flags) ==
			    CHAN_7289)  {
				iotraps |= 1 << chan;
		            sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap\n",
			             chan);
			}
			chan_info[chan] = 0;
			break;

		    case IORP:	/* Transfer until end of record */
			chan_flags[chan] |= STA_WAIT | DEV_WEOR;
			break;
		    case IOSP:	/* Transfer and proceed */
		    case IOCP:	/* Transfer and proceed, no eor */
			chan_fetch(chan);
			break;

		    case IORT:	/* Transfer, continue if LCH pending, */
			/* else trap, Skip rest of record */
			chan_flags[chan] |= STA_WAIT | DEV_WEOR /*| STA_TWAIT*/;
//			chan_flags[chan] &= ~(STA_ACTIVE);
			break;
		    case IOST:	/* Transfer, continue if LCH, else trap */
		    case IOCT:	/* Transfer but no end of record, else trap */
			chan_flags[chan] &= ~(STA_ACTIVE/*|STA_WAIT*/);
			chan_flags[chan] |= STA_TWAIT;
			break;
		    }
		}

		/* Check if device left us */
		if ((chan_flags[chan] & DEV_SEL) == 0) {
		    switch (cmd[chan] & 070) {
		    case IOCP:
		    case IORP:
		    case IOSP:
		    case IOCD:
			chan_flags[chan] &= ~(STA_START|STA_ACTIVE|STA_WAIT);
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
				"chan %d -Sel< %o\n", chan, cmd[chan] & 070);
			continue;	/* Handle new command next time */
		    case IOCT:
		    case IORT:
		    case IOST:	/* Behave like EOR */
			chan_flags[chan] &= ~(STA_ACTIVE|STA_WAIT);
			chan_flags[chan] |= STA_TWAIT;
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
				"chan %d -Sel< %o\n", chan, cmd[chan] & 070);
			continue;
		    }
		}

		break;

		/* Device has word, but has not taken it yet */
	    case DEV_WRITE | DEV_FULL:
		if (chan_flags[chan] & DEV_REOR) {
		    switch (cmd[chan] & 070) {
		    case IORP:
		    case IORT:
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
				"chan %d EOR>+ %o\n", chan, cmd[chan] & 070);
			chan_flags[chan] &= ~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;

		/* Check if device left us */
		if ((chan_flags[chan] & DEV_SEL) == 0) {
		    switch (cmd[chan] & 070) {
		    case IOCP:
		    case IORP:
		    case IOSP:
			chan_fetch(chan);
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
				"chan %d -Sel< %o\n", chan, cmd[chan] & 070);
			continue;	/* Handle new command next time */
		    case IOCD:
			chan_flags[chan] &= ~(STA_START|STA_ACTIVE/*|STA_WAIT*/);
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
				"chan %d -Sel< %o\n", chan, cmd[chan] & 070);
			continue;	/* Handle new command next time */
		    case IOCT:
		    case IORT:
		    case IOST:
			chan_flags[chan] &= ~(STA_ACTIVE/*|STA_WAIT*/);
			chan_flags[chan] |= STA_TWAIT;
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev, 
				"chan %d -Sel< %o\n", chan, cmd[chan] & 070);
			continue;
		    }
		}

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

		    /* Check if got EOR */
		    if (chan_flags[chan] & DEV_REOR) {
		        switch (cmd[chan] & 070) {
		        case IORP:
			    chan_flags[chan] &= ~(DEV_REOR/* | STA_WAIT*/);
		            if (chan_dev.dctrl & cmask)
			        sim_debug(DEBUG_DETAIL, &chan_dev,
				    "chan %d EOR> %o\n", chan, cmd[chan] & 070);
			    chan_fetch(chan);
			    chan_flags[chan] |= STA_ACTIVE;
			    break;
		        case IORT:
			    chan_flags[chan] &= ~(DEV_REOR|STA_ACTIVE);
		            chan_flags[chan] |= STA_TWAIT;
		            if (chan_dev.dctrl & cmask)
			        sim_debug(DEBUG_DETAIL, &chan_dev, 
				    "chan %d EOR> %o\n", chan, cmd[chan] & 070);
			    continue;
		        }
		    }

		/* Give device new word if we have one */
		if (wcount[chan] != 0) {

		    if (cmd[chan] & 1) {
			assembly[chan] = 0;
			if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DATA, &chan_dev,
				      "chan %d data > *\n", chan);
		    } else {
			assembly[chan] = M[caddr[chan]];
			if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DATA, &chan_dev,
				      "chan %d data > %012llo\n", chan,
				      assembly[chan]);
		        nxt_chan_addr(chan);
			bcnt[chan] = 6;
			wcount[chan]--;
		    }
		    chan_flags[chan] |= DEV_FULL;
		    continue;	/* Don't start next command until data taken */
		}

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

		switch (cmd[chan] & 070) {
		case IOCD:	/* Transfer and disconnect */
		    chan_flags[chan] |= DEV_DISCO | DEV_WEOR;
		    chan_flags[chan] &=
			~(STA_START | STA_ACTIVE | STA_PEND);
		    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7289)
			iotraps |= 1 << chan;
		    chan_info[chan] = 0;
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_DETAIL, &chan_dev,
				  "chan %d > DISCO\n", chan);
		    break;

		case IORP:	/* Transfer until end of record */
		    chan_flags[chan] |= DEV_WEOR|STA_WAIT;
		    break;
		case IOSP:	/* Transfer and proceed */
		case IOCP:	/* Transfer and proceed, no eor */
		    chan_fetch(chan);
		    break;

		case IORT:	/* Transfer, continue if LCH pending, */
		    /* else trap, Skip rest of record */
		    chan_flags[chan] |= DEV_WEOR|STA_WAIT;
		    break;
		case IOST:	/* Transfer, continue if LCH, else trap */
		case IOCT:	/* Transfer but no end of record, else trap */
		    chan_flags[chan] &= ~STA_ACTIVE;
		    chan_flags[chan] |= STA_TWAIT;
		    break;
		}
	    }
	    break;

	case CHAN_7909:
	again:
	    /* If waiting for EOR just spin */
	    if (chan_flags[chan] & STA_WAIT) {
		if (chan_flags[chan] & DEV_REOR) {
		    chan_flags[chan] &= 
				~(STA_WAIT|DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE);
		    if (chan_flags[chan] & DEV_SEL)
		        chan_flags[chan] |= DEV_DISCO;
		    if (chan_dev.dctrl & cmask)
		        sim_debug(DEBUG_DETAIL, &chan_dev,
		            "chan %d EOR Continue\n", chan);
		} 
		continue;
	    }

	    /* Nothing more to do if not active. */
	    if (chan_flags[chan] & STA_ACTIVE) {
		/* Execute the next command */
		switch (cmd[chan]) {
		case XXXZ:
		case XXXX:
		case TWT:
		    /* Check if command not allowed */
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_TRAP, &chan_dev, "chan %d CPU Trap\n",
				  chan);
		    iotraps |= 1 << chan;
		    chan_flags[chan] |= CTL_INHB;
		case WTR:
		case WTRX:
		    /* Check if command not allowed */
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    /* Go into a wait state */
		    chan_flags[chan] &= ~STA_ACTIVE;
		    location[chan]--;
		    break;
		case XMT:
		case XMTX:
		    /* Check if command not allowed */
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    if (wcount[chan] == 0)
			break;
		    wcount[chan]--;
		    M[caddr[chan]] = M[location[chan]];
		    nxt_chan_addr(chan);
		    bcnt[chan] = 6;
		    location[chan]++;
		    continue;
		case LIPT:
		case LIPTX:
		    chan_flags[chan] &= ~(SNS_IRQ | SNS_IMSK | SNS_UEND);
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_TRAP, &chan_dev, "chan %d %02o LIPT\n",
				 chan, chan_flags[chan] & 077);
		case TCH9:
		case TCHX:
		    location[chan] = caddr[chan];
		    break;
		case LIP:
		    chan_flags[chan] &= ~(SNS_IRQ | SNS_IMSK | SNS_UEND);
		    location[chan] = (uint16)M[040 + (2 * chan)] & MEMMASK;
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_TRAP, &chan_dev, "chan %d %02o LIP\n",
				 chan, chan_flags[chan] & 077);
		    break;
		case CTL:
		    if (chan_flags[chan] & CTL_CNTL)
			goto xfer;
		    if (chan_flags[chan] & (CTL_READ | CTL_WRITE | CTL_SNS)) {
			chan9_seqcheck(chan);
			continue;
		    }
		    chan_flags[chan] |= CTL_CNTL;
		    goto finddev;
		case CTLR:
		    if (chan_flags[chan] & CTL_CNTL)
			goto xfer;
		    if (chan_flags[chan] & (CTL_READ | CTL_WRITE | CTL_SNS)) {
			chan9_seqcheck(chan);
			break;
		    }
		    chan_flags[chan] |= CTL_CNTL | CTL_PREAD;
		    goto finddev;
		case CTLW:
		    if (chan_flags[chan] & CTL_CNTL)
			goto xfer;
		    if (chan_flags[chan] & (CTL_READ | CTL_WRITE | CTL_SNS)) {
			chan9_seqcheck(chan);
			break;
		    }
		    chan_flags[chan] |= CTL_CNTL | CTL_PWRITE;
		    goto finddev;
		case SNS:
		    if (chan_flags[chan] & (CTL_CNTL | CTL_READ | CTL_WRITE)) {
			chan9_seqcheck(chan);
			break;
		    }
		    chan_flags[chan] |= CTL_SNS;
		  finddev:
		    chan_flags[chan] &= ~(DEV_REOR|CTL_END|DEV_WEOR);
		    {
			DEVICE            **dptr;
			UNIT               *uptr;
			DIB                *dibp;

			for (dptr = sim_devices; *dptr != NULL; dptr++) {
			    int                 num = (*dptr)->numunits;
			    int                 j;

			    dibp = (DIB *) (*dptr)->ctxt;
			    /* If not device or 7909 type, just skip */
			    if (dibp == 0 || (dibp->ctype & CH_TYP_79XX) == 0)
				continue;
			    uptr = (*dptr)->units;
			    for (j = 0; j < num; j++, uptr++) {
				if ((uptr->flags & UNIT_DIS) == 0 && 
				    UNIT_G_CHAN(uptr->flags) == 
					  (unsigned int)chan &&
				    (sms[chan] & 1) ==
				    	  ((UNIT_SELECT & uptr->flags) != 0)) {
				    goto found;
				}
			    }
			}
			/* If no device, stop right now */
			chan9_set_error(chan, SNS_ADCHECK);
			chan_flags[chan] &= ~(CTL_PREAD | CTL_PWRITE | CTL_SNS |
			      CTL_CNTL);
			iotraps |= 1 << chan;
			chan_flags[chan] &= ~STA_ACTIVE;
			break;
		      found:
			/* Get channel ready to transfer */
			chan_flags[chan] &= 
				~(CTL_END|CTL_SEL|DEV_REOR|DEV_FULL);
			bcnt[chan] = 6;

			/* Call device to start it running */
			if (sms[chan] & 1) 
			    chan_flags[chan] |= CTL_SEL;
			switch (dibp->cmd(uptr, cmd[chan], sms[chan])) {
			case SCPE_IOERR:
			case SCPE_NODEV:
			    chan9_set_error(chan, SNS_IOCHECK);
			    iotraps |= 1 << chan;
			    chan_flags[chan] &= 
			~(CTL_PREAD|CTL_PWRITE|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;
			}
		    }
		    /* Special out for sense command */
		    if (cmd[chan] == SNS) {
			chan_flags[chan] &= ~DEV_WRITE;
			chan_flags[chan] |= DEV_SEL;
			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);
			chan_flags[chan] &= ~(CTL_READ | CTL_WRITE);
			if ((chan_flags[chan] & CTL_END) == 0)
			    chan_flags[chan] |= (chan_flags[chan] &
						 (CTL_PREAD | CTL_PWRITE)) >> 2;
			if ((chan_flags[chan] & (SNS_UEND|CTL_END)) ==
				(SNS_UEND|CTL_END) && (sms[chan] & 010) == 0)
			    chan_flags[chan] &= ~STA_ACTIVE;
			chan_flags[chan] &= ~(CTL_CNTL | CTL_PREAD |
					      CTL_PWRITE | CTL_END);
			if (chan_flags[chan] & CTL_WRITE)
			    chan_flags[chan] |= DEV_WRITE;
		        bcnt[chan] = 6;
			break;
		    }

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

		case LAR:
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    assembly[chan] = M[caddr[chan]];
		    if (chan_dev.dctrl & cmask)
		         sim_debug(DEBUG_CMD, &chan_dev,
				      "chan %d LAR > %012llo\n",
				      chan, assembly[chan]);
		    break;
		case SAR:
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    if (chan_dev.dctrl & cmask)
		         sim_debug(DEBUG_CMD, &chan_dev,
				      "chan %d SAR < %012llo\n",
				      chan, assembly[chan]);
		    M[caddr[chan]] = assembly[chan];
		    break;
		case CPYP:
		case CPYP2:
		case CPYP3:
		case CPYP4:
		    if (chan_flags[chan] & (DEV_REOR|CTL_END)) {
			if (sms[chan] & 0100) {
			    chan9_set_error(chan, SNS_UEND);
			    if (chan_flags[chan] & DEV_SEL)
				chan_flags[chan] |= (DEV_DISCO | DEV_WEOR);
		            chan_flags[chan] &= 
				~(STA_WAIT|DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE);
			    break;
			}
			if (wcount[chan] != 0) 
			    chan_flags[chan] &= ~(DEV_REOR);
		    }
		case CPYD:
		case CPYDX:
		    if ((chan_flags[chan] & (CTL_READ|CTL_WRITE|CTL_SNS))==0) {
			 chan9_seqcheck(chan);
			 break;
		    }


		    if ((chan_flags[chan] & DEV_FULL) == 0) {
		        /* Check if we still have a select signal */
		        if (wcount[chan] != 0 &&
			    (chan_flags[chan] & DEV_SEL) == 0) {
			    chan9_seqcheck(chan);
			    break;
		        }

		        /* Check if last word transfered */
			if (wcount[chan] == 0) {
			    if (cmd[chan] == CPYD || cmd[chan] == CPYDX ||
				chan_flags[chan] & SNS_UEND) {
		                if (chan_dev.dctrl & cmask)
			            sim_debug(DEBUG_DETAIL, &chan_dev,
				   	 "chan %d DISC %o\n", chan, cmd[chan] & 070);
			        if (sms[chan] & 0100 && 
					(chan_flags[chan] & DEV_REOR) == 0)
			             chan9_set_error(chan, SNS_UEND);
				chan_flags[chan] |= (DEV_WEOR);
				if (chan_flags[chan] & DEV_SEL)
				    chan_flags[chan] |= DEV_DISCO;
				chan_flags[chan] &= 
					~(CTL_SNS | CTL_READ | CTL_WRITE);
			        if ((chan_flags[chan] & (SNS_UEND|CTL_END)) ==
				        (SNS_UEND|CTL_END) &&
						 (sms[chan] & 010) == 0)
				    chan_flags[chan] &= ~STA_ACTIVE;
			    } else {
				if (chan_flags[chan] & DEV_REOR)
				    chan_flags[chan] &= ~DEV_REOR;
			    }
			    break;
			}

			/* Check for record end in non-concurrent IRQ mode*/
                       	if (chan_flags[chan] & DEV_REOR && sms[chan] & 0100) {
			    chan9_set_error(chan, SNS_UEND);
			    chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE);
			    if (chan_flags[chan] & DEV_SEL)
			        chan_flags[chan] |= (DEV_DISCO | DEV_WEOR);
			    break;
			}
		    }

		    /* Check if ready to transfer something */
		    switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) {
		    case DEV_WRITE | DEV_FULL:
		    case 0:
		        /* If device ended, quit transfer */
		        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] &= ~(STA_WAIT);
			    }
			    if (sms[chan] & 0100 && wcount[chan] != 0)
			        chan9_set_error(chan, SNS_UEND);
			    chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL|DEV_REOR|
				CTL_SNS|CTL_READ|CTL_WRITE|CTL_END);
			    /* Get new command ready after disco */
			    chan_fetch(chan);
		        }
			continue;	/* Do nothing if no data xfer */
		    case DEV_WRITE:	/* Device needs data word */
			assembly[chan] = M[caddr[chan]];
			if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DATA, &chan_dev,
				      "chan %d data > %012llo\n",
				      chan, assembly[chan]);
			if (sms[chan] & 020)	/* BCD Xlat mode */
			    bcd_xlat(chan, 0);
			if (sms[chan] & 040) {	/* Read backward */
			    caddr[chan] = 
				((dualcore) ? (0100000 & caddr[chan]) : 0) | 
				((caddr[chan] - 1) & MEMMASK);
			} else {
		            nxt_chan_addr(chan);
			}
		        bcnt[chan] = 6;
			wcount[chan]--;
			chan_flags[chan] |= DEV_FULL;
			break;
		    case DEV_FULL:	/* Device has given us a dataword */
			if (bcnt[chan] != 0) 
			    assembly[chan] <<= 6 * bcnt[chan];
			if (sms[chan] & 020)	/* BCD Xlat mode */
			    bcd_xlat(chan, 1);
			if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DATA, &chan_dev,
				      "chan %d data < %012llo\n",
				      chan, assembly[chan]);
			M[caddr[chan]] = assembly[chan];
			if (sms[chan] & 040) {	/* Read backward */
			    caddr[chan] = 
				((dualcore) ? (0100000 & caddr[chan]) : 0) | 
				((caddr[chan] - 1) & MEMMASK);
			} else {
		            nxt_chan_addr(chan);
			}
			assembly[chan] = 0;
			bcnt[chan] = 6;
			wcount[chan]--;
			chan_flags[chan] &= ~DEV_FULL;
			break;
		    }

		    /* Tell device this is last word */
//		    if (wcount[chan] == 0 &&
//			(cmd[chan] == CPYD || cmd[chan] == CPYDX)) {
//			chan_flags[chan] |= DEV_WEOR|STA_WAIT|DEV_DISCO;
//		        if (chan_dev.dctrl & cmask)
//			    sim_debug(DEBUG_DETAIL, &chan_dev,
//				"chan %d CPYD wc==0\n", chan);
//			break;
//		    }
		    continue;

		case TCM:
		case TCMX:
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    } else {
			/* Compare wordcount high to wordcound low */
			/* 0 = chan check, 1-6 = assmebly, 7 = 0 */
			uint8               v;
			uint8               ch = wcount[chan] >> 12;
			uint8               mask = wcount[chan] & 077;
			uint8               flag = wcount[chan] & 0100;

			if (ch == 0) {
			    v = (chan_flags[chan] >> 5) & 077;
			} else if (ch == 7) {
			    v = 0;
			} else {
			    v = (uint8)(077 & (assembly[chan] >> (6 * (6-ch))));
			}
		        if (chan_dev.dctrl & cmask)
			    sim_debug(DEBUG_DETAIL, &chan_dev,
				 "TCM %d:%02o & %02o\n\r", ch, v, mask);
			if ((v == mask && flag == 0)
			    || ((v & mask) == mask && flag != 0))
			    location[chan] = caddr[chan];
		    }
		    break;
		case TDC:
		    if (counter[chan] != 0) {
			location[chan] = caddr[chan];
			counter[chan]--;
		    }
		    break;
		case LCC:
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    counter[chan] = caddr[chan] & 077;
		    break;
		case SMS:
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_DETAIL, &chan_dev,
				      "chan %d SMS %03o -> %03o %03o ",
				chan, sms[chan], caddr[chan] & 0177,
				(SNS_IRQS & chan_flags[chan])>>5);
		    sms[chan] = caddr[chan] & 0177;
		    /* Check to see if IRQ still pending */
		    if ((chan_flags[chan] & CTL_INHB) == 0 &&
			chan_flags[chan] & SNS_IRQS &
			(~((sms[chan] << 5) & (SNS_IMSK ^ SNS_IRQS)))) {
			chan_irq[chan] = 1;
		    }
		    if (chan_dev.dctrl & cmask)
			sim_debug(DEBUG_DETAIL, &chan_dev, "Irqs = %03o\n",
				((chan_flags[chan] & SNS_IRQS)>>5) &
				     ((sms[chan] ^ 016) | 061));
		    break;
		case ICC:
		case ICCX:
		    if (chan_flags[chan] & DEV_SEL) {
			chan9_seqcheck(chan);
			break;
		    }
		    /* transfer counter from wordcount high to assembly */
		    /* 0 = SMS to 6, 1-6 = assmebly, 7 = nop */
		    {
			t_uint64            v = counter[chan] & 077;
			uint8               ch = wcount[chan] >> 12;

			if (ch == 0) {
			    /* Not what POO says, but what diags want */
			    /* POO says other digits not affected. */
			    assembly[chan] = sms[chan] & 00137;
			} else if (ch != 7) {
			    assembly[chan] &= ~(077L << (6 * (6 - ch)));
			    assembly[chan] |= (v << (6 * (6 - ch)));
			}
		    }
		    break;
		}
	    }

	    /* Check for intrupts */
	    if (chan_irq[chan] || 
		/* Can only interupt when channel inactive */
		((chan_flags[chan] & (DEV_SEL | STA_ACTIVE | CTL_CNTL | CTL_SNS
			 | SNS_IRQ | CTL_INHB | CTL_READ | CTL_WRITE)) == 0 &&
		 cmd[chan] != TWT  &&
		(chan_flags[chan] & SNS_IRQS &
			(((sms[chan] ^ 016) | 061) << 5)))) {
		uint8	ocmd = cmd[chan];
		M[040 + (chan * 2)] = location[chan] & MEMMASK;
		M[040 + (chan * 2)] |= ((t_uint64) caddr[chan]) << 18;
		chan_flags[chan] |= STA_ACTIVE|CTL_INHB;
		location[chan] = 041 + (chan * 2);
		chan_irq[chan] = 0;
		if (chan_dev.dctrl & cmask)
		     sim_debug(DEBUG_TRAP, &chan_dev, "chan irq %d\n\r", chan);
		chan_fetch(chan);
		/* Fake a xec type trap */
		if ((ocmd & 073) == WTR || ocmd == TWT) 
		   location[chan] = (uint16)(M[040 + (chan * 2)] + 1)& MEMMASK;
		else
		   location[chan] = (uint16)M[040 + (chan * 2)] & MEMMASK;
		goto again;
	    }

	    if (chan_flags[chan] & STA_ACTIVE) {
		uint8	c = cmd[chan];
		chan_fetch(chan);
		/* Check if we should interupt during unusual end */
		if (sms[chan] & 0100 && (c & 070) == CPYP &&
		    (cmd[chan] & 071) == CPYD && wcount[chan] == 0) {
		    if (chan_dev.dctrl & cmask)
		          sim_debug(DEBUG_DETAIL, &chan_dev, 
					"chan non-concur %d\n\r", chan); 
		    chan9_set_error(chan, SNS_UEND);
		    chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE);
		    if (chan_flags[chan] & DEV_SEL) 
			chan_flags[chan] |= DEV_WEOR|DEV_DISCO;
		    chan_fetch(chan);
		}
		if (cmd[chan] != TCM && (chan_flags[chan] & DEV_DISCO) == 0)
		    goto again;
	    }
#endif
	}
    }
}

void
chan_fetch(int chan)
{
    uint16              loc = location[chan];
    t_uint64            temp = M[loc & MEMMASK];

    sim_interval--;
    location[chan] = (loc + 1) & MEMMASK;
    cmd[chan] = (uint8)(((temp >> 30) & 074) | ((temp >> 16) & 1));
    wcount[chan] = (uint16)(temp >> 18) & 077777;
    caddr[chan] = (uint16)temp & MEMMASK;
    if (dualcore)
	caddr[chan] |= temp & 0100000;
    /* Check indirect bit */
    if (temp & 0400000) {
	caddr[chan] = (uint16)M[caddr[chan]];
	if (dualcore)
	    caddr[chan] &= 0100000 | MEMMASK;
	else
	    caddr[chan] &= MEMMASK;
	sim_interval--;
    }
    /* Clear pending IO traps for channel */
    //iotraps &= ~(1 << chan);
    if (chan_dev.dctrl & (0x0100 << chan))
	sim_debug(DEBUG_CHAN, &chan_dev,
		  "chan %d fetch adr=%05o cmd=%03o caddr=%05o wcount=%05o\n",
		  chan, location[chan], cmd[chan], caddr[chan],
		  wcount[chan]);
}

/* Reset the channel, clear any pending device */
void
chan_rst(int chan, int type)
{
    /* Issure reset command to device */
    if (type && CHAN_G_TYPE(chan_unit[chan].flags) != CHAN_7909)
	return;
    if (type == 0 && CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909)
	return;
    if (chan_dev.dctrl & (0x0100 << chan))
        sim_debug(DEBUG_CHAN, &chan_dev, "Reset channel\n");
    /* Clear outstanding traps on reset */
    if (type)
	iotraps &= ~(1 << chan);
    chan_info[chan] &= ~(CHAINF_START | CHAINF_RUN);
    chan_flags[chan] &= (CHS_EOF|CHS_BOT|CHS_EOT|DEV_DISCO|DEV_SEL);
    caddr[chan] = 0;
    cmd[chan] = 0;
    sms[chan] = 0;
    chan_irq[chan] = 0;
    wcount[chan] = 0;
    location[chan] = 0;
    counter[chan] = 0;		/* Channel memory address */
}

/* Issue a command to a channel */
int
chan_cmd(uint16 dev, uint16 dcmd)
{
    UNIT               *uptr;
    uint32              chan;
    DEVICE            **dptr;
    DIB                *dibp;
    int                 j;

    /* Find device on given channel and give it the command */
    chan = (dev >> 9) & 017;
    /* If no channel device, quick exit */
    if (chan_unit[chan].flags & UNIT_DIS)
	return SCPE_IOERR;
    /* On 704 device new command aborts current operation */
    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO &&
	(chan_flags[chan] & (DEV_SEL | DEV_DISCO)) == DEV_SEL) {
	/* Check if device picked up last transfer */
	if ((chan_flags[chan] & (DEV_FULL|DEV_WRITE)) == (DEV_FULL|DEV_WRITE))
	    return SCPE_BUSY;
	/* Yes, disconnect device and tell it to write a EOR */
	chan_flags[chan] |= DEV_DISCO | DEV_WEOR;
	return SCPE_BUSY;
    }
    /* Unit is busy doing something, wait */
    if (chan_flags[chan] & (DEV_SEL | DEV_DISCO | STA_TWAIT | STA_WAIT))
	return SCPE_BUSY;
    chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR|DEV_FULL|DEV_WRITE);
    /* Ok, try and find the unit */
    dev &= 07777; 
    for (dptr = sim_devices; *dptr != NULL; dptr++) {
	int                 r;

	dibp = (DIB *) (*dptr)->ctxt;
	/* If no DIB, not channel device */
	if (dibp == NULL || dibp->ctype == CHAN_7909 ||
	    (dibp->addr & dibp->mask) != (dev & dibp->mask))
	    continue;
	uptr = (*dptr)->units;
	if (dibp->upc == 1) {
	    int                 num = (*dptr)->numunits;

	    for (j = 0; j < num; j++) {
		if (UNIT_G_CHAN(uptr->flags) == chan) {
		    r = dibp->cmd(uptr, dcmd, dev);
		    if (r != SCPE_NODEV) {
			bcnt[chan] = 6;
			return r;
		    }
		}
		uptr++;
	    }
	} else {
	    if (UNIT_G_CHAN(uptr->flags) == chan) {
		r = dibp->cmd(uptr, dcmd, dev);
		if (r != SCPE_NODEV) {
		    bcnt[chan] = 6;
		    return r;
		}
	    }
	}
    }
    return SCPE_NODEV;
}


/* Give channel a new address to start working at */
int
chan_start(int chan, uint16 addr)
{
    /* Hold this command until after channel has disconnected */
    if (chan_flags[chan] & DEV_DISCO)
	return SCPE_BUSY;

    /* Depending on channel type controls how command works */
    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) {
	if (chan_flags[chan] & STA_ACTIVE)
	    return SCPE_BUSY;
	chan_flags[chan] &=
	    ~(CTL_CNTL | CTL_SNS | CTL_READ | CTL_PREAD | CTL_INHB |
	      CTL_WRITE | CTL_PWRITE | SNS_UEND | SNS_IOCHECK);
    } else {
	/* All clear, start ball rolling on new command */
	/* Force iocheck if attempt to load inactive channel with command */
	if ((chan_flags[chan] & DEV_SEL) == 0) {
            /* Fetch next command */
            location[chan] = addr;
            chan_fetch(chan);
	    return SCPE_IOERR;
	}
    }
    if (chan_dev.dctrl & (0x0100 << chan))
	sim_debug(DEBUG_CHAN, &chan_dev,
		  "chan %d start IC=%05o addr=%o\n\r", chan, IC - 1, addr);
    /* Fetch next command */
    location[chan] = addr;
    chan_fetch(chan);
    chan_flags[chan] &= ~(STA_PEND|STA_TWAIT);
    chan_flags[chan] |= STA_START | STA_ACTIVE;
    chan_info[chan] |= CHAINF_START;
    return SCPE_OK;
}

/* Give channel a new address to start working at */
int
chan_load(int chan, uint16 addr)
{
    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) {
	if (chan_flags[chan] & STA_ACTIVE)
	    return SCPE_BUSY;
	/* Ignore command if this waiting on IRQ */
	if (cmd[chan] == TWT && iotraps & (1 << chan))
		return SCPE_OK;
	location[chan] = caddr[chan];
    } else {
	/* Force iocheck if attempt to load channel with command,
	   that has not been started or is not in select state */
	if ((chan_flags[chan] & (DEV_SEL | STA_START)) != (DEV_SEL|STA_START))
	    return SCPE_IOERR;
	/* If channel active, or waiting EOR, should hold CPU */
	if (chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) {
	    chan_flags[chan] |= STA_PEND;
	    return SCPE_BUSY;
	}
	chan_flags[chan] &= ~(STA_PEND|STA_TWAIT);
	location[chan] = addr;
    }
    /* All clear, start ball rolling on new command */
    if (chan_dev.dctrl & (0x0100 << chan))
	sim_debug(DEBUG_CHAN, &chan_dev,
		  "chan %d load IC=%05o addr=%o stat=%08x\n\r", chan, IC - 1,
			 addr, chan_flags[chan]);
    chan_fetch(chan);
    chan_flags[chan] |= STA_ACTIVE;
    return SCPE_OK;
}

/* return the channels current command address */
void
chan_store(int chan, uint16 loc)
{
    t_uint64            reg = 0LL;

    /* Check if channel has units attached */
    if (chan_unit[chan].flags & CHAN_SET) {
	/* Return command/addr/xcmd/location */
	if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) {
	    reg = location[chan] & MEMMASK;
	    reg |= ((t_uint64) caddr[chan]) << 18;
	    /* Check if channel has units attached */
	} else {
	    /* If doing a TCH, process it */
	    if ((cmd[chan] & 070) == TCH) 
		chan_proc();
	    reg = caddr[chan];
	    reg |= ((t_uint64) (location[chan] & MEMMASK)) << 18;
	    reg |= ((t_uint64) (cmd[chan] & 070)) << 30;
	    reg |= ((t_uint64) (cmd[chan] & 01)) << 16;
	}
    }
    if (chan_dev.dctrl & (0x0100 << chan))
        sim_debug(DEBUG_SNS, &chan_dev, "chan %d status %012llo\n\r",
		 chan,reg);
    M[loc & MEMMASK] = reg;
}

/* Store channel diagnostic bits */
void
chan_store_diag(int chan, uint16 loc)
{
    t_uint64            reg;
    int                 results;

    /* Counter[6], iocheck,seq check, unusal end, attn 1,
     * attn 2, adpter check, prepare to read, prepare to write,
     * read status, write status, interupt */
    if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) {
	reg = ((t_uint64) counter[chan]) << 30;
	results = SNS_MASK & chan_flags[chan];
	if (results & (((sms[chan] ^ 016) | 061) << 5))
	    results |= 1;
	reg |= ((t_uint64) (results)) << 19;
        if (chan_dev.dctrl & (0x0100 << chan))
            sim_debug(DEBUG_SNS, &chan_dev, "chan %d diags %012llo\n\r",
			 chan,reg);
	M[loc & MEMMASK] = reg;
    }
}

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

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

    /* 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_REOR;
	    chan_flags[chan] &= ~(DEV_WEOR/*|STA_WAIT*/);
	    return END_RECORD;
	}

	/* If active set attention, report IO error if
	  device does not want to disconnect  */
	if (chan_flags[chan] & STA_ACTIVE) { 
	    chan_flags[chan] |= CHS_ATTN;
	    if ((flags & DEV_DISCO) == 0) 
	        iocheck = 1;
	}

	/* If requested, force disconnect */
	chan_flags[chan] |= DEV_DISCO & flags;

	return TIME_ERROR;
    } else {
        if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) 
	    MQ = *data;
	assembly[chan] = *data;
	bcnt[chan] = 6;
	chan_flags[chan] |= DEV_FULL;
	chan_flags[chan] &= ~DEV_WRITE;
	if (flags & DEV_REOR) {
	    chan_flags[chan] |= DEV_REOR;
	}
    }

    return DATA_OK;
}

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

    /* Return END_RECORD if requested */
    if (flags & DEV_WEOR) {
	chan_flags[chan] |= DEV_REOR;
	chan_flags[chan] &= ~(DEV_WEOR);
	return END_RECORD;
    }

    /* Check if no data waiting */
    if ((chan_flags[chan] & DEV_FULL) == 0) {
	/* Nope, see if we are waiting for end of record. */
	if (chan_flags[chan] & DEV_WEOR) {
	    chan_flags[chan] |= DEV_WRITE;
	    chan_flags[chan] &= ~(DEV_WEOR /*| STA_WAIT*/);
	    return END_RECORD;
	}

	/* If active set attention, report IO error if
	  device does not want to disconnect */
	if (chan_flags[chan] & STA_ACTIVE) { 
	    chan_flags[chan] |= CHS_ATTN;
	    if ((flags & DEV_DISCO) == 0) 
	        iocheck = 1;
	}

	/* If requested, force disconnect */
	chan_flags[chan] |= DEV_DISCO & flags;
	
	return TIME_ERROR;
    } else {
	*data = assembly[chan];
	bcnt[chan] = 6;
	chan_flags[chan] &= ~DEV_FULL;
	/* If end of record, don't transfer any data */
	if (flags & DEV_REOR) {
	    chan_flags[chan] &= ~(DEV_WRITE);
	    chan_flags[chan] |= DEV_REOR;
	} else
	    chan_flags[chan] |= DEV_WRITE;
    }
    return DATA_OK;
}

/*
 * Write a char to the assembly register.
 */
int
chan_write_char(int chan, uint8 * data, int flags)
{
    /* If Writing end of record, abort */
    if (chan_flags[chan] & DEV_WEOR) {
	chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR);
	return END_RECORD;
    }

    /* 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_REOR;
	    chan_flags[chan] &= ~(DEV_WEOR/*|STA_WAIT*/);
	    return END_RECORD;
	}

	/* If active set attention, report IO error if
	  device does not want to disconnect */
	if (chan_flags[chan] & STA_ACTIVE) { 
	    chan_flags[chan] |= CHS_ATTN;
	    if ((flags & DEV_DISCO) == 0) 
	        iocheck = 1;
	}

	/* If requested, force disconnect */
	chan_flags[chan] |= DEV_DISCO & flags;

	return TIME_ERROR;
    } else {
	int	cnt = --bcnt[chan];
	t_uint64	wd;
        if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) 
	   wd = MQ;
	else
 	   wd = assembly[chan];
	wd &= 0007777777777LL;
	wd <<= 6;
	wd |= (*data) & 077;
        if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) 
	    MQ = wd;
	else
	    assembly[chan] = wd;
	
	if (cnt == 0) {
	    chan_flags[chan] |= DEV_FULL;
	    chan_flags[chan] &= ~DEV_WRITE;
	}
	if (flags & DEV_REOR) {
	    chan_flags[chan] |= DEV_FULL|DEV_REOR;
	    chan_flags[chan] &= ~DEV_WRITE;
	}
    }


    return DATA_OK;
}

/*
 * Read next char from assembly register.
 */
int
chan_read_char(int chan, uint8 * data, int flags)
{

    /* Return END_RECORD if requested */
    if (flags & DEV_WEOR) {
	chan_flags[chan] &= ~(DEV_WEOR);
	return END_RECORD;
    }

    /* Check if he write out last data */
    if ((chan_flags[chan] & DEV_FULL) == 0) {
	/* Nope, see if we are waiting for end of record. */
	if (chan_flags[chan] & DEV_WEOR) {
	    chan_flags[chan] |= DEV_WRITE|DEV_REOR;
	    chan_flags[chan] &= ~(DEV_WEOR /*| STA_WAIT*/);
	    return END_RECORD;
	}

	/* If active set attention, report IO error if
	  device does not want to disconnect */
	if (chan_flags[chan] & STA_ACTIVE) { 
	    chan_flags[chan] |= CHS_ATTN;
	    if ((flags & DEV_DISCO) == 0) 
	        iocheck = 1;
	}

	/* If requested, force disconnect */
	chan_flags[chan] |= DEV_DISCO & flags;
	
	return TIME_ERROR;
    } else {
	int	cnt = --bcnt[chan];
	t_uint64	wd = assembly[chan];
	*data = (uint8)(077 & (wd >> 30));
	wd <<= 6;
	wd |= 077 & (wd >> 36);
	wd &= 0777777777777LL;
        if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) 
	    MQ = wd;
	assembly[chan] = wd;
	if (cnt == 0) {
	    chan_flags[chan] &= ~DEV_FULL;
	    bcnt[chan] = 6;
	}
	/* 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;
	} else
	    chan_flags[chan] |= DEV_WRITE;
    }
    return DATA_OK;
}

void
chan9_seqcheck(int chan)
{
    /* Disconnect channel if active */
    if (chan_flags[chan] & DEV_SEL)
	chan_flags[chan] |= DEV_DISCO;
    chan_flags[chan] &= ~(CTL_READ|CTL_WRITE|CTL_SNS|STA_ACTIVE);
    if (chan_dev.dctrl & (0x0100 << chan))
	sim_debug(DEBUG_EXP, &chan_dev, "chan %d seq\n", chan);
    chan9_set_error(chan, SNS_SEQCHECK);
}

void
chan9_set_error(int chan, uint32 mask)
{
    if (chan_flags[chan] & mask)
	return;
    chan_flags[chan] |= mask;
    if (mask & (~((sms[chan] << 5) & (SNS_IMSK ^ SNS_IRQS)))) {
  	chan_irq[chan] = 1;
    }
}
