/* 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 t_uint64 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] & (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 = M[location[chan] - 1]; | |
chan_info[chan] |= CHAINF_RUN; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d HDaddr %012llo\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; | |
} | |
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 */ | |
if ((chan_flags[chan] & (DEV_WRITE)) == (DEV_WRITE) || | |
(chan_flags[chan] & (DEV_FULL)) == (DEV_FULL)) | |
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"; | |
} |