blob: 007facc5d2becdff25a3bc01f9220919802ef6e6 [file] [log] [blame] [raw]
/* i7070_chan.c: IBM 7070 Channel simulator
Copyright (c) 2005-2016, Richard Cornwell
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
channel
The system state for the IBM 7070 channel is:
There are 4 types of channel:
PIO: Basic polled mode transfer. Channel only manages
status and disconnect of devices.
7604: Basic channel.
7907: Enhanced channel for disk, hypertape and com controlers.
Common registers to all but PIO channels.
ADDR<0:16> Location to read or write next word from.
CMD<0:6> Channel command.
LIMIT<0:16> Transfer limit
ASM<0:44> Assembled data from devices.
LOCATION<0:16> Address of next command.
Simulation registers to handle device handshake.
STATUS<0:16> Simulated register for basic channel status.
SENSE<0:16> Additional flags for 7907 channels.
*/
#include "i7070_defs.h"
extern UNIT cpu_unit;
#define CHAN_DEF UNIT_DISABLE|CHAN_SET
t_stat chan_reset(DEVICE * dptr);
void chan_fetch(int chan);
t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *chan_description (DEVICE *dptr);
/* Channel data structures
chan_dev Channel device descriptor
chan_unit Channel unit descriptor
chan_reg Channel register list
chan_mod Channel modifiers list
*/
uint32 location[NUM_CHAN]; /* Location of RDW instruction*/
uint32 caddr[NUM_CHAN]; /* Channel memory address */
uint8 bcnt[NUM_CHAN]; /* Channel character count */
uint8 cmd[NUM_CHAN]; /* Current command */
uint8 op[NUM_CHAN]; /* Operators for 7907 channel */
uint32 limit[NUM_CHAN]; /* Word count */
t_uint64 assembly[NUM_CHAN]; /* Assembly register */
uint32 chan_flags[NUM_CHAN]; /* Unit status */
uint32 chan_info[NUM_CHAN]; /* Private channel info */
uint8 chan_irq[NUM_CHAN]; /* Channel has a irq pending */
extern uint16 pri_latchs[10];
#define CHAN_OUTDEV 0x010000 /* Type out device */
#define CHAN_PRIO 0x008000 /* Channel has priority pending */
#define CHAN_TWE 0x004000 /* Channel format error */
#define CHAN_SEOR 0x002000 /* Channel saw a eor */
#define CHAN_NORDW 0x020000 /* No RDW for this command */
#define CHAN_SEOS 0x040000 /* Channel saw a end of segment */
#define CHAN_SCLR 0x080000 /* Short record */
#define CHAN_FIRST 0x100000 /* First tranfered word */
#define CHAN_START 0x200000 /* Channel has just started */
#define CHAN_OCTAL 0x400000 /* Octal conversion */
const char *chan_type_name[] = {
"Polled", "Unit Record", "7604", "7907", ""};
UNIT chan_unit[] = {
/* Puesdo channel for PIO devices */
{UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_UREC)|UNIT_S_CHAN(CHAN_CHUREC),0)},
/* Normal channels */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_A)|CHAN_S_TYPE(CHAN_7604),0)},/* A */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_B)|CHAN_S_TYPE(CHAN_7604),0)},/* B */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_C)|CHAN_S_TYPE(CHAN_7604),0)},/* C */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_D)|CHAN_S_TYPE(CHAN_7604),0)},/* D */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_E)|CHAN_S_TYPE(CHAN_7907),0)},/* E */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_F)|CHAN_S_TYPE(CHAN_7907),0)},/* F */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_G)|CHAN_S_TYPE(CHAN_7907),0)},/* G */
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_H)|CHAN_S_TYPE(CHAN_7907),0)} /* H */
};
REG chan_reg[] = {
{BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO|REG_FIT},
{BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT},
{BRDATA(LIMIT, limit, 10, 18, NUM_CHAN), REG_RO|REG_FIT},
{BRDATA(ASM, assembly, 16, 44, NUM_CHAN), REG_VMIO|REG_RO|REG_FIT},
{BRDATA(LOCATION, location, 10, 18, NUM_CHAN), REG_RO|REG_FIT},
{BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT},
{NULL}
};
MTAB chan_mod[] = {
{CHAN_MODEL, CHAN_S_TYPE(CHAN_UREC), "UREC", NULL, NULL,NULL,NULL},
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7604), "7604", NULL, NULL, NULL, NULL},
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7907), "7907", NULL, NULL, NULL, NULL},
{MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL, "Show units on channel"},
{0}
};
/* Simulator debug controls */
DEBTAB chn_debug[] = {
{"CHANNEL", DEBUG_CHAN},
{"TRAP", DEBUG_TRAP},
{"CMD", DEBUG_CMD},
{"DATA", DEBUG_DATA},
{"DETAIL", DEBUG_DETAIL},
{"EXP", DEBUG_EXP},
{"SENSE", DEBUG_SNS},
{"CH0", 0x0100 << 0},
{"CH1", 0x0100 << 1},
{"CH2", 0x0100 << 2},
{"CH3", 0x0100 << 3},
{"CH4", 0x0100 << 4},
{"CHA", 0x0100 << 5},
{"CHB", 0x0100 << 6},
{"CHC", 0x0100 << 7},
{"CHD", 0x0100 << 8},
{0, 0}
};
DEVICE chan_dev = {
"CH", chan_unit, chan_reg, chan_mod,
NUM_CHAN, 10, 18, 1, 10, 44,
NULL, NULL, &chan_reset, NULL, NULL, NULL,
NULL, DEV_DEBUG, 0, chn_debug,
NULL, NULL, &chan_help, NULL, NULL, &chan_description
};
#define DELTA_CHAR 057
#define SM_CHAR 037
#define SM_MEM 0x39
#define RM_CHAR 0x80
/* Translation tables */
uint8 bcd_mem[64] = {
/* ? 1 2 3 4 5 6 7 */
/* 00 */ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
/* 8 9 0 =/# !/@ ? ? tm */
/* 10 */ 0x98, 0x99, 0x90, 0x45, 0x46, 0x47, 0x48, 0x49,
/* sp / S T U V W X */
/* 20 */ 0x60, 0x31, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
/* Y Z rm , %/( ? ? sm */
/* 30 */ 0x88, 0x89, 0x80, 0x35, 0x36, 0x37, 0x38, 0x39,
/* - J K L M N O P */
/* 40 */ 0x30, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
/* Q R -0 $ * ? ? del */
/* 50 */ 0x78, 0x79, 0x70, 0x25, 0x26, 0x27, 0x28, 0xFF,
/*+/& A B C D E F G */
/* 60 */ 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
/* H I +0 . sq ? ? gm */
/* 70 */ 0x68, 0x69, 0x60, 0x15, 0x16, 0x17, 0x18, 0x19
};
uint8 mem_bcd[256] = {
/* sp */
/* 00 */ 020, 000, 000, 000, 000, 000, 000, 000,
/* */
/* 08 */ 000, 000, 000, 000, 000, 000, 000, 000,
/* sq ? */
/* 10 */ 000, 000, 000, 000, 000, 073, 074, 075,
/* ? ? */
/* 18 */ 076, 077, 000, 000, 000, 000, 000, 000,
/*+/- $ * ? */
/* 20 */ 060, 000, 000, 000, 000, 053, 054, 055,
/* ? +/- */
/* 28 */ 056, 060, 000, 000, 000, 000, 000, 000,
/* - / , %/( ? */
/* 30 */ 040, 021, 000, 000, 000, 033, 034, 035,
/* ? sm */
/* 38 */ 036, 037, 000, 000, 000, 000, 000, 000,
/* =/# !/@ ? */
/* 40 */ 000, 000, 000, 000, 000, 013, 014, 015,
/* ? tm */
/* 48 */ 016, 017, 000, 000, 000, 000, 000, 000,
/* */
/* 50 */ 000, 000, 000, 000, 000, 000, 000, 000,
/* */
/* 58 */ 000, 000, 000, 000, 000, 000, 000, 000,
/* +0 A B C D E F G */
/* 60 */ 072, 061, 062, 063, 064, 065, 066, 067,
/* H I */
/* 68 */ 070, 071, 000, 000, 000, 000, 000, 000,
/* -0 J K L M N O P */
/* 70 */ 052, 041, 042, 043, 044, 045, 046, 047,
/* Q R */
/* 78 */ 050, 051, 000, 000, 000, 000, 000, 000,
/* rm S T U V W X */
/* 80 */ 032, 000, 022, 023, 024, 025, 026, 027,
/* Y Z */
/* 88 */ 030, 031, 000, 000, 000, 000, 000, 000,
/* 0 1 2 3 4 5 6 7 */
/* 90 */ 012, 001, 002, 003, 004, 005, 006, 007,
/* 8 9 */
/* 98 */ 010, 011, 000, 000, 000, 000, 000, 000
};
t_stat
chan_reset(DEVICE * dptr)
{
int i;
/* Clear channel assignment */
for (i = 0; i < NUM_CHAN; i++) {
chan_flags[i] = 0;
chan_info[i] = 0;
caddr[i] = 0;
cmd[i] = 0;
bcnt[i] = 10;
chan_irq[i] = 0;
limit[i] = 0;
location[i] = 0;
}
return chan_set_devs(dptr);
}
/* Boot from given device */
t_stat
chan_boot(int32 unit_num, DEVICE * dptr)
{
return SCPE_NOFNC; /* Not implimented until I know how boot work */
}
t_stat
chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) {
DEVICE **dptr;
DIB *dibp;
uint32 j;
UNIT *uptr;
for (dptr = sim_devices; *dptr != NULL; dptr++) {
int r;
dibp = (DIB *) (*dptr)->ctxt;
/* If no DIB, not channel device */
if (dibp == 0)
continue;
uptr = (*dptr)->units;
/* If this is a 79XX device, check it */
if (dibp->ctype & CH_TYP_79XX) {
for (j = 0; j < (*dptr)->numunits; j++, uptr++) {
if ((uptr->flags & UNIT_DIS) == 0 &&
UNIT_G_CHAN(uptr->flags) == chan &&
(dev == ((UNIT_SELECT & uptr->flags) != 0))) {
r = dibp->cmd(uptr, dcmd, dev);
if (r != SCPE_NODEV)
return r;
}
}
} else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) {
if (dibp->upc == 1) {
for (j = 0; j < (*dptr)->numunits; j++) {
if ((uptr->flags & UNIT_DIS) == 0 &&
UNIT_G_CHAN(uptr->flags) == chan) {
r = dibp->cmd(uptr, dcmd, dev);
if (r != SCPE_NODEV)
return r;
}
uptr++;
}
} else {
if ((uptr->flags & UNIT_DIS) == 0 &&
UNIT_G_CHAN(uptr->flags) == chan) {
r = dibp->cmd(uptr, dcmd, dev);
if (r != SCPE_NODEV)
return r;
}
}
}
}
return SCPE_NODEV;
}
/* Execute the next channel instruction. */
void
chan_proc()
{
int chan;
int cmask;
/* Scan channels looking for work */
for (chan = 0; chan < NUM_CHAN; chan++) {
/* Skip if channel is disabled */
if (chan_unit[chan].flags & UNIT_DIS)
continue;
cmask = 0x0100 << chan;
switch (CHAN_G_TYPE(chan_unit[chan].flags)) {
case CHAN_UREC:
case CHAN_7604:
/* If channel is disconnecting, do nothing */
if (chan_flags[chan] & DEV_DISCO)
continue;
/* If device requested attention, abort current command */
if (chan_flags[chan] & CHS_ATTN) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn\n",
chan);
chan_flags[chan] &=
~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT);
/* Disconnect if selected */
if (chan_flags[chan] & DEV_SEL)
chan_flags[chan] |= (DEV_DISCO);
continue;
}
/* If no select, stop channel */
if ((chan_flags[chan] & DEV_SEL) == 0
&& (chan_flags[chan] & STA_TWAIT)) {
t_uint64 temp;
int adr;
chan_trap:
if (chan != 0) {
adr = 100 + (chan * 10) + (chan_info[chan] & 0xf);
temp = 2;
if (chan_info[chan] & CHAN_TWE)
temp = 0;
else if (chan_flags[chan] & CHS_ERR)
temp = 1;
else if (chan_flags[chan] & CHS_EOF)
temp = 5;
else if (chan_info[chan] & CHAN_SEOS)
temp = 6;
else if (chan_info[chan] & CHAN_SCLR)
temp = 7;
else if ((chan_info[chan] & CHAN_NORDW) == 0) {
if ((chan_info[chan] & CHAN_SEOR) == 0 &&
caddr[chan] > limit[chan])
temp = 4;
else if (caddr[chan] < limit[chan])
temp = 3;
}
chan_flags[chan] &= ~(CHS_ERR|CHS_EOF);
temp <<= 32;
if (chan_info[chan] & CHAN_NORDW) {
temp |= M[adr] & 0xFFFFFFFFLL;
} else {
upd_idx(&temp, caddr[chan]);
bin_dec(&temp, location[chan], 0, 4);
}
temp |= PSIGN;
/* Copy over flag */
temp |= M[adr] & 0xF000000000LL;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_TRAP, &chan_dev,
"chan %d Trap: %012llx prio=%d\n\r", chan,
temp, (chan_info[chan]&CHAN_PRIO)?1:0);
M[adr] = temp;
if ((chan_info[chan] & CHAN_PRIO) ||
((temp >> 32) & 0xf) != 2)
pri_latchs[chan] |= 1 << (chan_info[chan] & 0xF);
chan_info[chan] &= ~CHAN_PRIO;
} else if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap %04x\n",
chan, chan_info[chan]);
chan_flags[chan] &=
~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT);
continue;
}
/* No activity, nothing happening here folks, move along */
if ((chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) == 0) {
/* This could be a no data command, if pending priorty
request, ask device if it is ready */
if ((cmd[chan] & CHN_SEGMENT) == 0 &&
chan_info[chan] & CHAN_PRIO &&
chan_issue_cmd(chan, IO_TRS, chan_info[chan]&0xf)
== SCPE_OK)
goto chan_trap;
continue;
}
/* If first time through here, load up channel control */
if (chan_flags[chan] & STA_ACTIVE && chan_info[chan] & CHAN_START)
chan_fetch(chan);
/* Process reading of a segment command */
if ((cmd[chan] & (CHN_SEGMENT|CHN_RM_FND)) ==
(CHN_SEGMENT|CHN_RM_FND)) {
/* Two backspaces and a read */
switch (cmd[chan] & (CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS)) {
case CHN_RM_FND:
if (chan_issue_cmd(chan, IO_BSR,
chan_info[chan]&0xf) == SCPE_OK) {
cmd[chan] |= CHN_COMPRESS;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"segment %d bsr 2\n\r", chan);
}
break;
case CHN_RM_FND|CHN_COMPRESS:
if (chan_issue_cmd(chan, IO_BSR,
chan_info[chan]&0xf) == SCPE_OK) {
cmd[chan] |= CHN_NUM_MODE;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"segment %d bsr 2\n\r", chan);
if (chan_flags[chan] & CHS_BOT) {
chan_flags[chan] &= ~(STA_ACTIVE);
goto chan_trap;
}
}
break;
case CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS:
chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST);
if ( chan_issue_cmd(chan, IO_RDS,
chan_info[chan]&0xf) == SCPE_OK) {
cmd[chan] &= ~(CHN_NUM_MODE|CHN_COMPRESS|CHN_RM_FND);
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"segment %d read\n\r", chan);
chan_flags[chan] &= ~(STA_WAIT|DEV_REOR);
}
break;
}
/* Device ready, decide command to issue */
if (cmd[chan] & CHN_RECORD) {
if (chan_flags[chan] & CHS_BOT) {
chan_flags[chan] |= STA_TWAIT;
}
/* Two backspaces and a read */
} else if (chan_flags[chan] & CHS_EOT) {
chan_flags[chan] |= STA_TWAIT;
}
continue;
}
/* None disabled, active channel is if transfering */
switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) {
/* Device has given us a dataword */
case DEV_FULL:
/* Process reading of a segment command */
if (cmd[chan] & CHN_SEGMENT) {
/* Check if hit end of record */
#if 0 /* Check segment operation correct before removing */
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"chk segment %d %d data = %012llx",
chan, bcnt[chan], assembly[chan]);
if ((chan_flags[chan] & (STA_WAIT|DEV_REOR))
== (STA_WAIT|DEV_REOR)) {
chan_flags[chan] &= ~(STA_WAIT|DEV_REOR|DEV_FULL);
chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST);
continue;
}
if (bcnt[chan] >= 6 && chan_flags[chan] & DEV_REOR) {
if (chan_info[chan] & CHAN_SEOS) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev, " found\n");
/* Correct error */
chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR);
/* What we were looking for? */
if (caddr[chan] >= limit[chan]) {
chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF);
chan_flags[chan] |= STA_TWAIT;
chan_info[chan] |= CHAN_SEOR;
} else
caddr[chan]++;
}
chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST|CHAN_TWE);
chan_flags[chan] &= ~(DEV_FULL|DEV_REOR|CHS_ERR);
} else
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev, " search\n");
/* How about regular record */
chan_flags[chan] &= ~DEV_FULL;
/* Wait for next record */
chan_flags[chan] |= STA_WAIT|DEV_DISCO;
if (cmd[chan] & CHN_RECORD)
cmd[chan] |= CHN_RM_FND;
else
cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE;
assembly[chan] = 0;
bcnt[chan] = 10;
#endif
continue;
}
/* If we are not waiting EOR save it in memory */
if ((chan_flags[chan] & STA_WAIT) == 0) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d data < %011llx\n",
chan, assembly[chan]);
if (caddr[chan] < MEMSIZE)
M[caddr[chan]] = assembly[chan];
if (bcnt[chan] != 0)
chan_info[chan] |= CHAN_SCLR;
else
chan_info[chan] &= ~CHAN_SCLR;
/* Check if last transfer before final */
if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) {
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT|STA_WAIT;
} else
caddr[chan]++;
/* Update channel status word */
if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) {
int adr = 100 + (chan * 10) + (chan_info[chan]&0xf);
upd_idx(&M[adr], caddr[chan]);
bin_dec(&M[adr], location[chan], 0, 4);
}
/* Check for record mark */
if ((cmd[chan] & CHN_RECORD) &&
(assembly[chan] & SMASK) == ASIGN &&
(assembly[chan] & 0xFF) == RM_CHAR) {
if (cmd[chan] & CHN_LAST) {
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT|STA_WAIT;
} else
chan_fetch(chan);
}
bcnt[chan] = 10;
assembly[chan] = 0;
}
chan_info[chan] |= CHAN_FIRST; /* Saved one char */
chan_flags[chan] &= ~DEV_FULL;
/* Device does not need a word and has not given us one */
case 0:
if (chan_flags[chan] & DEV_REOR) {
/* Check EOR at end of segment */
if (cmd[chan] & CHN_SEGMENT) {
if ((chan_info[chan] & CHAN_FIRST) == 0 &&
bcnt[chan] == 8 &&
assembly[chan] == (ASIGN|(((t_uint64)SM_MEM) << 32))){
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"chk segment %d %d data = %012llx found\n\r",
chan, bcnt[chan], assembly[chan]);
/* What we were looking for? */
if (caddr[chan] >= limit[chan]) {
chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF);
chan_flags[chan] |= STA_TWAIT;
} else
caddr[chan]++;
} else
/* Check if hit end of record */
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DETAIL, &chan_dev,
"chk segment %d %d data = %012llx search\n\r",
chan, bcnt[chan], assembly[chan]);
/* Correct error */
chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR);
assembly[chan] = 0;
bcnt[chan] = 10;
chan_flags[chan] &= ~(DEV_REOR|DEV_FULL);
/* Wait for next record */
chan_flags[chan] |= STA_WAIT|DEV_DISCO;
if (cmd[chan] & CHN_RECORD)
cmd[chan] |= CHN_RM_FND;
else
cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE;
continue;
}
/* Device idle, expecting data from it */
/* Check if got EOR */
chan_flags[chan] &= ~(DEV_REOR|STA_ACTIVE|STA_WAIT);
chan_flags[chan] |= STA_TWAIT|DEV_DISCO;
chan_info[chan] |= CHAN_SEOR;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR< %o\n",
chan, cmd[chan]);
continue;
}
if (caddr[chan] > limit[chan]
&& (chan_flags[chan] & STA_WAIT) == 0) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev,
"chan %d < WC0 %o\n", chan, cmd[chan]);
if (cmd[chan] & CHN_LAST) {
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_DISCO;
chan_info[chan] &= ~CHAN_SEOR;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev,
"chan %d < DISCO\n", chan);
} else
chan_fetch(chan);
}
break;
/* Device has word, but has not taken it yet */
case DEV_WRITE | DEV_FULL:
continue; /* Do nothing if no data xfer pending */
/* Device needs a word of data */
case DEV_WRITE: /* Device needs data word */
/* If we are waiting on EOR, do nothing */
if (chan_flags[chan] & STA_WAIT)
continue;
/* Special for write segment mark command */
if (cmd[chan] & CHN_SEGMENT) {
/* Send one char */
assembly[chan] = SM_MEM;
bcnt[chan] = 2;
caddr[chan] = limit[chan]+1;
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_FULL|DEV_WEOR;
cmd[chan] = CHN_ALPHA|CHN_SEGMENT;
chan_info[chan] |= CHAN_NORDW;
continue;
}
/* Give device new word if we have one */
if (caddr[chan] <= limit[chan]) {
/* Check if got EOR */
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &= ~(STA_WAIT | DEV_REOR
| STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev,
"chan %d EOR> %o\n", chan, cmd[chan] & 070);
continue;
}
if (caddr[chan] < MEMSIZE)
assembly[chan] = M[caddr[chan]];
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d data > %011llx\n", chan,
assembly[chan]);
bcnt[chan] = 10;
chan_flags[chan] |= DEV_FULL;
/* Check if last transfer before final */
if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) {
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT|STA_WAIT;
} else
caddr[chan]++;
/* Update channel status word */
if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) {
int adr = 100 + (chan * 10) + (chan_info[chan]&0xf);
upd_idx(&M[adr], caddr[chan]);
bin_dec(&M[adr], location[chan], 0, 4);
}
/* Check for record mark */
if ((cmd[chan] & CHN_RECORD) &&
(assembly[chan] & SMASK) == ASIGN &&
(assembly[chan] & 0xFF) == RM_CHAR) {
if (cmd[chan] & CHN_LAST) {
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT|STA_WAIT;
} else
chan_fetch(chan);
}
continue; /* Don't start next command until data taken */
}
/* Wait for device to recognize EOR */
if (chan_flags[chan] & DEV_WEOR)
continue;
/* Get here if passed limit */
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev, "chan %d > WC0 %o stat=%x\n",
chan, cmd[chan] & 070, chan_flags[chan]);
if (cmd[chan] & CHN_LAST) {
chan_flags[chan] |= DEV_DISCO | DEV_WEOR | STA_TWAIT;
chan_flags[chan] &= ~(STA_START | STA_ACTIVE);
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_EXP, &chan_dev,
"chan %d > DISCO\n", chan);
} else
chan_fetch(chan);
}
break;
case CHAN_7907:
/* If channel is disconnecting, just hold on */
if (chan_flags[chan] & DEV_DISCO)
continue;
/* If no select, stop channel */
if ((chan_flags[chan] & DEV_SEL) == 0
&& (chan_flags[chan] & STA_TWAIT)) {
t_uint64 temp;
temp = 2;
if (chan_info[chan] & CHAN_TWE)
temp = 1;
else if (chan_flags[chan] & SNS_UEND)
temp = 5;
else if ((chan_info[chan] & CHAN_SEOR) == 0 && op[chan] == 1)
temp = 4;
else if (caddr[chan] <= limit[chan] &&
(op[chan] == 1 || op[chan] == 3))
temp = 3;
temp <<= 36;
chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2);
temp |= (chan_irq[chan])?MSIGN:PSIGN;
chan_flags[chan] &= ~(SNS_UEND|CTL_CNTL|CTL_SNS|CTL_READ|
CTL_WRITE|CTL_PREAD|CTL_PWRITE);
upd_idx(&temp, caddr[chan]);
bin_dec(&temp, location[chan], 0, 4);
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap: %012llx\n",
chan, temp);
M[(chan - 4) + 300] = temp;
if ((chan_info[chan] & CHAN_PRIO) || ((temp >> 36) & 0xf) != 2)
pri_latchs[8] |= 1<<(4-chan);
chan_flags[chan] &=
~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT);
chan_info[chan] &= ~CHAN_PRIO;
continue;
}
/* Check if device raised attention */
if ((chan_flags[chan] & (STA_ACTIVE|DEV_SEL|STA_TWAIT)) == 0 &&
(chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2))) {
t_uint64 temp;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Attn Trap\n",
chan);
temp = 2;
if (chan_flags[chan] & SNS_UEND)
temp = 5;
temp <<= 36;
temp |= MSIGN;
chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2);
chan_flags[chan] &= ~(SNS_ATTN1|SNS_ATTN2|SNS_UEND);
upd_idx(&temp, caddr[chan]);
bin_dec(&temp, location[chan], 0, 4);
M[(chan - 4) + 300] = temp;
pri_latchs[9] |= 1 << (4 - chan);
continue;
}
/* Nothing more to do if not active. */
if (chan_flags[chan] & STA_ACTIVE) {
t_uint64 temp;
/* Execute the next command */
switch (op[chan]) {
case 6: /* Tranfer in channel */
/* I am not sure if this is correct, but it passes
diagnostics */
location[chan] = limit[chan];
break;
case 0: /* Write status */
temp = PSIGN|(2LL << 36);
upd_idx(&temp, caddr[chan]);
bin_dec(&temp, location[chan], 0, 4);
M[caddr[chan]] = temp;
break;
case 1: /* Read */
/* Check if in other mode */
if (chan_flags[chan] & (CTL_CNTL)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d read busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &=
~(CTL_CNTL|DEV_REOR|DEV_WRITE);
} else
continue;
}
/* Check if last command not finished */
if (chan_flags[chan] & (CTL_SNS|CTL_WRITE)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d read busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= DEV_DISCO;
chan_flags[chan] &= ~(CTL_SNS|CTL_WRITE);
}
continue;
}
/* If not already in read mode, set read mode */
if ((chan_flags[chan] & CTL_READ) == 0) {
chan_flags[chan] |= CTL_READ;
chan_flags[chan] &= ~(DEV_FULL|DEV_WRITE|DEV_REOR);
chan_info[chan] &= ~CHAN_SEOR;
bcnt[chan] = 10;
assembly[chan] = 0;
continue;
}
/* Has device given us a word */
if (chan_flags[chan] & DEV_FULL) {
/* Check if record mark */
if ((cmd[chan] & CHN_RECORD) &&
(assembly[chan] & SMASK) == ASIGN &&
(assembly[chan] & 0xFF) == RM_CHAR) {
break;
}
/* Check if ready to transfer something */
if (caddr[chan] <= limit[chan]) {
M[caddr[chan]] = assembly[chan];
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d data > %012llx\n",
chan, assembly[chan]);
caddr[chan]++;
bcnt[chan] = 10;
assembly[chan] = 0;
chan_flags[chan] &= ~DEV_FULL;
/* Check if last word transfered */
if (caddr[chan] > limit[chan])
break;
}
/* Check if we still have a select signal */
if ((chan_flags[chan] & DEV_SEL) == 0) {
chan_info[chan] |= CHAN_TWE;
chan_flags[chan] &=
~(CTL_WRITE|CTL_END|STA_ACTIVE);
break;
}
/* Device gave us a EOR, get next word */
if (chan_flags[chan] & DEV_REOR) {
chan_info[chan] |= CHAN_SEOR;
chan_flags[chan] &= ~DEV_REOR;
break;
}
continue;
}
/* Abort if we get control end */
if (chan_flags[chan] & CTL_END) {
/* Disconnect channel if select still active */
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= (DEV_DISCO);
}
if (chan_flags[chan] & DEV_REOR)
chan_info[chan] |= CHAN_SEOR;
chan_flags[chan] &=
~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END);
break;
}
continue;
case 3: /* Write */
/* Check if in other mode */
if (chan_flags[chan] & (CTL_CNTL)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d write busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &=
~(CTL_CNTL|DEV_REOR|DEV_WRITE);
} else
continue;
}
/* Check if last command not finished */
if (chan_flags[chan] & (CTL_SNS|CTL_READ)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d write busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= DEV_DISCO;
chan_flags[chan] &= ~(CTL_READ|CTL_SNS);
}
continue;
}
/* If not in write, flag as write */
if ((chan_flags[chan] & CTL_WRITE) == 0) {
chan_flags[chan] |= CTL_WRITE|DEV_WRITE;
}
/* Command finished?*/
if (chan_flags[chan] & CTL_END) {
/* Disconnect channel if select still active */
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= (DEV_DISCO);
}
chan_flags[chan] &=
~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END);
break;
}
/* Check if we still have a select signal */
if ((chan_flags[chan] & DEV_SEL) == 0 &&
caddr[chan] < limit[chan]) {
chan_info[chan] |= CHAN_TWE;
chan_flags[chan] &= ~(CTL_WRITE|CTL_END|STA_ACTIVE);
break;
}
/* Check if device needs data */
if ((chan_flags[chan] & DEV_FULL) == 0) {
/* Got EOR? */
if (chan_flags[chan] & DEV_REOR) {
if (caddr[chan] > limit[chan]) {
chan_info[chan] |= CHAN_SEOR;
}
chan_flags[chan] |= DEV_DISCO;
chan_flags[chan] &= ~DEV_REOR;
break;
}
/* Check if ready to transfer something */
if (caddr[chan] <= limit[chan]) {
assembly[chan] = M[caddr[chan]];
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d data > %012llx\n",
chan, assembly[chan]);
caddr[chan]++;
bcnt[chan] = 10;
chan_flags[chan] |= DEV_FULL;
/* Check if record mark */
if ((cmd[chan] & CHN_RECORD) &&
(assembly[chan] & SMASK) == ASIGN &&
(assembly[chan] & 0xFF) == RM_CHAR) {
chan_flags[chan] |= DEV_WEOR;
break;
}
continue;
}
chan_info[chan] |= CHAN_SEOR;
break;
}
continue;
case 5: /* Sense */
/* Check if in other mode */
if (chan_flags[chan] & (CTL_CNTL)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d sense busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &=
~(CTL_CNTL|DEV_REOR|DEV_WRITE);
} else
continue;
}
if (chan_flags[chan] & CTL_SNS) {
/* Check if we still have a select signal */
if ((chan_flags[chan] & DEV_SEL) == 0) {
chan_info[chan] |= CHAN_TWE;
chan_flags[chan] &= ~(CTL_SNS|STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT;
break;
}
/* If device ended, quit transfer */
if ((chan_flags[chan] & DEV_FULL) == 0) {
if (chan_flags[chan] & CTL_END) {
/* Disconnect channel if select still active */
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= (DEV_DISCO);
}
if (chan_flags[chan] & DEV_REOR) {
chan_info[chan] |= CHAN_SEOR;
chan_flags[chan] &= ~DEV_REOR;
}
chan_flags[chan] &= ~(CTL_SNS);
break;
}
/* Check if last word transfered */
if (caddr[chan] > limit[chan]) {
if (chan_flags[chan] & SNS_UEND) {
chan_flags[chan] |=
(DEV_DISCO | DEV_WEOR);
chan_flags[chan] &= ~(DEV_SEL);
} else {
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &= ~DEV_REOR;
chan_info[chan] |= CHAN_SEOR;
}
}
chan_flags[chan] &= ~(CTL_SNS);
break;
}
} else {
/* Device has given us a dataword */
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d data < %012llx\n",
chan, assembly[chan]);
M[caddr[chan]] = assembly[chan];
assembly[chan] = 0;
bcnt[chan] = 10;
chan_flags[chan] &= ~DEV_FULL;
if (caddr[chan] >= limit[chan])
break;
caddr[chan]++;
}
/* Handle EOR on sense */
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &= ~(CTL_SNS|DEV_REOR);
chan_info[chan] |= CHAN_SEOR;
break;
}
continue;
}
/* Check if in other mode */
if (chan_flags[chan] & (CTL_CNTL|CTL_READ|CTL_WRITE)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d sense busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT;
}
chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE);
continue;
}
/* Start sense command */
chan_flags[chan] |= CTL_SNS;
chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL);
switch(chan_issue_cmd(chan,0,chan_test(chan, CTL_SEL))){
case SCPE_IOERR:
case SCPE_NODEV:
chan_info[chan] |= CHAN_TWE;
chan_flags[chan] &= ~STA_ACTIVE;
case SCPE_BUSY: /* Device not ready yet, wait */
chan_flags[chan] &= ~(CTL_SNS);
continue;
case SCPE_OK:
/* Device will be waiting for command */
break;
}
/* Get channel ready to transfer */
chan_flags[chan] &= ~DEV_WRITE;
chan_flags[chan] |= DEV_SEL;
continue;
case 4: /* Transfer command */
if (chan_flags[chan] & CTL_CNTL)
goto xfer;
if (chan_flags[chan] & (CTL_READ|CTL_WRITE|CTL_SNS)) {
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_DATA, &chan_dev,
"chan %d control busy %04x\n",
chan, chan_flags[chan]);
if (chan_flags[chan] & DEV_SEL) {
chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT;
}
chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE);
continue;
}
chan_flags[chan] |= CTL_CNTL;
/* Get channel ready to transfer */
chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL);
switch(chan_issue_cmd(chan,0,chan_stat(chan,CTL_SEL))){
case SCPE_IOERR:
case SCPE_NODEV:
chan_info[chan] |= CHAN_TWE;
chan_flags[chan] &=
~(CTL_SNS|CTL_CNTL|STA_ACTIVE);
continue;
case SCPE_BUSY:
/* Device not ready yet, wait */
continue;
case SCPE_OK:
/* Device will be waiting for command */
break;
}
chan_flags[chan] |= DEV_WRITE;
xfer:
/* Check if comand tranfer done */
if (chan_flags[chan] & DEV_REOR) {
chan_flags[chan] &=
~(DEV_WRITE|DEV_REOR|DEV_FULL|CTL_CNTL);
chan_info[chan] |= CHAN_SEOR;
break;
}
/* Wait for device to grab the command */
if (chan_flags[chan] & DEV_FULL)
continue;
/* Check if device ready for next command word */
if ((chan_flags[chan] & (DEV_WRITE | DEV_FULL)) ==
DEV_WRITE) {
if (caddr[chan] <= limit[chan]) {
assembly[chan] = M[caddr[chan]];
chan_flags[chan] |= DEV_FULL;
bcnt[chan] = 10;
if (chan_dev.dctrl & cmask)
sim_debug(DEBUG_CMD, &chan_dev,
"chan %d cmd > %012llx\n",
chan, assembly[chan]);
if (caddr[chan] < limit[chan])
caddr[chan]++;
continue;
}
}
break;
case 2: /* Read Backwards */
/* Unknown commands at moment */
case 7:
case 8:
case 9:
chan_info[chan] |= CHAN_TWE;
if (chan_flags[chan] & DEV_SEL)
chan_flags[chan] |= DEV_DISCO;
chan_flags[chan] &=
~(STA_ACTIVE|CTL_WRITE|CTL_READ|CTL_CNTL|CTL_SNS);
break;
}
/* If last all done */
if (cmd[chan] & CHN_LAST || chan_flags[chan] & (SNS_UEND)
|| chan_info[chan] & CHAN_TWE) {
if (chan_flags[chan] & DEV_SEL)
chan_flags[chan] |= DEV_DISCO;
chan_flags[chan] &= ~(STA_ACTIVE);
chan_flags[chan] |= STA_TWAIT;
} else
chan_fetch(chan);
}
continue;
}
}
}
void
chan_fetch(int chan)
{
uint32 loc = location[chan];
t_uint64 temp;
sim_interval--;
chan_info[chan] &= ~CHAN_START;
if (loc < MEMSIZE)
temp = M[loc];
else {
cmd[chan] |= CHN_LAST;
return;
}
location[chan] = (loc + 1);
if ((temp & SMASK) == MSIGN)
cmd[chan] |= CHN_LAST;
get_rdw(temp, &caddr[chan], &limit[chan]);
op[chan] = (temp >> 36) & 0xf;
if (chan_dev.dctrl & (0x0100 << chan))
sim_debug(DEBUG_CHAN, &chan_dev,
"chan %d fetch adr=%05d op=%d cmd=%03o caddr=%05d limit=%05d\n",
chan, loc, op[chan], cmd[chan], caddr[chan], limit[chan]);
}
void chan_set_attn_a(int chan) {
pri_latchs[0] |= 0x002;
}
void chan_set_attn_b(int chan) {
pri_latchs[0] |= 0x004;
}
void chan_set_attn_inq(int chan) {
if (chan == CHAN_UREC)
pri_latchs[0] |= 0x080;
else
pri_latchs[0] |= 0x100;
}
void chan_clear_attn_inq(int chan) {
if (chan == CHAN_UREC)
pri_latchs[0] &= ~0x080;
else
pri_latchs[0] &= ~0x100;
}
/* Issue a command to a channel */
int
chan_cmd(uint16 dev, uint16 dcmd, uint16 addr)
{
uint32 chan;
int prio;
t_stat r;
/* Find device on given channel and give it the command */
chan = (dev >> 8) & 0xf;
/* If no channel device, quick exit */
if (chan_unit[chan].flags & UNIT_DIS)
return SCPE_IOERR;
/* Unit is busy doing something, wait */
if (chan_flags[chan] & (DEV_SEL|DEV_DISCO|STA_TWAIT|STA_WAIT|STA_ACTIVE))
return SCPE_BUSY;
/* Ok, try and find the unit */
prio = (dev & 0x1000)? 1: 0;
dev &= 0xff;
location[chan] = addr;
cmd[chan] = dcmd & 0xff;
dcmd >>= 8;
chan_info[chan] = (dev & 0xf) | (chan << 4);
chan_info[chan] |= CHAN_START;
/* Special check for console */
if (chan == 0 && dev == 0)
chan_info[chan] |= CHAN_OUTDEV;
/* Check for octal translation */
if (chan == 1 && dev & 020)
chan_info[chan] |= CHAN_OCTAL;
/* Enable priority */
if (prio)
chan_info[chan] |= CHAN_PRIO;
assembly[chan] = 0;
bcnt[chan] = 10;
if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7907) {
chan_flags[chan] |= STA_ACTIVE;
if (dev & 1)
chan_flags[chan] |= CTL_SEL;
else
chan_flags[chan] &= ~CTL_SEL;
chan_fetch(chan);
return SCPE_OK;
}
r = chan_issue_cmd(chan, dcmd, dev);
if (r != SCPE_OK) {
/* No device, kill active */
chan_flags[chan] &= ~(STA_ACTIVE);
} else {
extern uint32 IC;
/* If transfering data, activate channel */
if (chan_flags[chan] & DEV_SEL)
chan_flags[chan] |= STA_ACTIVE;
if (chan_dev.dctrl & (0x0100 << chan))
sim_debug(DEBUG_CMD, &chan_dev,
"chan %d cmd=%o IC=%05d addr=%05d\n\r", chan, dcmd, IC, addr);
}
return r;
}
/*
* Write a word to the assembly register.
*/
int
chan_write(int chan, t_uint64 * data, int flags)
{
return TIME_ERROR;
}
/*
* Read next word from assembly register.
*/
int
chan_read(int chan, t_uint64 * data, int flags)
{
return TIME_ERROR;
}
/*
* Write a char to the assembly register.
*/
int
chan_write_char(int chan, uint8 * data, int flags)
{
uint8 ch = *data;
/* Check if last data still not taken */
if (chan_flags[chan] & DEV_FULL) {
/* Nope, see if we are waiting for end of record. */
if (chan_flags[chan] & DEV_WEOR) {
chan_flags[chan] &= ~DEV_WEOR;
chan_flags[chan] |= DEV_REOR;
return END_RECORD;
}
if (chan_flags[chan] & STA_ACTIVE) {
chan_flags[chan] |= CHS_ATTN; /* We had error */
}
if (chan == 0) {
chan_flags[chan] |= DEV_DISCO;
}
return TIME_ERROR;
}
#if 0 /* Check segment working before removing */
/* Check for segment mark */
if (flags & DEV_REOR && (chan_info[chan] & CHAN_FIRST) == 0) {
if (ch == SM_CHAR)
chan_info[chan] |= CHAN_SEOS;
assembly[chan] |= ASIGN|(((t_uint64)bcd_mem[ch]) << 32);
if (cmd[chan] & CHN_ALPHA)
chan_flags[chan] |= DEV_FULL|DEV_REOR;
else
chan_flags[chan] |= DEV_REOR;
chan_flags[chan] &= ~DEV_WRITE;
chan_proc();
return END_RECORD;
}
/* Clear first char in record flag */
chan_info[chan] |= CHAN_FIRST;
#endif
if (ch == DELTA_CHAR && (cmd[chan] & CHN_ALPHA) == 0) {
if (bcnt[chan] == 10)
cmd[chan] ^= CHN_NUM_MODE;
else
chan_info[chan] |= CHAN_TWE;
} else {
if (chan_flags[chan] & CTL_SNS) {
/* Set sign based on attention signal */
if (bcnt[chan] == 10) {
if (chan_irq[chan] & (SNS_ATTN1 >> (chan_info[chan] & 1)))
assembly[chan] = PSIGN;
else
assembly[chan] = MSIGN;
chan_irq[chan] &= ~(SNS_ATTN1 >> (chan_info[chan] & 1));
}
/* Store character */
ch &= 0x17;
if (ch & 0x04)
ch ^= 0x24; /* Bit move */
ch |= 0x44;
bcnt[chan]-=2;
assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan]);
} else if (cmd[chan] & CHN_NUM_MODE) {
ch &= 0xf;
if (ch == 0 || ch > 10)
chan_info[chan] |= CHAN_TWE;
else if (ch == 10)
ch = 0;
bcnt[chan]--;
assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan])|PSIGN;
/* Check for sign digit */
switch(*data & 060) {
case 0: /* Normal digit */
case 020: /* error */
if (bcnt[chan] == 0)
chan_info[chan] |= CHAN_TWE;
break;
case 040:
if (bcnt[chan] > 5)
chan_info[chan] |= CHAN_TWE;
assembly[chan] &= DMASK;
/* Put number in right location */
while(bcnt[chan] != 0) {
bcnt[chan]--;
assembly[chan] >>= 4;
}
assembly[chan] |= MSIGN;
break;
case 060:
if (bcnt[chan] > 5)
chan_info[chan] |= CHAN_TWE;
assembly[chan] &= DMASK;
/* Put number in right location */
while(bcnt[chan] != 0) {
bcnt[chan]--;
assembly[chan] >>= 4;
}
assembly[chan] |= PSIGN;
break;
}
} else {
if (chan_info[chan] & CHAN_OCTAL)
ch = ((ch & 070) << 1) | (ch & 07);
else
ch = bcd_mem[ch];
if (ch == 0xFF) {
chan_info[chan] |= CHAN_TWE;
ch = 0;
}
bcnt[chan] -= 2;
assembly[chan] |= ((t_uint64)ch) << (8 * (bcnt[chan] / 2));
assembly[chan] |= (chan_info[chan] & CHAN_OCTAL)?PSIGN:ASIGN;
}
}
if (flags & DEV_REOR) {
chan_flags[chan] |= DEV_FULL|DEV_REOR;
chan_flags[chan] &= ~DEV_WRITE;
if (bcnt[chan] != 0 && ((cmd[chan] & (CHN_NUM_MODE)) == 0 ||
(cmd[chan] & (CHN_ALPHA)) != 0))
chan_info[chan] |= CHAN_SCLR;
chan_info[chan] |= CHAN_SEOR;
chan_proc();
return END_RECORD;
} else if (bcnt[chan] == 0) {
chan_flags[chan] |= DEV_FULL;
chan_flags[chan] &= ~DEV_WRITE;
chan_proc();
}
/* If Writing end of record, abort */
if (flags & DEV_WEOR) {
chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR);
return END_RECORD;
}
return DATA_OK;
}
/*
* Read next char from assembly register.
*/
int
chan_read_char(int chan, uint8 * data, int flags)
{
uint8 ch;
/* Return END_RECORD if requested */
if (flags & DEV_WEOR) {
chan_flags[chan] &= ~(DEV_WEOR /*| STA_WAIT*/);
return END_RECORD;
}
chan_proc();
/* Check if he write out last data */
if ((chan_flags[chan] & DEV_FULL) == 0) {
if (chan_flags[chan] & DEV_WEOR) {
chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT | DEV_WRITE);
chan_flags[chan] |= DEV_REOR|DEV_DISCO;
return END_RECORD;
}
if (chan_flags[chan] & STA_ACTIVE) {
chan_flags[chan] |= CHS_ATTN;
}
if (chan == 0) {
chan_flags[chan] |= DEV_DISCO;
}
return TIME_ERROR;
}
/* Send control words differently */
if (chan_flags[chan] & CTL_CNTL) {
if ((assembly[chan] & SMASK) == ASIGN) {
bcnt[chan] -= 2;
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff;
*data = mem_bcd[ch];
} else {
bcnt[chan]--;
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf;
if (ch == 0)
ch = 10;
*data = ch;
}
goto done;
}
/* Handle console type out */
if (chan_info[chan] & CHAN_OUTDEV) {
if (bcnt[chan] == 10 && (cmd[chan] & CHN_NUM_MODE) == 0) {
switch(assembly[chan] & SMASK) {
case ASIGN: break;
case PSIGN: *data = 060;
cmd[chan] |= CHN_NUM_MODE;
return SCPE_OK;
case MSIGN: *data = 040;
cmd[chan] |= CHN_NUM_MODE;
return SCPE_OK;
}
}
if (cmd[chan] & CHN_NUM_MODE) {
bcnt[chan]--;
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf;
if (ch == 0)
ch = 10;
if (bcnt[chan] == 0)
cmd[chan] &= ~CHN_NUM_MODE;
*data = ch;
} else {
bcnt[chan] -= 2;
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff;
*data = mem_bcd[ch];
}
goto done;
}
/* Check for mode change */
if (bcnt[chan] == 10 && (cmd[chan] & (CHN_ALPHA)) == 0) {
if (((assembly[chan] & SMASK) == ASIGN &&
(cmd[chan] & CHN_NUM_MODE) == CHN_NUM_MODE)
||((assembly[chan] & SMASK) != ASIGN &&
(cmd[chan] & CHN_NUM_MODE) == CHN_ALPHA_MODE)) {
*data = DELTA_CHAR;
cmd[chan] ^= CHN_NUM_MODE;
return DATA_OK;
}
/* Handle zero compression here */
if ((cmd[chan] & (CHN_NUM_MODE|CHN_COMPRESS)) ==
(CHN_NUM_MODE|CHN_COMPRESS)) {
while((assembly[chan] >> (4 * bcnt[chan]) & 0xf) == 0 &&
bcnt[chan] < 5)
bcnt[chan]--;
}
}
/* If in number mode, dump as number */
if (cmd[chan] & CHN_NUM_MODE) {
bcnt[chan]--;
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf;
if (ch == 0)
ch = 10;
if (bcnt[chan] == 0)
ch |= ((assembly[chan] & SMASK) == MSIGN)? 040: 060;
*data = ch;
} else {
bcnt[chan] -= 2;
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff;
*data = mem_bcd[ch];
}
done:
if (bcnt[chan] == 0) {
chan_flags[chan] &= ~DEV_FULL;
bcnt[chan] = 10;
}
/* If end of record, don't transfer any data */
if (flags & DEV_REOR) {
chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL);
chan_flags[chan] |= DEV_REOR;
chan_proc();
} else
chan_flags[chan] |= DEV_WRITE;
return DATA_OK;
}
void
chan_set_load_mode(int chan)
{
cmd[chan] &= ~CHN_ALPHA;
cmd[chan] |= CHN_NUM_MODE;
}
void
chan9_set_error(int chan, uint32 mask)
{
if (chan_flags[chan] & mask)
return;
chan_flags[chan] |= mask;
}
t_stat
chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "%s\n\n", chan_description(dptr));
fprintf (st, "The 7070 supports up to 8 channels. Channel models include\n\n");
fprintf (st, " 7604 standard multiplexor channel\n");
fprintf (st, " 7907 advanced capabilities channel\n\n");
fprintf (st, "Channels are fixed on the 7070.\n\n");
fprintf (st, "Channel * is for unit record devices.\n");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
return SCPE_OK;
}
const char *
chan_description(DEVICE *dptr)
{
return "IBM 7070 channel controller";
}