/* i7070_chan.c: IBM 7070 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 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 DEVICE      *sim_devices[];
extern uint32       sim_interval;
extern UNIT         cpu_unit;

#define CHAN_DEF	UNIT_DISABLE|CHAN_SET

t_stat              chan_reset(DEVICE * dptr);
void                chan_fetch(int chan);

/* 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 */

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},
    {"ASM", &assembly, 16, 44, 0, NUM_CHAN, REG_VMIO|REG_RO|REG_FIT, 0},
    {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},
    {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
};

#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;
    int                 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
		    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] & DMASK) == 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] & DMASK) == 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<<(chan-4);
		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 << (chan - 4);
		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] & DMASK) == 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] & DMASK) == ASIGN &&
		                    (assembly[chan] & 0xFF) == RM_CHAR) {
				    chan_flags[chan] |= DEV_WEOR;
				    break;
		                } 
			        /* Check if no more words */
			//	if (caddr[chan] < limit[chan]) 
			            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(chan) {
    pri_latchs[0] |= 0x002;
}

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

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

void chan_clear_attn_inq(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 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
chan9_set_error(int chan, uint32 mask)
{
    if (chan_flags[chan] & mask)
	return;
    chan_flags[chan] |= mask;
}
