1250 lines
44 KiB
C
1250 lines
44 KiB
C
/* i7080_chan.c: IBM 7080 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 channel state for the IBM 705 channel is:
|
||
705: Polled mode transfer, unit record devices.
|
||
Each chan_cmd will transfer one record.
|
||
7621: Basic data channel for 729 tapes.
|
||
7908: Channel to talk to disk, hypertape and data com.
|
||
|
||
The 705 has 4 7621 channels.
|
||
The status for these are kept in bank 2. Form as follow:
|
||
Word 3 digits 7-6: 0
|
||
Word 3 digit 5: chan control digit.
|
||
Word 3 digits 4-0: data buffer A.
|
||
Word 2 digits 7-6: 0
|
||
Word 2 digit 5: chan control digit.
|
||
Word 2 digits 4-0: data buffer B.
|
||
Word 1 digits 7-4: 0
|
||
Word 1 digits 3-0: Data Memory Address SMAC.
|
||
Word 0 digits 7-4: Channel Program Status.
|
||
Word 0 digits 3-0: Record Count/Program location.
|
||
|
||
The 705 has 2 7908 channels.
|
||
The status for these are kept in bank 4. Form as follow:
|
||
Word 3 digits 7-0: 0
|
||
Word 2 digits 7-0: 0
|
||
Word 1 digits 7-4: 0
|
||
Word 1 digits 3-0: Data Memory Address SMAC.
|
||
Word 0 digits 7-4: Channel Program Status.
|
||
Word 0 digits 3-0: Program location.
|
||
|
||
*/
|
||
|
||
#include "i7080_defs.h"
|
||
|
||
extern uint16 iotraps;
|
||
extern uint8 iocheck;
|
||
extern UNIT cpu_unit;
|
||
extern uint16 IC;
|
||
extern uint8 AC[6 * 512];
|
||
extern int chwait;
|
||
extern uint16 selreg;
|
||
extern uint16 selreg2;
|
||
extern uint16 flags;
|
||
extern uint32 MAC2;
|
||
extern uint16 irqflags;
|
||
extern uint8 ioflags[6200/8];
|
||
|
||
#define UNIT_V_MOD (UNIT_V_UF + 4)
|
||
#define UNIT_V_HS (UNIT_V_MOD + 1)
|
||
#define CHAN_MOD (1 << UNIT_V_MOD)
|
||
#define CHAN_HS (1 << UNIT_V_HS)
|
||
|
||
|
||
|
||
t_stat set_chan_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat chan_reset(DEVICE * dptr);
|
||
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 caddr[NUM_CHAN]; /* Channel memory address */
|
||
uint8 bcnt[NUM_CHAN]; /* Channel character count */
|
||
uint16 cmd[NUM_CHAN]; /* Current command */
|
||
uint16 irqdev[NUM_CHAN]; /* Device to generate interupts
|
||
for channel */
|
||
uint32 assembly[NUM_CHAN]; /* Assembly register */
|
||
uint32 chan_flags[NUM_CHAN]; /* Unit status */
|
||
extern uint8 inquiry;
|
||
|
||
|
||
#define READ_WRD 1
|
||
#define WRITE_WRD 2
|
||
|
||
const char *chan_type_name[] = {
|
||
"Polled", "Unit Record", "7621", "7908", "754"};
|
||
|
||
|
||
UNIT chan_unit[] = {
|
||
{UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_UREC), 0)},
|
||
/* Tape devices */
|
||
{UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 0}, /* 20 */
|
||
{UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 1}, /* 21 */
|
||
{UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 2}, /* 22 */
|
||
{UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 3}, /* 23 */
|
||
/* 7080 High speed data channels */
|
||
{UDATA(NULL, CHAN_HS | CHAN_SET | CHAN_S_TYPE(CHAN_7908), 0), 0, 0}, /* 40 */
|
||
{UDATA(NULL, CHAN_HS | CHAN_SET | CHAN_S_TYPE(CHAN_7908), 0), 0, 1}, /* 41 */
|
||
{UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 4}, /* 44 */
|
||
{UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 5}, /* 45 */
|
||
{UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 6}, /* 46 */
|
||
{UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 7}, /* 47 */
|
||
};
|
||
|
||
REG chan_reg[] = {
|
||
{BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO},
|
||
{BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO},
|
||
{BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO},
|
||
{NULL}
|
||
};
|
||
|
||
MTAB chan_mod[] = {
|
||
{CHAN_MODEL, CHAN_S_TYPE(CHAN_UREC), "UREC", "UREC", &set_chan_type,
|
||
NULL,NULL},
|
||
{CHAN_MODEL, CHAN_S_TYPE(CHAN_754), "754", "754", &set_chan_type,
|
||
NULL,NULL},
|
||
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7621), "7621", "7621", &set_chan_type,
|
||
NULL,NULL},
|
||
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7908), "7908", NULL, NULL,NULL,NULL},
|
||
{CHAN_HS, CHAN_HS, "HS", "HS", NULL,NULL,NULL},
|
||
{MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL},
|
||
{0}
|
||
};
|
||
|
||
|
||
/* Simulator debug controls */
|
||
DEBTAB chn_debug[] = {
|
||
{"CHANNEL", DEBUG_CHAN},
|
||
{"TRAP", DEBUG_TRAP},
|
||
{"CMD", DEBUG_CMD},
|
||
{"DATA", DEBUG_DATA},
|
||
{"DETAIL", DEBUG_DETAIL},
|
||
{"EXP", DEBUG_EXP},
|
||
{"SENSE", DEBUG_SNS},
|
||
{"CH0", 0x0100 << 0},
|
||
{"CH20", 0x0100 << 1},
|
||
{"CH21", 0x0100 << 2},
|
||
{"CH22", 0x0100 << 3},
|
||
{"CH23", 0x0100 << 4},
|
||
{"CH40", 0x0100 << 5},
|
||
{"CH41", 0x0100 << 6},
|
||
{"CH44", 0x0100 << 7},
|
||
{"CH45", 0x0100 << 8},
|
||
{"CH46", 0x0100 << 9},
|
||
{"CH47", 0x0100 << 10},
|
||
{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,
|
||
NULL, DEV_DEBUG, 0, chn_debug,
|
||
NULL, NULL, &chan_help, NULL, NULL, &chan_description
|
||
};
|
||
|
||
|
||
t_stat
|
||
set_chan_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) {
|
||
if ((uptr->flags & CHAN_MOD) == 0)
|
||
return SCPE_ARG;
|
||
uptr->flags &= ~CHAN_MODEL;
|
||
uptr->flags |= val;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
chan_reset(DEVICE * dptr)
|
||
{
|
||
int i;
|
||
|
||
/* Clear channel assignment */
|
||
for (i = 0; i < NUM_CHAN; i++) {
|
||
chan_flags[i] = 0;
|
||
caddr[i] = 0;
|
||
cmd[i] = 0;
|
||
bcnt[i] = 0;
|
||
}
|
||
return chan_set_devs(dptr);
|
||
}
|
||
|
||
/* Map device to channel */
|
||
int
|
||
chan_mapdev(uint16 dev) {
|
||
|
||
switch((dev >> 8) & 0xff) {
|
||
case 0x02: return 1 + ((dev >> 4) & 0xf); /* Map tapes to 20-23 */
|
||
case 0x20:
|
||
if (CHAN_G_TYPE(chan_unit[1].flags) == CHAN_754)
|
||
return -1;
|
||
return 1; /* Channel 20 */
|
||
case 0x21:
|
||
if (CHAN_G_TYPE(chan_unit[2].flags) == CHAN_754)
|
||
return -1;
|
||
return 2; /* Channel 21 */
|
||
case 0x22:
|
||
if (CHAN_G_TYPE(chan_unit[3].flags) == CHAN_754)
|
||
return -1;
|
||
return 3; /* Channel 22 */
|
||
case 0x23:
|
||
if (CHAN_G_TYPE(chan_unit[4].flags) == CHAN_754)
|
||
return -1;
|
||
return 4; /* Channel 23 */
|
||
case 0x40: return 5; /* HS Channel 40 */
|
||
case 0x41: return 6; /* HS Channel 41 */
|
||
case 0x44: return 7; /* Channel 44 */
|
||
case 0x45: return 8; /* Channel 45 */
|
||
case 0x46: return 9; /* Channel 46 */
|
||
case 0x47: return 10; /* Channel 47 */
|
||
default:
|
||
if (dev > 0x2000) /* Invalid if over 2000 and not selected */
|
||
return -1;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* Boot from given device */
|
||
t_stat
|
||
chan_boot(int32 unit_num, DEVICE * dptr)
|
||
{
|
||
/* Set IAR = 1 (done by reset), channel to read one
|
||
record to location 1 */
|
||
UNIT *uptr = &dptr->units[unit_num];
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
extern t_stat cpu_reset(DEVICE *);
|
||
|
||
cpu_reset(&cpu_dev);
|
||
selreg = ((DIB *) dptr->ctxt)->addr + unit_num;
|
||
chwait = chan + 1; /* Force wait for channel */
|
||
chan_flags[chan] |= STA_ACTIVE;
|
||
chan_flags[chan] &= ~STA_PEND;
|
||
cmd[chan] = 0;
|
||
caddr[chan] = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) {
|
||
DEVICE **dptr;
|
||
DIB *dibp;
|
||
unsigned int j;
|
||
UNIT *uptr;
|
||
|
||
for (dptr = sim_devices; *dptr != NULL; dptr++) {
|
||
int r;
|
||
|
||
dibp = (DIB *) (*dptr)->ctxt;
|
||
/* If no DIB, not channel device */
|
||
if (dibp == 0)
|
||
continue;
|
||
uptr = (*dptr)->units;
|
||
/* If this is a 7907 device, check it */
|
||
if (dibp->ctype & CH_TYP_79XX) {
|
||
for (j = 0; j < (*dptr)->numunits; j++, uptr++) {
|
||
if (UNIT_G_CHAN(uptr->flags) == chan &&
|
||
(UNIT_SELECT & uptr->flags) == 0 &&
|
||
(dibp->addr & dibp->mask) == (dev & dibp->mask)) {
|
||
r = dibp->cmd(uptr, dcmd, dev);
|
||
if (r != SCPE_NODEV)
|
||
return r;
|
||
}
|
||
}
|
||
/* Handle 7621 DS units */
|
||
} else if (dibp->ctype & CH_TYP_76XX &&
|
||
(UNIT_G_CHAN(uptr->flags) == chan)) {
|
||
r = dibp->cmd(uptr, dcmd, dev);
|
||
if (r != SCPE_NODEV)
|
||
return r;
|
||
/* Handle 754 and unit record devices */
|
||
} else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) {
|
||
if (dibp->upc == 1) {
|
||
for (j = 0; j < (*dptr)->numunits; j++) {
|
||
if (UNIT_G_CHAN(uptr->flags) == chan) {
|
||
r = dibp->cmd(uptr, dcmd, dev);
|
||
if (r != SCPE_NODEV)
|
||
return r;
|
||
}
|
||
uptr++;
|
||
}
|
||
} else {
|
||
if (UNIT_G_CHAN(uptr->flags) == chan) {
|
||
r = dibp->cmd(uptr, dcmd, dev);
|
||
if (r != SCPE_NODEV)
|
||
return r;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return SCPE_NODEV;
|
||
}
|
||
|
||
/* Decrement the record count for a given channel, return 1 when
|
||
no more records to send */
|
||
int chan_decr_reccnt(int chan) {
|
||
int unit;
|
||
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d reccnt %02o %02o %02o\n",
|
||
chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]);
|
||
if (AC[unit + 1] == 10 && AC[unit + 2] == 10 && AC[unit + 3] == 10)
|
||
return 1;
|
||
if (AC[unit + 1] != 10) {
|
||
AC[unit + 1]--;
|
||
if (AC[unit + 1] == 0)
|
||
AC[unit + 1] = 10;
|
||
} else {
|
||
AC[unit + 1] = 9;
|
||
if (AC[unit + 2] != 10) {
|
||
AC[unit + 2]--;
|
||
if (AC[unit + 2] == 0)
|
||
AC[unit + 2] = 10;
|
||
} else {
|
||
AC[unit + 2] = 9;
|
||
if (AC[unit + 3] != 10) {
|
||
AC[unit + 3]--;
|
||
if (AC[unit + 3] == 0)
|
||
AC[unit + 3] = 10;
|
||
}
|
||
}
|
||
}
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d reccnt- %02o %02o %02o\n",
|
||
chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]);
|
||
if (AC[unit + 1] == 10 && AC[unit + 2] == 10 && AC[unit + 3] == 10)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
/* Return true if record count is zero */
|
||
int chan_zero_reccnt(int chan) {
|
||
int unit;
|
||
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d reccnt %02o %02o %02o\n",
|
||
chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]);
|
||
if (AC[unit + 1] == 10 && AC[unit + 2] == 10 && AC[unit + 3] == 10)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
/* Return next channel data address, advance address by 5 if channel */
|
||
uint32 chan_next_addr(int chan) {
|
||
int unit = 0;
|
||
uint32 addr = 0;
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_754:
|
||
case CHAN_UREC:
|
||
return ++caddr[chan];
|
||
|
||
case CHAN_7621:
|
||
unit = 8 + 512 + chan_unit[chan].u3 * 32;
|
||
break;
|
||
case CHAN_7908:
|
||
unit = 8 + 1024 + chan_unit[chan].u3 * 32;
|
||
break;
|
||
}
|
||
addr = load_addr(unit);
|
||
store_addr(addr + 5, unit);
|
||
return addr;
|
||
}
|
||
|
||
/* Execute the next channel instruction. */
|
||
void
|
||
chan_proc()
|
||
{
|
||
int chan;
|
||
int cmask;
|
||
int unit;
|
||
uint32 addr;
|
||
|
||
/* 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;
|
||
|
||
/* Check if RWW pending */
|
||
if (chan_flags[chan] & STA_PEND) {
|
||
/* If pending issue read command */
|
||
chan_flags[chan] &= ~STA_PEND;
|
||
if (selreg2 & 0x8000) {
|
||
int chan2;
|
||
/* Find device on given channel and give it the command */
|
||
chan2 = chan_mapdev(selreg2 & 0x7fff);
|
||
if (chan2 < 0 || chan2 >= NUM_CHAN)
|
||
continue;
|
||
/* If no channel device, quick exit */
|
||
if (chan_unit[chan2].flags & UNIT_DIS ||
|
||
CHAN_G_TYPE(chan_unit[chan2].flags) != CHAN_754) {
|
||
flags |= 0x440; /* Set I/O Check */
|
||
selreg2 = 0;
|
||
continue;
|
||
}
|
||
|
||
/* Channel is busy doing something, wait */
|
||
if (chan_flags[chan2] & (DEV_SEL| DEV_DISCO|STA_TWAIT|STA_WAIT|
|
||
STA_ACTIVE)) {
|
||
chan_flags[chan] |= STA_PEND;
|
||
/* Try again */
|
||
continue;
|
||
}
|
||
|
||
/* Issue another command */
|
||
switch(chan_issue_cmd(chan2, IO_RDS, selreg2 & 0x7fff)) {
|
||
case SCPE_BUSY: /* Try again */
|
||
chan_flags[chan] |= STA_PEND;
|
||
break;
|
||
case SCPE_NODEV:
|
||
case SCPE_IOERR:
|
||
/* Something wrong, stop */
|
||
flags |= 0x440; /* Set I/O Check */
|
||
selreg2 = 0;
|
||
break;
|
||
case SCPE_OK:
|
||
chan_flags[chan2] |= STA_ACTIVE;
|
||
selreg2 &= 0x7fff;
|
||
chwait = chan2+1; /* Change wait channel */
|
||
break;
|
||
}
|
||
} else {
|
||
/* No pending, just store last address in MAC2 */
|
||
MAC2 = caddr[chan];
|
||
selreg2 = 0;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
/* If channel not active, don't process anything */
|
||
if ((chan_flags[chan] & STA_ACTIVE) == 0)
|
||
continue;
|
||
|
||
if ((chan_flags[chan] & (CTL_READ|CTL_WRITE)) &&
|
||
(chan_flags[chan] & (CTL_END|SNS_UEND))) {
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= DEV_WEOR|DEV_DISCO;
|
||
chan_flags[chan] &= ~(SNS_UEND|CTL_END|CTL_READ|CTL_WRITE);
|
||
}
|
||
|
||
/* 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 %d\n",
|
||
chan, irqdev[chan]);
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_UREC:
|
||
case CHAN_754:
|
||
if (selreg2 != 0)
|
||
chan_flags[chan] |= STA_PEND;
|
||
if (chan_flags[chan] & CHS_ERR)
|
||
flags |= 0x40; /* Set I/O Check */
|
||
break;
|
||
case CHAN_7621:
|
||
case CHAN_7908:
|
||
irqflags |= 1 << chan;
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "chan %d IRQ %x\n",
|
||
chan, irqdev[chan]);
|
||
break;
|
||
}
|
||
chan_flags[chan] &= ~(CHS_ATTN|STA_ACTIVE|STA_WAIT|DEV_WRITE);
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
unit = irqdev[chan];
|
||
if (chan_flags[chan] & CHS_EOF)
|
||
ioflags[unit/8] |= (1 << (unit & 07));
|
||
flags |= 0x400; /* Set Any flag */
|
||
/* Disconnect if selected */
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= (DEV_DISCO);
|
||
continue;
|
||
}
|
||
|
||
/* If channel action all done, finish operation */
|
||
if (((chan_flags[chan] & (DEV_SEL|STA_ACTIVE|STA_WAIT)) == STA_ACTIVE)
|
||
&& (chan_flags[chan] & (CTL_CNTL|CTL_PREAD|CTL_PWRITE|CTL_READ|
|
||
CTL_WRITE|CTL_SNS)) == 0) {
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_UREC:
|
||
case CHAN_754:
|
||
if (selreg2 != 0)
|
||
chan_flags[chan] |= STA_PEND;
|
||
break;
|
||
case CHAN_7621:
|
||
case CHAN_7908:
|
||
irqflags |= 1 << chan;
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "chan %d IRQ %x\n",
|
||
chan, irqdev[chan]);
|
||
break;
|
||
}
|
||
chan_flags[chan] &= ~(STA_ACTIVE|DEV_WRITE);
|
||
if (chan_flags[chan] & CHS_EOF) {
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOF %x\n",
|
||
chan, irqdev[chan]);
|
||
unit = irqdev[chan];
|
||
ioflags[unit/8] |= (1 << (unit & 07));
|
||
chan_flags[chan] &= ~CHS_EOF;
|
||
chan_flags[chan] |= CHS_ERR;
|
||
flags |= 0x400; /* Set Any flag */
|
||
}
|
||
continue;
|
||
}
|
||
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_UREC:
|
||
case CHAN_754:
|
||
/* If device put up EOR, terminate transfer. */
|
||
if (chan_flags[chan] & (DEV_REOR|DEV_WEOR)) {
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR\n",
|
||
chan);
|
||
if (selreg2 != 0)
|
||
chan_flags[chan] |= STA_PEND;
|
||
chan_flags[chan] &= ~(STA_ACTIVE|STA_WAIT|DEV_WRITE|DEV_REOR);
|
||
/* Disconnect if selected */
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= (DEV_DISCO);
|
||
continue;
|
||
}
|
||
break;
|
||
|
||
case CHAN_7621:
|
||
/* Waiting on unit ready, or command */
|
||
if (chan_flags[chan] & STA_WAIT) {
|
||
if ((chan_flags[chan] & STA_TWAIT) == 0) {
|
||
/* Device ready, see if command under record count */
|
||
if (cmd[chan] & CHAN_CMD) {
|
||
/* Done if EOF set */
|
||
if (chan_flags[chan] & CHS_EOF) {
|
||
cmd[chan] &= ~(CHAN_RECCNT|CHAN_CMD);
|
||
chan_flags[chan] &= ~(STA_WAIT);
|
||
continue; /* On to next channel */
|
||
}
|
||
|
||
/* Issue another command */
|
||
switch(chan_issue_cmd(chan, 0xff & (cmd[chan] >> 9),
|
||
irqdev[chan])) {
|
||
case SCPE_BUSY:
|
||
continue;
|
||
case SCPE_OK:
|
||
/* If started, drop record count */
|
||
if (chan_decr_reccnt(chan)){
|
||
cmd[chan] &= ~(CHAN_RECCNT|CHAN_CMD);
|
||
chan_flags[chan] &= ~(STA_WAIT);
|
||
continue; /* On to next channel */
|
||
}
|
||
continue;
|
||
case SCPE_NODEV:
|
||
case SCPE_IOERR:
|
||
/* Something wrong, stop */
|
||
cmd[chan] &= ~(CHAN_RECCNT|CHAN_CMD);
|
||
break;
|
||
}
|
||
continue; /* On to next channel */
|
||
}
|
||
chan_flags[chan] &= ~(STA_WAIT);
|
||
}
|
||
continue; /* On to next channel */
|
||
}
|
||
|
||
/* If device put up EOR, terminate transfer. */
|
||
if (chan_flags[chan] & DEV_REOR) {
|
||
int ch;
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR\n",
|
||
chan);
|
||
/* If reading, check if partial word read */
|
||
if ((chan_flags[chan] & DEV_WRITE) == 0) {
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
unit += (cmd[chan] & CHAN_BFLAG)? 16: 24;
|
||
ch = AC[unit + 5];
|
||
if (ch != 10) {
|
||
/* Yes, fill with group marks and mark as full */
|
||
while(ch < 5)
|
||
AC[unit+ch++] = CHR_GM;
|
||
cmd[chan] |= (cmd[chan] & CHAN_BFLAG)?
|
||
CHAN_BFULL: CHAN_AFULL;
|
||
}
|
||
}
|
||
if (cmd[chan] & CHAN_RECCNT) {
|
||
if (!chan_decr_reccnt(chan)) {
|
||
chan_flags[chan] &= ~DEV_REOR;
|
||
continue;
|
||
}
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
}
|
||
chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR);
|
||
/* Disconnect if selected */
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= (DEV_DISCO);
|
||
}
|
||
|
||
/* Channel gave us a Write EOR, terminate it needed. */
|
||
if (chan_flags[chan] & DEV_WEOR) {
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "chan %d WEOR\n",
|
||
chan);
|
||
if (cmd[chan] & CHAN_RECCNT) {
|
||
if (!chan_decr_reccnt(chan)) {
|
||
chan_flags[chan] &= ~DEV_WEOR;
|
||
cmd[chan] &= ~CHAN_END;
|
||
continue;
|
||
}
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
}
|
||
chan_flags[chan] &= ~(DEV_WEOR|DEV_REOR);
|
||
/* Disconnect if selected */
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= (DEV_DISCO);
|
||
}
|
||
|
||
if ((chan_flags[chan] & DEV_WRITE) &&
|
||
(cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) !=
|
||
(CHAN_AFULL|CHAN_BFULL)) {
|
||
char ch;
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
if (cmd[chan] & CHAN_END)
|
||
break;
|
||
addr = chan_next_addr(chan);
|
||
if ((cmd[chan] & CHAN_AFULL) == 0) {
|
||
unit += 24;
|
||
cmd[chan] |= CHAN_AFULL;
|
||
ch = 'a';
|
||
} else if ((cmd[chan] & CHAN_BFULL) == 0) {
|
||
unit += 16;
|
||
cmd[chan] |= CHAN_BFULL;
|
||
ch = 'b';
|
||
} else {
|
||
break;
|
||
}
|
||
AC[unit] = M[addr++];
|
||
AC[unit+1] = M[addr++];
|
||
AC[unit+2] = M[addr++];
|
||
AC[unit+3] = M[addr++];
|
||
AC[unit+4] = M[addr++];
|
||
AC[unit+5] = 10;
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_DATA, &chan_dev,
|
||
"chan %d (%d) > %c %02o%02o%02o%02o%02o\n",
|
||
chan, addr-5, ch,
|
||
AC[unit], AC[unit+1], AC[unit+2], AC[unit+3],
|
||
AC[unit+4]);
|
||
|
||
if (cmd[chan] & CHAN_NOREC && (addr % 20000) == 0)
|
||
cmd[chan] |= CHAN_END;
|
||
/* Wrap around if over end of memory, set error */
|
||
if (addr > EMEMSIZE) {
|
||
chan_flags[chan] |= CHS_ERR;
|
||
/* addr -= EMEMSIZE; */
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "write wrap %d\n", chan);
|
||
}
|
||
break;
|
||
}
|
||
while ((chan_flags[chan] & DEV_WRITE) == 0&&
|
||
(cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) != 0) {
|
||
char ch;
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
addr = chan_next_addr(chan);
|
||
if (cmd[chan] & CHAN_AFULL) {
|
||
unit += 24;
|
||
cmd[chan] &= ~CHAN_AFULL;
|
||
ch = 'a';
|
||
} else if (cmd[chan] & CHAN_BFULL) {
|
||
unit += 16;
|
||
cmd[chan] &= ~CHAN_BFULL;
|
||
ch = 'b';
|
||
} else {
|
||
break;
|
||
}
|
||
if ((cmd[chan]& CHAN_SKIP) == 0) {
|
||
M[addr++] = AC[unit];
|
||
M[addr++] = AC[unit+1];
|
||
M[addr++] = AC[unit+2];
|
||
M[addr++] = AC[unit+3];
|
||
M[addr++] = AC[unit+4];
|
||
if (addr > EMEMSIZE) {
|
||
cmd[chan] |= CHAN_SKIP;
|
||
chan_flags[chan] |= CHS_ERR;
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_EXP, &chan_dev, "read wrap %d\n", chan);
|
||
}
|
||
if (chan_dev.dctrl & cmask)
|
||
sim_debug(DEBUG_DATA, &chan_dev,
|
||
"chan %d (%d) < %c %02o%02o%02o%02o%02o\n",
|
||
chan, addr-5, ch,
|
||
AC[unit], AC[unit+1], AC[unit+2], AC[unit+3],
|
||
AC[unit+4]);
|
||
}
|
||
AC[unit+5] = 10;
|
||
}
|
||
break;
|
||
case CHAN_7908:
|
||
switch(chan_flags[chan] & (DEV_WRITE|DEV_FULL)) {
|
||
case 0:
|
||
case DEV_WRITE|DEV_FULL:
|
||
continue;
|
||
case DEV_WRITE:
|
||
if (cmd[chan] & CHAN_END)
|
||
break;
|
||
unit = 1024 + chan_unit[chan].u3 * 32;
|
||
addr = chan_next_addr(chan);
|
||
assembly[chan] = M[addr++] & 077;
|
||
assembly[chan] |= (M[addr++] & 077) << 6;
|
||
assembly[chan] |= (M[addr++] & 077) << 12;
|
||
assembly[chan] |= (M[addr++] & 077) << 18;
|
||
assembly[chan] |= (M[addr++] & 077) << 24;
|
||
if (cmd[chan] & CHAN_NOREC && (addr % 20000) == 19999)
|
||
cmd[chan] |= CHAN_END;
|
||
bcnt[chan] = 0;
|
||
chan_flags[chan] |= DEV_FULL;
|
||
break;
|
||
case DEV_FULL:
|
||
unit = 1024 + chan_unit[chan].u3 * 32;
|
||
addr = chan_next_addr(chan);
|
||
if ((cmd[chan]& CHAN_SKIP) == 0) {
|
||
M[addr++] = assembly[chan] & 077;
|
||
M[addr++] = (assembly[chan] >> 6) & 077;
|
||
M[addr++] = (assembly[chan] >> 12) & 077;
|
||
M[addr++] = (assembly[chan] >> 18) & 077;
|
||
M[addr++] = (assembly[chan] >> 24) & 077;
|
||
}
|
||
if (addr > EMEMSIZE) {
|
||
cmd[chan] |= CHAN_SKIP;
|
||
chan_flags[chan] |= CHS_ATTN;
|
||
}
|
||
bcnt[chan] = 0;
|
||
chan_flags[chan] &= ~DEV_FULL;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void chan_set_attn_inq(int chan) {
|
||
/* inquiry = 1; */
|
||
}
|
||
|
||
void chan_clear_attn_inq(int chan) {
|
||
/* inquiry = 0; */
|
||
}
|
||
|
||
|
||
/* Issue a command to a channel */
|
||
int
|
||
chan_cmd(uint16 dev, uint16 dcmd, uint32 addr)
|
||
{
|
||
int chan;
|
||
int unit;
|
||
t_stat r;
|
||
int op;
|
||
|
||
/* Find device on given channel and give it the command */
|
||
chan = chan_mapdev(dev);
|
||
if (chan < 0 || chan >= NUM_CHAN)
|
||
return SCPE_IOERR;
|
||
/* 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 */
|
||
caddr[chan] = addr;
|
||
assembly[chan] = 0;
|
||
op = dcmd >> 8;
|
||
cmd[chan] &= CHAN_RECCNT; /* Clear everything but record count flag */
|
||
cmd[chan] |= dcmd & CHAN_ZERO;
|
||
if (op == IO_RDS && (dcmd & 0xf) != 0) {
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_754:
|
||
case CHAN_UREC:
|
||
cmd[chan] |= CHAN_SKIP;
|
||
break;
|
||
case CHAN_7621:
|
||
switch(dcmd & 0xf) {
|
||
case 1: cmd[chan] |= CHAN_SKIP; break;
|
||
case 2:
|
||
unit = 8+512 + chan_unit[chan].u3 * 32;
|
||
M[addr++] = AC[unit++];
|
||
M[addr++] = AC[unit++];
|
||
M[addr++] = AC[unit++];
|
||
M[addr++] = AC[unit++];
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
return SCPE_OK;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
case CHAN_7908:
|
||
switch(dcmd & 0xf) {
|
||
case 1: cmd[chan] |= CHAN_SKIP;
|
||
case 0: chan_flags[chan] |= CTL_READ; break;
|
||
case 3: chan_flags[chan] |= CTL_SNS; break; /* Do Sense */
|
||
case 4: /* Do control then read */
|
||
chan_flags[chan] |= CTL_CNTL|CTL_PREAD;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (op == IO_WRS && (dcmd & 0xf) != 0) {
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_754:
|
||
case CHAN_UREC:
|
||
cmd[chan] |= CHAN_NOREC;
|
||
break;
|
||
case CHAN_7621:
|
||
dcmd &= ~ CHAN_ZERO;
|
||
switch(dcmd & 0xf) {
|
||
case 1: cmd[chan] |= CHAN_NOREC; break;
|
||
case 2:
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
addr /= 10;
|
||
AC[unit+1] = addr % 10;
|
||
if (AC[unit+1] == 0)
|
||
AC[unit+1] = 10;
|
||
addr /= 10;
|
||
AC[unit+2] = addr % 10;
|
||
addr /= 10;
|
||
if (AC[unit+2] == 0)
|
||
AC[unit+2] = 10;
|
||
AC[unit+3] = addr % 10;
|
||
if (AC[unit+3] == 0)
|
||
AC[unit+3] = 10;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DETAIL, &chan_dev,
|
||
"chan %d set reccnt %02o %02o %02o\n",
|
||
chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]);
|
||
cmd[chan] |= CHAN_RECCNT;
|
||
return SCPE_OK;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
case CHAN_7908:
|
||
switch(dcmd & 0xf) {
|
||
case 1: cmd[chan] |= CHAN_NOREC;
|
||
case 0: chan_flags[chan] |= CTL_WRITE; break;
|
||
case 3: /* Do control */
|
||
chan_flags[chan] |= CTL_CNTL; break;
|
||
case 4: /* Do control then write */
|
||
chan_flags[chan] |= CTL_CNTL|CTL_PWRITE; break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
/* Handle initial record count of zero for special ops */
|
||
if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7621 &&
|
||
cmd[chan] & CHAN_RECCNT) {
|
||
switch(op) {
|
||
case IO_WEF:
|
||
case IO_ERG:
|
||
case IO_BSR:
|
||
if (chan_zero_reccnt(chan)) {
|
||
/* Just check if unit ready */
|
||
r = chan_issue_cmd(chan, OP_TRS, dev);
|
||
if (r == SCPE_OK)
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
return r;
|
||
}
|
||
break;
|
||
default: /* Don't worry about it, not effected by RC */
|
||
break;
|
||
}
|
||
}
|
||
|
||
chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE|SNS_UEND|CTL_WRITE|
|
||
CTL_SNS|CHS_ATTN);
|
||
|
||
r = chan_issue_cmd(chan, op, dev);
|
||
if (r == SCPE_OK)
|
||
chan_flags[chan] &= ~(CHS_EOF|CHS_ERR|CHS_ATTN);
|
||
/* Activate channel if select raised */
|
||
if (r == SCPE_OK && chan_flags[chan] & DEV_SEL) {
|
||
chan_flags[chan] |= STA_ACTIVE;
|
||
irqdev[chan] = dev;
|
||
irqflags &= ~(1 << chan);
|
||
ioflags[dev/8] &= ~(1 << (dev & 07));
|
||
/* Set starting address. */
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_754:
|
||
case CHAN_UREC:
|
||
if (op == IO_RDS) {
|
||
if (selreg2 & 0x8000)
|
||
caddr[chan] = MAC2;
|
||
selreg2 &= 0x7fff;
|
||
}
|
||
chwait = chan+1;
|
||
break;
|
||
case CHAN_7621:
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
AC[unit+16+5] = 10; /* Set digit next to 0 */
|
||
AC[unit+24+5] = 10;
|
||
store_addr(caddr[chan], 8 + unit);
|
||
if (cmd[chan] & CHAN_RECCNT && chan_zero_reccnt(chan)) {
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
}
|
||
break;
|
||
case CHAN_7908:
|
||
store_addr(caddr[chan], 8+1024 + chan_unit[chan].u3 * 32);
|
||
break;
|
||
}
|
||
}
|
||
if (r == SCPE_OK && CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7621) {
|
||
switch(op) {
|
||
case IO_WEF:
|
||
case IO_ERG:
|
||
case IO_BSR:
|
||
if (cmd[chan] & CHAN_RECCNT && chan_zero_reccnt(chan)) {
|
||
cmd[chan] &= ~CHAN_RECCNT;
|
||
}
|
||
if (cmd[chan] & CHAN_RECCNT) {
|
||
chan_decr_reccnt(chan);
|
||
cmd[chan] &= CHAN_RECCNT;
|
||
cmd[chan] |= (op << 9) | CHAN_CMD;
|
||
}
|
||
/* Fall through */
|
||
|
||
case IO_SKR:
|
||
case IO_BSF:
|
||
case IO_REW:
|
||
case IO_RUN:
|
||
chan_flags[chan] |= STA_ACTIVE|STA_WAIT;
|
||
irqdev[chan] = dev;
|
||
irqflags &= ~(1 << chan);
|
||
ioflags[dev/8] &= ~(1 << (dev & 07));
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/*
|
||
* Process the CHR 3 13 command and abort all channel activity
|
||
*/
|
||
void
|
||
chan_chr_13()
|
||
{
|
||
int chan;
|
||
|
||
/* 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;
|
||
|
||
/* If channel not active, don't process anything */
|
||
if ((chan_flags[chan] & STA_ACTIVE) == 0)
|
||
continue;
|
||
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= DEV_WEOR|DEV_DISCO;
|
||
|
||
chan_flags[chan] &= ~(CHS_ATTN|STA_ACTIVE|STA_WAIT);
|
||
}
|
||
|
||
irqflags = 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* 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;
|
||
int unit;
|
||
uint16 msk;
|
||
|
||
/* Based on channel type get next character */
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_754:
|
||
case CHAN_UREC:
|
||
if (*data == 0)
|
||
*data = 020;
|
||
if (caddr[chan] > EMEMSIZE) {
|
||
cmd[chan] |= CHAN_SKIP;
|
||
chan_flags[chan] |= CHS_ATTN;
|
||
}
|
||
if ((cmd[chan] & CHAN_SKIP) == 0)
|
||
M[caddr[chan]] = *data;
|
||
caddr[chan]++;
|
||
break;
|
||
case CHAN_7621:
|
||
if (*data == 0)
|
||
*data = 020;
|
||
/* If no data, then return timing error */
|
||
if ((cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) == (CHAN_AFULL|CHAN_BFULL))
|
||
return TIME_ERROR;
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
if ((cmd[chan] & CHAN_BFLAG) == 0 && (cmd[chan] & CHAN_AFULL) == 0) {
|
||
unit += 24;
|
||
msk = CHAN_AFULL;
|
||
} else if ((cmd[chan] & CHAN_BFLAG) && (cmd[chan] & CHAN_BFULL) == 0) {
|
||
unit += 16;
|
||
msk = CHAN_BFULL;
|
||
} else { /* We are off sync, or something */
|
||
/* We have data, so switch CHAN_BFLAG and try other buffer */
|
||
cmd[chan] ^= CHAN_BFLAG;
|
||
unit += (cmd[chan] & CHAN_BFLAG)? 16: 24;
|
||
msk = (cmd[chan] & CHAN_BFLAG)? CHAN_BFULL: CHAN_AFULL;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DETAIL, &chan_dev,
|
||
"switching buffer %d\n", chan);
|
||
}
|
||
ch = AC[5 + unit];
|
||
if (ch == 10)
|
||
ch = 0;
|
||
AC[unit + ch] = *data & 077;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DATA, &chan_dev,
|
||
"%d < %02o (%d)\n", chan, *data, ch);
|
||
AC[5 + unit] = ch + 1;
|
||
if (ch == 4) {
|
||
cmd[chan] |= msk;
|
||
cmd[chan] ^= CHAN_BFLAG;
|
||
}
|
||
break;
|
||
case CHAN_7908:
|
||
if (bcnt[chan] > 4)
|
||
return TIME_ERROR;
|
||
if (chan_flags[chan] & CTL_SNS) {
|
||
*data &= 027;
|
||
*data |= 040;
|
||
} else {
|
||
if (*data == 0)
|
||
*data = 020;
|
||
}
|
||
assembly[chan] |= *data << (6 * bcnt[chan]);
|
||
bcnt[chan]++;
|
||
if (bcnt[chan] == 5)
|
||
chan_flags[chan] |= DEV_FULL;
|
||
}
|
||
|
||
|
||
/* If device gave us an end, terminate transfer */
|
||
if (flags & DEV_REOR) {
|
||
chan_flags[chan] |= DEV_REOR|DEV_FULL;
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= DEV_DISCO;
|
||
return END_RECORD;
|
||
/* If over size of memory, terminate */
|
||
} else if (!MEM_ADDR_OK(caddr[chan])) {
|
||
chan_flags[chan] |= DEV_REOR|DEV_FULL;
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= DEV_DISCO;
|
||
return END_RECORD;
|
||
}
|
||
|
||
return DATA_OK;
|
||
}
|
||
|
||
/*
|
||
* Read next char from assembly register.
|
||
*/
|
||
int
|
||
chan_read_char(int chan, uint8 * data, int flags)
|
||
{
|
||
uint8 ch;
|
||
int unit;
|
||
uint16 msk;
|
||
|
||
/* Check if he write out last data */
|
||
if ((chan_flags[chan] & STA_ACTIVE) == 0)
|
||
return TIME_ERROR;
|
||
|
||
/* Based on channel type get next character */
|
||
switch(CHAN_G_TYPE(chan_unit[chan].flags)) {
|
||
case CHAN_754:
|
||
case CHAN_UREC:
|
||
*data = M[caddr[chan]];
|
||
if (*data == CHR_BLANK)
|
||
*data = CHR_ABLANK;
|
||
if (cmd[chan] & CHAN_ZERO && *data != CHR_GM)
|
||
M[caddr[chan]] = CHR_BLANK;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DATA, &chan_dev,
|
||
"%d > %02o (%d)\n", chan, *data, *data);
|
||
caddr[chan]++;
|
||
if ((cmd[chan] & CHAN_NOREC && (caddr[chan] % 19999) == 0)) {
|
||
chan_flags[chan] |= DEV_WEOR;
|
||
return END_RECORD;
|
||
}
|
||
break;
|
||
case CHAN_7621:
|
||
/* If no data, then return timing error */
|
||
if ((cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) == 0) {
|
||
if (cmd[chan] & CHAN_END) {
|
||
chan_flags[chan] |= DEV_WEOR;
|
||
return END_RECORD;
|
||
}
|
||
return TIME_ERROR;
|
||
}
|
||
unit = 512 + chan_unit[chan].u3 * 32;
|
||
if ((cmd[chan] & CHAN_BFLAG) == 0 && (cmd[chan] & CHAN_AFULL)) {
|
||
unit += 24;
|
||
msk = ~CHAN_AFULL;
|
||
} else if ((cmd[chan] & CHAN_BFLAG) && (cmd[chan] & CHAN_BFULL)) {
|
||
unit += 16;
|
||
msk = ~CHAN_BFULL;
|
||
} else { /* We are off sync, or something */
|
||
/* We have data, so switch CHAN_BFLAG and try other buffer */
|
||
cmd[chan] ^= CHAN_BFLAG;
|
||
unit += (cmd[chan] & CHAN_BFLAG)? 16: 24;
|
||
msk = (cmd[chan] & CHAN_BFLAG)? ~CHAN_BFULL: ~CHAN_AFULL;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DETAIL, &chan_dev,
|
||
"switching buffer %d\n", chan);
|
||
}
|
||
ch = AC[5 + unit];
|
||
if (ch == 10)
|
||
ch = 0;
|
||
*data = AC[unit + ch] & 077;
|
||
if (*data == CHR_BLANK)
|
||
*data = CHR_ABLANK;
|
||
if (chan_dev.dctrl & (0x0100 << chan))
|
||
sim_debug(DEBUG_DATA, &chan_dev,
|
||
"%d > %02o (%d)\n", chan, *data, ch);
|
||
AC[5 + unit] = ch + 1;
|
||
if (ch == 4) {
|
||
cmd[chan] &= msk;
|
||
cmd[chan] ^= CHAN_BFLAG;
|
||
}
|
||
break;
|
||
case CHAN_7908:
|
||
if (bcnt[chan] > 4)
|
||
return TIME_ERROR;
|
||
*data = assembly[chan] >> (6 * bcnt[chan]);
|
||
if (*data == CHR_BLANK)
|
||
*data = CHR_ABLANK;
|
||
if (chan_flags[chan] & CTL_CNTL && *data == CHR_GM) {
|
||
chan_flags[chan] |= (chan_flags[chan] & (CTL_PREAD|CTL_PWRITE)) << 2;
|
||
chan_flags[chan] &= ~(CTL_CNTL|CTL_PREAD|CTL_PWRITE);
|
||
if (chan_flags[chan] & CTL_READ) {
|
||
chan_next_addr(chan);
|
||
chan_next_addr(chan);
|
||
}
|
||
bcnt[chan] = 0;
|
||
return END_RECORD;
|
||
}
|
||
|
||
bcnt[chan]++;
|
||
if (bcnt[chan] == 5)
|
||
chan_flags[chan] &= ~DEV_FULL;
|
||
}
|
||
|
||
/* Check if we hit group mark */
|
||
if ((cmd[chan] & CHAN_NOREC) == 0 && *data == CHR_GM) {
|
||
chan_flags[chan] |= DEV_WEOR;
|
||
return END_RECORD;
|
||
}
|
||
|
||
/* If end of record, don't transfer any data */
|
||
if (flags & DEV_REOR) {
|
||
chan_flags[chan] &= ~(DEV_WRITE/*|STA_ACTIVE*/);
|
||
if (chan_flags[chan] & DEV_SEL)
|
||
chan_flags[chan] |= DEV_DISCO;
|
||
chan_flags[chan] |= DEV_REOR;
|
||
return TIME_ERROR;
|
||
} else
|
||
chan_flags[chan] |= DEV_WRITE;
|
||
return DATA_OK;
|
||
}
|
||
|
||
|
||
void
|
||
chan9_set_error(int chan, uint32 mask)
|
||
{
|
||
if (chan_flags[chan] & mask)
|
||
return;
|
||
chan_flags[chan] |= mask;
|
||
}
|
||
|
||
t_stat
|
||
chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf(st, "%s\n", chan_description(dptr));
|
||
fprintf(st, "The 7080 supports up to 10 channels. Channel 0 is for unit\n");
|
||
fprintf(st, "record devices. Channels 1 through 4 are for tape drives.\n\n");
|
||
fprintf (st, " 7261 tapes on Data Synchronizer\n");
|
||
fprintf (st, " 754 Standard 705 tape drives\n\n");
|
||
fprintf (st, "Channels are fixed on the 7080.\n\n");
|
||
fprintf (st, "Channel * is a puesdo channel for unit record devices.\n");
|
||
|
||
fprintf(st, "\n");
|
||
fprint_set_help(st, dptr);
|
||
fprint_show_help(st, dptr);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *
|
||
chan_description(DEVICE *dptr)
|
||
{
|
||
return "IBM 7080 channel controller";
|
||
}
|
||
|
||
|