/* i7010_chan.c: IBM 7010 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 7010 channel is: | |
There is only one type of channel on 7010, and it will talk to | |
All type devices. | |
Common registers to all but PIO channels. | |
ADDR<0:16> Address of next command. | |
CMD<0:6> Channel command. | |
ASM<0:32> Assembled data from devices. | |
Simulation registers to handle device handshake. | |
STATUS<0:16> Simulated register for basic channel status. | |
SENSE<0:16> Additional flags for 7907 channels. | |
*/ | |
#include "i7010_defs.h" | |
extern UNIT cpu_unit; | |
extern uint8 chan_seek_done[NUM_CHAN]; /* Channel seek finished */ | |
#define CHAN_DEF UNIT_DISABLE|CHAN_SET | |
t_stat set_urec(UNIT * uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat get_urec(FILE * st, UNIT * uptr, int32 v, CONST 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 */ | |
uint8 cmd[NUM_CHAN]; /* Current command */ | |
uint16 irqdev[NUM_CHAN]; /* Device to generate interupts | |
for channel */ | |
uint32 chunit[NUM_CHAN]; /* Channel unit */ | |
uint32 assembly[NUM_CHAN]; /* Assembly register */ | |
uint32 chan_flags[NUM_CHAN]; /* Unit status */ | |
extern uint8 chan_io_status[NUM_CHAN]; | |
extern uint8 inquiry; | |
extern uint8 urec_irq[NUM_CHAN]; | |
#define CHAN_LOAD 0001 /* Channel in load mode */ | |
#define CHAN_NOREC 0002 /* Don't stop at record */ | |
#define CHAN_WM 0004 /* Sent word mark char */ | |
#define CHAN_6BIT 0010 /* Send 6-8 bit command */ | |
#define CHAN_DSK_SEEK 0020 /* Seek Command */ | |
#define CHAN_DSK_DATA 0040 /* Command needs data */ | |
#define CHAN_DSK_RD 0100 /* Command is read command */ | |
#define CHAN_OVLP 0200 /* Channel ran overlaped */ | |
const char *chan_type_name[] = { | |
"Polled", "Unit Record", "7010", "7010", "7010"}; | |
/* Map commands to channel commands */ | |
/* Commands are reversed to be way they are sent out */ | |
uint8 disk_cmdmap[16] = { 0xff, 0x82, 0x84, 0x86, 0x00, 0x89, 0x88, 0x83, | |
0x87, 0x04, 0x80, 0xff, 0x85, 0xff, 0xff, 0xff}; | |
UNIT chan_unit[] = { | |
{UDATA(NULL, CHAN_SET|UNIT_DIS, 0)}, /* Place holder channel */ | |
{UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(1),0)}, | |
{UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(2),0)}, | |
{UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(3),0)}, | |
{UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(4),0)}, | |
}; | |
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(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT}, | |
{NULL} | |
}; | |
MTAB chan_mod[] = { | |
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7010), "7010", NULL, NULL,NULL,NULL}, | |
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "UREC", "UREC", &set_urec, &get_urec, | |
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}, | |
{"CH1", 0x0100 << 1}, | |
{"CH2", 0x0100 << 2}, | |
{"CH3", 0x0100 << 3}, | |
{"CH4", 0x0100 << 4}, | |
{0, 0} | |
}; | |
DEVICE chan_dev = { | |
"CH", chan_unit, chan_reg, chan_mod, | |
NUM_CHAN, 10, 18, 1, 8, 8, | |
NULL, NULL, &chan_reset, NULL, NULL, NULL, | |
NULL, DEV_DEBUG, 0, chn_debug, | |
NULL, NULL, &chan_help, NULL, NULL, &chan_description | |
}; | |
struct urec_t { | |
uint16 addr; | |
const char *name; | |
} urec_devs[] = { | |
{0100, "CR"}, | |
{0200, "LP"}, | |
{0400, "CP"}, | |
{0000, "NONE"}, | |
{0000, NULL} | |
}; | |
/* Sets the device that will interrupt on the channel. */ | |
t_stat | |
set_urec(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int chan; | |
int i; | |
if (cptr == NULL) | |
return SCPE_IERR; | |
if (uptr == NULL) | |
return SCPE_IERR; | |
chan = UNIT_G_CHAN(uptr->flags); | |
for(i = 0; urec_devs[i].name != NULL; i++) | |
if (strcmp(cptr, urec_devs[i].name) == 0) | |
break; | |
if (urec_devs[i].name == NULL) | |
return SCPE_ARG; | |
irqdev[chan] = urec_devs[i].addr; | |
return SCPE_OK; | |
} | |
t_stat | |
get_urec(FILE * st, UNIT * uptr, int32 v, CONST void *desc) | |
{ | |
int chan; | |
int i; | |
if (uptr == NULL) | |
return SCPE_IERR; | |
chan = UNIT_G_CHAN(uptr->flags); | |
if (irqdev[chan] == 0) { | |
fprintf(st, "UREC=NONE"); | |
return SCPE_OK; | |
} | |
for(i = 0; urec_devs[i].name != NULL; i++) { | |
if (urec_devs[i].addr == irqdev[chan]) { | |
fprintf(st, "UREC=%s", urec_devs[i].name); | |
return SCPE_OK; | |
} | |
} | |
fprintf(st, "UREC=%o", irqdev[chan]); | |
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; | |
chunit[i] = 0; | |
caddr[i] = 0; | |
cmd[i] = 0; | |
bcnt[i] = 0; | |
} | |
return chan_set_devs(dptr); | |
} | |
/* Channel selector characters */ | |
uint8 chan_char[NUM_CHAN] = {0, CHR_RPARN, CHR_LPARN, CHR_QUEST, CHR_EXPL}; | |
/* 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 int chwait; | |
chwait = chan; /* Force wait for channel */ | |
/* Set up channel to load into location 1 */ | |
caddr[chan] = 1; | |
assembly[chan] = 0; | |
cmd[chan] = CHAN_NOREC|CHAN_LOAD; | |
chunit[chan] = unit_num; | |
chan_flags[chan] |= STA_ACTIVE; | |
return SCPE_OK; | |
} | |
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 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; | |
} | |
} | |
} 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; | |
} | |
/* 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; | |
/* If channel is disconnecting, do nothing */ | |
if (chan_flags[chan] & DEV_DISCO) | |
continue; | |
if (chan_flags[chan] & CHS_EOF) { | |
chan_io_status[chan] |= IO_CHS_COND; | |
chan_flags[chan] &= ~CHS_EOF; | |
} | |
if (chan_flags[chan] & CHS_ERR) { | |
chan_io_status[chan] |= IO_CHS_CHECK; | |
chan_flags[chan] &= ~CHS_ERR; | |
} | |
if (cmd[chan] & CHAN_DSK_DATA) { | |
if (chan_flags[chan] & DEV_REOR) { | |
/* Find end of command */ | |
while(MEM_ADDR_OK(caddr[chan]) && M[caddr[chan]] != (WM|077)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_CHAN, &chan_dev, "%02o,", M[caddr[chan]]); | |
caddr[chan]++; | |
} | |
caddr[chan]++; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_CHAN, &chan_dev, "chan %d fin\n", chan); | |
/* Configure channel for data transfer */ | |
cmd[chan] &= ~CHAN_DSK_DATA; | |
chan_flags[chan] |= (chan_flags[chan]& | |
(CTL_PREAD|CTL_PWRITE))>>2; | |
chan_flags[chan] &= ~(DEV_REOR|CTL_PREAD|CTL_PWRITE|CTL_CNTL); | |
/* If no select, all done */ | |
if ((chan_flags[chan] & DEV_SEL) == 0) | |
chan_flags[chan] &= ~(CTL_READ|CTL_WRITE); | |
/* Set direction if reading */ | |
if (chan_flags[chan] & CTL_READ) | |
chan_flags[chan] |= DEV_WRITE; | |
/* Check if we should finish now */ | |
if ((chan_flags[chan] & (CTL_READ|CTL_WRITE)) == 0 | |
|| chan_flags[chan] & (SNS_UEND|CTL_END)) { | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= DEV_WEOR|DEV_DISCO; | |
if (cmd[chan] & CHAN_DSK_SEEK) | |
chan_flags[chan] &= ~(CTL_END); | |
else | |
chan_flags[chan] &= ~(STA_ACTIVE|SNS_UEND|CTL_END); | |
chan_io_status[chan] |= IO_CHS_DONE; | |
} | |
continue; | |
} | |
} | |
if (cmd[chan] & CHAN_DSK_SEEK) { | |
if (chan_seek_done[chan] || chan_flags[chan] & SNS_UEND) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_CHAN, &chan_dev, "chan %d seek done\n", chan); | |
chan_flags[chan] &= ~(STA_ACTIVE|SNS_UEND); | |
cmd[chan] &= ~CHAN_DSK_SEEK; | |
} | |
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] &= ~(STA_ACTIVE|SNS_UEND|CTL_END|CTL_READ | |
|CTL_WRITE); | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_CHAN, &chan_dev, "chan %d end\n", chan); | |
cmd[chan] &= ~CHAN_DSK_SEEK; | |
chan_io_status[chan] |= IO_CHS_DONE; | |
} | |
/* If device put up EOR, terminate transfer. */ | |
if (chan_flags[chan] & DEV_REOR) { | |
if (chan_flags[chan] & DEV_WRITE) { | |
if ((cmd[chan] & (CHAN_LOAD|CHAN_WM)) == (CHAN_WM|CHAN_LOAD)) | |
M[caddr[chan]++] = 035; | |
caddr[chan]++; | |
} else { | |
if ((cmd[chan] & CHAN_NOREC) == 0 && | |
(chan_flags[chan] & STA_WAIT) == 0) { | |
if (MEM_ADDR_OK(caddr[chan])) { | |
if (M[caddr[chan]++] != (WM|077)) { | |
if (MEM_ADDR_OK(caddr[chan])) { | |
chan_io_status[chan] |= IO_CHS_WRL; | |
if (!MEM_ADDR_OK(caddr[chan]+1)) { | |
caddr[chan]++; | |
} | |
} | |
} | |
} else { | |
chan_io_status[chan] |= IO_CHS_WRL; | |
} | |
} | |
if ((cmd[chan] & CHAN_NOREC) && MEM_ADDR_OK(caddr[chan])) { | |
chan_io_status[chan] |= IO_CHS_WRL; | |
if (!MEM_ADDR_OK(caddr[chan]+1)) { | |
chan_io_status[chan] &= ~IO_CHS_WRL; | |
} | |
caddr[chan]++; | |
} | |
} | |
chan_flags[chan] &= ~(STA_ACTIVE|STA_WAIT|DEV_WRITE|DEV_REOR); | |
chan_io_status[chan] |= IO_CHS_DONE; | |
/* Disconnect if selected */ | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= (DEV_DISCO); | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR %d %o\n", chan, | |
caddr[chan], chan_io_status[chan]); | |
continue; | |
} | |
if (((chan_flags[chan] & (DEV_SEL|STA_ACTIVE)) == STA_ACTIVE) && | |
(chan_flags[chan] & (CTL_CNTL|CTL_PREAD|CTL_PWRITE|CTL_READ| | |
CTL_WRITE|CTL_SNS)) == 0) { | |
chan_flags[chan] &= ~STA_ACTIVE; | |
} | |
/* If device requested attention, abort current command */ | |
if (chan_flags[chan] & CHS_ATTN) { | |
chan_flags[chan] &= ~(CHS_ATTN|STA_ACTIVE|STA_WAIT); | |
chan_io_status[chan] |= IO_CHS_DONE|IO_CHS_COND; | |
/* Disconnect if selected */ | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= (DEV_DISCO); | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn %o\n", | |
chan, chan_io_status[chan]); | |
continue; | |
} | |
} | |
} | |
void chan_set_attn_urec(int chan, uint16 addr) { | |
if (irqdev[chan] == addr) | |
urec_irq[chan] = 1; | |
} | |
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) | |
{ | |
uint32 chan; | |
t_stat r; | |
/* Find device on given channel and give it the command */ | |
chan = (dev >> 12) & 0x7; | |
/* 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; | |
cmd[chan] = 0; | |
if (dcmd & 0100) /* Mod $ or X */ | |
cmd[chan] |= CHAN_NOREC; | |
if (dcmd & 0200) /* Opcode L */ | |
cmd[chan] |= CHAN_LOAD; | |
else | |
cmd[chan] |= CHAN_WM; /* Force first char to have word mark set */ | |
dcmd = (dcmd >> 8) & 0x7f; | |
chunit[chan] = dev; | |
chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE|SNS_UEND|CTL_WRITE | |
|CTL_SNS|STA_PEND); | |
/* Handle disk device special */ | |
if ((dsk_dib.mask & dev) == (dsk_dib.addr & dsk_dib.mask)) { | |
uint16 dsk_cmd = 0; | |
dsk_cmd = disk_cmdmap[dev&017]; | |
/* Set up channel if command ok */ | |
if (dsk_cmd == 0xFF || dev & 060) { | |
/* Set io error and abort */ | |
return SCPE_IOERR; | |
} | |
if (cmd[chan] & CHAN_LOAD) { | |
cmd[chan] &= ~CHAN_LOAD; | |
dsk_cmd = 0x100; | |
} else { | |
cmd[chan] |= CHAN_6BIT; | |
} | |
/* Try to start drive */ | |
r = chan_issue_cmd(chan, dsk_cmd, dev); | |
if (r != SCPE_OK) | |
return r; | |
chan_flags[chan] |= CTL_CNTL; | |
if (dcmd == IO_RDS) | |
chan_flags[chan] |= CTL_PREAD; | |
if (dcmd == IO_WRS) | |
chan_flags[chan] |= CTL_PWRITE; | |
if (dcmd == IO_TRS) | |
chan_flags[chan] |= CTL_SNS; | |
cmd[chan] |= CHAN_DSK_DATA; | |
if ((dsk_cmd & 0xff) == 0x80 && cmd[chan] & CHAN_OVLP) { | |
cmd[chan] |= CHAN_DSK_SEEK; | |
chan_seek_done[chan] = 0; | |
} | |
chan_flags[chan] &= ~DEV_REOR; /* Clear in case still set */ | |
chan_flags[chan] |= STA_ACTIVE; | |
return r; | |
} | |
if ((com_dib.mask & dev) == (com_dib.addr & com_dib.mask)) { | |
switch(dcmd) { | |
case IO_RDS: chan_flags[chan] |= CTL_READ; break; | |
case IO_WRS: chan_flags[chan] |= CTL_WRITE; break; | |
case IO_TRS: chan_flags[chan] |= CTL_SNS; break; | |
case IO_CTL: chan_flags[chan] |= CTL_CNTL; break; | |
} | |
if ((dev & 077) != 1) | |
cmd[chan] |= CHAN_6BIT; | |
r = chan_issue_cmd(chan, dcmd, dev); | |
if (r == SCPE_OK) | |
chan_flags[chan] |= STA_ACTIVE; | |
return r; | |
} | |
r = chan_issue_cmd(chan, dcmd, dev); | |
/* Activate channel if select raised */ | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= STA_ACTIVE; | |
} | |
return r; | |
} | |
/* | |
* Write a word to the assembly register. | |
*/ | |
int | |
chan_write(int chan, t_uint64 * data, int flags) | |
{ | |
/* Not implimented on this machine */ | |
return TIME_ERROR; | |
} | |
/* | |
* Read next word from assembly register. | |
*/ | |
int | |
chan_read(int chan, t_uint64 * data, int flags) | |
{ | |
/* Not implimented on this machine */ | |
return TIME_ERROR; | |
} | |
/* | |
* Write a char to the assembly register. | |
*/ | |
int | |
chan_write_char(int chan, uint8 * data, int flags) | |
{ | |
uint8 ch = *data; | |
sim_debug(DEBUG_DATA, &chan_dev, "chan %d char %o %d %o %o\n", chan, | |
*data, caddr[chan], chan_io_status[chan], flags); | |
if (chan_flags[chan] & STA_WAIT) { | |
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d setWR %d %o\n", chan, | |
caddr[chan], chan_io_status[chan]); | |
chan_io_status[chan] |= IO_CHS_WRL; | |
return END_RECORD; | |
} | |
/* Check if end of data */ | |
if ((chan_flags[chan] & STA_WAIT) == 0 && (cmd[chan] & CHAN_NOREC) == 0 && | |
M[caddr[chan]] == (WM|077)) { | |
chan_flags[chan] |= STA_WAIT; /* Saw group mark */ | |
chan_io_status[chan] |= IO_CHS_WRL; | |
caddr[chan]++; | |
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d GEor %d %o\n", chan, | |
caddr[chan], chan_io_status[chan]); | |
return END_RECORD; | |
} | |
/* If over size of memory, terminate */ | |
if (!MEM_ADDR_OK(caddr[chan])) { | |
chan_flags[chan] |= DEV_REOR; | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= DEV_DISCO; | |
chan_io_status[chan] |= IO_CHS_DONE; | |
caddr[chan]++; | |
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d past mem %d %o\n", chan, | |
caddr[chan], chan_io_status[chan]); | |
chan_flags[chan] &= ~(DEV_WRITE|STA_ACTIVE); | |
return DATA_OK; | |
} | |
/* If we are in load mode and see word mark, save it */ | |
if ((cmd[chan] & (CHAN_LOAD|CHAN_WM)) == CHAN_LOAD && ch == 035) | |
cmd[chan] |= CHAN_WM; | |
else { | |
if (cmd[chan] & CHAN_6BIT) | |
ch &= 077; | |
if (cmd[chan] & CHAN_WM && ch != 035) | |
ch |= WM; | |
cmd[chan] &= ~CHAN_WM; | |
if ((cmd[chan] & CHAN_LOAD) == 0) | |
ch |= M[caddr[chan]] & WM; | |
if ((chan_flags[chan] & DEV_REOR) == 0) | |
M[caddr[chan]] = ch; | |
caddr[chan]++; | |
} | |
/* If device gave us an end, terminate transfer */ | |
if (flags & DEV_REOR) { | |
chan_flags[chan] |= DEV_REOR; | |
sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d Eor %d %o %x\n", chan, | |
caddr[chan], chan_io_status[chan], chan_flags[chan]); | |
return END_RECORD; | |
} | |
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); | |
chan_io_status[chan] |= IO_CHS_DONE; | |
return END_RECORD; | |
} | |
/* Check if he write out last data */ | |
if ((chan_flags[chan] & STA_ACTIVE) == 0) | |
return TIME_ERROR; | |
/* Send rest of command */ | |
if (cmd[chan] & CHAN_DSK_DATA) { | |
*data = M[caddr[chan]]; | |
if (*data == (WM|077)) | |
return END_RECORD; | |
*data &= 077; | |
caddr[chan]++; | |
return DATA_OK; | |
} | |
/* If we had a previous word mark send it */ | |
if ((cmd[chan] & (CHAN_LOAD|CHAN_WM)) == (CHAN_LOAD|CHAN_WM)) { | |
*data = assembly[chan]; | |
cmd[chan] &= ~CHAN_WM; | |
} else { | |
if (!MEM_ADDR_OK(caddr[chan]+1)) { | |
chan_flags[chan] &= ~STA_ACTIVE; | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= DEV_DISCO; | |
caddr[chan]++; | |
return END_RECORD; | |
} | |
assembly[chan] = M[caddr[chan]++]; | |
/* Handle end of record */ | |
if ((cmd[chan] & CHAN_NOREC) == 0 && assembly[chan] == (WM|077)) { | |
chan_flags[chan] &= ~STA_ACTIVE; | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= DEV_DISCO; | |
chan_io_status[chan] |= IO_CHS_DONE; | |
return END_RECORD; | |
} | |
if (cmd[chan] & CHAN_LOAD && | |
(assembly[chan] & WM || assembly[chan] == 035)) { | |
cmd[chan] |= CHAN_WM; | |
assembly[chan] &= 077; | |
*data = 035; | |
return DATA_OK; | |
} | |
if (cmd[chan] & CHAN_6BIT) | |
*data &= 077; | |
*data = assembly[chan]; | |
} | |
/* 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_io_status[chan] |= IO_CHS_DONE; | |
chan_flags[chan] |= DEV_REOR; | |
return END_RECORD; | |
} 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\n", chan_description(dptr)); | |
fprintf (st, "The 7010 supports up to 4 channels. Channel models include\n\n"); | |
fprintf (st, " Channel * is for unit record devices.\n"); | |
fprintf (st, " Channels 1-4 are 7010 multiplexor channel\n\n"); | |
fprintf (st, "Channels are fixed on the 7010.\n\n"); | |
fprint_set_help(st, dptr); | |
fprint_show_help(st, dptr); | |
return SCPE_OK; | |
} | |
const char * | |
chan_description(DEVICE *dptr) | |
{ | |
return "IBM 7010 channel controller"; | |
} | |