blob: 3daf25eaf051a033bd692a8f1e702f9a62e02759 [file] [log] [blame] [raw]
/* i7090_chan.c: IBM 7090 Channel simulator
Copyright (c) 2005-2016, Richard Cornwell
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
channel
The system state for the IBM 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 uint16 iotraps;
extern uint8 iocheck;
extern uint8 dualcore;
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);
t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *chan_description (DEVICE *dptr);
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);
/* Globally visible flags */
const char *chan_type_name[] = {
"Polled", "Unit Record", "7607", "7909", "7289"};
/* Delay device for IOD instruction */
DIB dly_dib =
{ CH_TYP_PIO, 1, 0333, 07777, &dly_cmd, NULL };
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,
NULL, NULL, &chan_help, NULL, NULL, &chan_description
};
/* 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_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;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d HDaddr %06o\n",
chan, hsdrm_addr);
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;
switch(cmd[chan]) {
case IORT:
case IOCT:
case IOST:
iotraps |= 1 << chan;
break;
}
iotraps |= 1LL << (chan + 18);
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 ((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*/;
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 */
/* 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);
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"chan %d -Sel< %o\n", chan, cmd[chan] & 070);
continue;
case IOCT:
case IORT:
case IOST:
chan_flags[chan] &= ~(STA_ACTIVE);
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);
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);
/* Fall through */
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;
}
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 %o\n",
((chan_flags[chan] & SNS_IRQS)>>5) &
((sms[chan] ^ 016) | 061), chan_irq[chan]);
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;
t_uint64 temp;
sim_interval--;
loc = location[chan] & MEMMASK;
if (dualcore)
loc |= location[chan] & 0100000;
temp = M[loc];
location[chan] = ((loc + 1) & MEMMASK) | (loc & 0100000);
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 */
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 == 0 && 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|STA_WAIT);
/* 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;
cmd[chan] = 0;
caddr[chan] = 0;
location[chan] = 0;
return r;
}
}
uptr++;
}
} else {
if (UNIT_G_CHAN(uptr->flags) == chan) {
r = dibp->cmd(uptr, dcmd, dev);
if (r != SCPE_NODEV) {
bcnt[chan] = 6;
cmd[chan] = 0;
caddr[chan] = 0;
location[chan] = 0;
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|STA_WAIT|DEV_WEOR|DEV_FULL);
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;
chan_flags[chan] &= ~(CTL_INHB);
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 | 0100000)] = 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 | 0100000)] = reg;
}
}
/*
* Write a word to the assembly register.
*/
int
chan_write(int chan, t_uint64 * data, int flags)
{
/* 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|DEV_FULL);
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);
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|DEV_FULL);
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);
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;
}
}
t_stat
chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
#ifdef I7090
fprintf(st, "%s\n\n", chan_description(dptr));
fprintf (st, "The 7090 supports up to 8 channels. Channel models include\n\n");
fprintf (st, " Unit record Polled mode I/O devices\n");
fprintf (st, " 7607 standard multiplexor channel\n");
fprintf (st, " 7909 advanced capabilities channel\n");
fprintf (st, " 7289 special channel for high speed drum\n\n");
fprintf (st, "Channels can be reconfigured on the 7090, this generally ");
fprintf (st, "happens automatically.\nHowever at times it can be useful to ");
fprintf (st, "force a channel to a specific device. If\ndevices are attached");
fprintf (st, "to incorrect channel types an error will be reported at sim\n");
fprintf (st, "start. The first channel is fixed for Polled mode devices.\n\n");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
#else
fprintf(st, "IBM 704 Channel\n\n");
fprintf(st, "Psuedo device to display IBM 704 I/O. The IBM 704 used polled");
fprintf(st, " I/O,\nThe assembly register and the flags can be displayed\n");
fprintf(st, "There are no options for the this device\n");
#endif
return SCPE_OK;
}
const char *
chan_description(DEVICE *dptr)
{
return "IBM 7090 channel controller";
}