/* i701_chan.c: IBM 701 Channel simulator | |
Copyright (c) 2005, 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 | |
There is no channel on the 701, this module just provides basic support | |
for polled mode devices. | |
Simulated register for the channel is: | |
STATUS<0:16> Simulated register for basic channel status. | |
*/ | |
#include "i7090_defs.h" | |
extern uint8 iocheck; | |
extern UNIT cpu_unit; | |
extern uint16 IC; | |
extern t_uint64 MQ; | |
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); | |
uint32 dly_cmd(UNIT *, uint16, uint16); | |
/* Channel data structures | |
chan_dev Channel device descriptor | |
chan_unit Channel unit descriptor | |
chan_reg Channel register list | |
chan_mod Channel modifiers list | |
*/ | |
t_uint64 assembly[NUM_CHAN]; /* Assembly register */ | |
uint32 chan_flags[NUM_CHAN]; /* Unit status */ | |
uint8 bcnt[NUM_CHAN]; /* Character count */ | |
const char *chan_type_name[] = { | |
"Polled", "", "", "", ""}; | |
/* Delay device for IOD instruction */ | |
DIB dly_dib = | |
{ CH_TYP_PIO, 1, 2052, 07777, &dly_cmd, NULL }; | |
UNIT chan_unit[] = { | |
/* Puesdo channel for 701 devices */ | |
{UDATA(NULL, UNIT_DISABLE | CHAN_SET | | |
CHAN_S_TYPE(CHAN_PIO)|UNIT_S_CHAN(0), 0)}, | |
}; | |
REG chan_reg[] = { | |
{BRDATAD(ASM, assembly, 8, 36, NUM_CHAN, "Channel Assembly Register"), | |
REG_RO|REG_FIT}, | |
{BRDATAD(FLAGS, chan_flags, 2, 32, NUM_CHAN, "Channel flags"), | |
REG_RO|REG_FIT}, | |
{NULL} | |
}; | |
MTAB chan_mod[] = { | |
{0} | |
}; | |
DEVICE chan_dev = { | |
"CH", chan_unit, chan_reg, chan_mod, | |
NUM_CHAN, 8, 15, 1, 8, 36, | |
NULL, NULL, &chan_reset, NULL, NULL, NULL, | |
&dly_dib, DEV_DEBUG, 0, NULL, | |
NULL, NULL, &chan_help, NULL, NULL, &chan_description | |
}; | |
/* Nothing special to do, just return true if cmd is write and we got here */ | |
uint32 dly_cmd(UNIT * uptr, uint16 cmd, uint16 dev) | |
{ | |
if (cmd == IO_WRS) | |
return SCPE_OK; | |
return SCPE_NODEV; | |
} | |
t_stat | |
chan_reset(DEVICE * dptr) | |
{ | |
int i; | |
/* Clear channel assignment */ | |
for (i = 0; i < NUM_CHAN; i++) { | |
if (chan_unit[i].flags & CHAN_AUTO) | |
chan_unit[i].flags &= ~CHAN_SET; | |
else | |
chan_unit[i].flags |= CHAN_SET; | |
chan_flags[i] = 0; | |
} | |
return chan_set_devs(dptr); | |
} | |
/* Boot from given device */ | |
t_stat | |
chan_boot(int32 unit_num, DEVICE * dptr) | |
{ | |
/* Tell device to do a read, 3 records */ | |
/* Set channel address = 0, wc = 3, location = 0, CMD=0 */ | |
/* Set M[1] = TCO? 1, IC = 1 */ | |
UNIT *uptr = &dptr->units[unit_num]; | |
int chan = UNIT_G_CHAN(uptr->flags); | |
IC = 0; | |
chan_flags[chan] |= STA_ACTIVE; | |
chan_flags[chan] &= ~STA_PEND; | |
return SCPE_OK; | |
} | |
/* Execute the next channel instruction. */ | |
void | |
chan_proc() | |
{ | |
if (chan_flags[0] & CHS_ATTN) { | |
chan_flags[0] &= ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT); | |
if (chan_flags[0] & DEV_SEL) | |
chan_flags[0] |= (DEV_DISCO); | |
} | |
} | |
/* Issue a command to a channel */ | |
int | |
chan_cmd(uint16 dev, uint16 dcmd) | |
{ | |
UNIT *uptr; | |
uint32 chan; | |
DEVICE **dptr; | |
DIB *dibp; | |
int j; | |
/* Find device on given channel and give it the command */ | |
chan = (dev >> 9) & 017; | |
if (chan >= NUM_CHAN) | |
return SCPE_IOERR; | |
/* If no channel device, quick exit */ | |
if (chan_unit[chan].flags & UNIT_DIS) | |
return SCPE_IOERR; | |
/* On 704 device new command aborts current operation */ | |
if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO && | |
(chan_flags[chan] & (DEV_SEL | DEV_FULL | DEV_DISCO)) == DEV_SEL) { | |
chan_flags[chan] |= DEV_DISCO | DEV_WEOR; | |
return SCPE_BUSY; | |
} | |
/* Unit is busy doing something, wait */ | |
if (chan_flags[chan] & (DEV_SEL | DEV_DISCO | STA_TWAIT | STA_WAIT)) | |
return SCPE_BUSY; | |
/* Ok, try and find the unit */ | |
dev &= 07777; | |
for (dptr = sim_devices; *dptr != NULL; dptr++) { | |
int r; | |
dibp = (DIB *) (*dptr)->ctxt; | |
/* If no DIB, not channel device */ | |
if (dibp == NULL || dibp->ctype == CHAN_7909 || | |
(dibp->addr & dibp->mask) != (dev & dibp->mask)) | |
continue; | |
uptr = (*dptr)->units; | |
if (dibp->upc == 1) { | |
int num = (*dptr)->numunits; | |
for (j = 0; j < num; j++) { | |
if (UNIT_G_CHAN(uptr->flags) == chan) { | |
r = dibp->cmd(uptr, dcmd, dev); | |
if (r != SCPE_NODEV) { | |
bcnt[chan] = 6; | |
return r; | |
} | |
} | |
uptr++; | |
} | |
} else { | |
if (UNIT_G_CHAN(uptr->flags) == chan) { | |
r = dibp->cmd(uptr, dcmd, dev); | |
if (r != SCPE_NODEV) { | |
bcnt[chan] = 6; | |
return r; | |
} | |
} | |
} | |
} | |
return SCPE_NODEV; | |
} | |
/* | |
* Write a word to the assembly register. | |
*/ | |
int | |
chan_write(int chan, t_uint64 * data, int flags) | |
{ | |
/* Check if last data still not taken */ | |
if (chan_flags[chan] & DEV_FULL) { | |
/* Nope, see if we are waiting for end of record. */ | |
if (chan_flags[chan] & DEV_WEOR) { | |
chan_flags[chan] |= DEV_REOR; | |
chan_flags[chan] &= ~(DEV_WEOR|STA_WAIT); | |
return END_RECORD; | |
} | |
if (chan_flags[chan] & STA_ACTIVE) { | |
chan_flags[chan] |= CHS_ATTN; /* We had error */ | |
if ((flags & DEV_DISCO) == 0) | |
iocheck = 1; | |
} | |
chan_flags[chan] |= DEV_DISCO; | |
return TIME_ERROR; | |
} else { | |
if (chan == 0) | |
MQ = *data; | |
assembly[chan] = *data; | |
bcnt[chan] = 6; | |
chan_flags[chan] |= DEV_FULL; | |
chan_flags[chan] &= ~DEV_WRITE; | |
if (flags & DEV_REOR) { | |
chan_flags[chan] |= DEV_REOR; | |
} | |
} | |
/* 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 word from assembly register. | |
*/ | |
int | |
chan_read(int chan, t_uint64 * data, int flags) | |
{ | |
/* Return END_RECORD if requested */ | |
if (flags & DEV_WEOR) { | |
chan_flags[chan] &= ~(DEV_WEOR); | |
return END_RECORD; | |
} | |
/* Check if he write out last data */ | |
if ((chan_flags[chan] & DEV_FULL) == 0) { | |
if (chan_flags[chan] & DEV_WEOR) { | |
chan_flags[chan] |= DEV_WRITE; | |
chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT); | |
return END_RECORD; | |
} | |
if (chan_flags[chan] & STA_ACTIVE) { | |
chan_flags[chan] |= CHS_ATTN; | |
if ((flags & DEV_DISCO) == 0) | |
iocheck = 1; | |
} | |
chan_flags[chan] |= DEV_DISCO; | |
return TIME_ERROR; | |
} else { | |
*data = assembly[chan]; | |
bcnt[chan] = 6; | |
chan_flags[chan] &= ~DEV_FULL; | |
/* If end of record, don't transfer any data */ | |
if (flags & DEV_REOR) { | |
chan_flags[chan] &= ~(DEV_WRITE); | |
chan_flags[chan] |= DEV_REOR; | |
} else | |
chan_flags[chan] |= DEV_WRITE; | |
} | |
return DATA_OK; | |
} | |
/* | |
* Write a char to the assembly register. | |
*/ | |
int | |
chan_write_char(int chan, uint8 * data, int flags) | |
{ | |
/* Check if last data still not taken */ | |
if (chan_flags[chan] & DEV_FULL) { | |
/* Nope, see if we are waiting for end of record. */ | |
if (chan_flags[chan] & DEV_WEOR) { | |
chan_flags[chan] |= DEV_REOR; | |
chan_flags[chan] &= ~(DEV_WEOR|STA_WAIT); | |
return END_RECORD; | |
} | |
if (chan_flags[chan] & STA_ACTIVE) { | |
chan_flags[chan] |= CHS_ATTN; /* We had error */ | |
if ((flags & DEV_DISCO) == 0) | |
iocheck = 1; | |
} | |
chan_flags[chan] |= DEV_DISCO; | |
return TIME_ERROR; | |
} else { | |
int cnt = --bcnt[chan]; | |
t_uint64 wd = (chan == 0)? MQ:assembly[chan]; | |
wd &= 0007777777777LL; | |
wd <<= 6; | |
wd |= (*data) & 077; | |
if (chan == 0) | |
MQ = wd; | |
else | |
assembly[chan] = wd; | |
if (cnt == 0) { | |
chan_flags[chan] |= DEV_FULL; | |
chan_flags[chan] &= ~DEV_WRITE; | |
} | |
if (flags & DEV_REOR) { | |
chan_flags[chan] |= DEV_FULL|DEV_REOR; | |
chan_flags[chan] &= ~DEV_WRITE; | |
} | |
} | |
/* 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) | |
{ | |
/* Return END_RECORD if requested */ | |
if (flags & DEV_WEOR) { | |
chan_flags[chan] &= ~(DEV_WEOR); | |
return END_RECORD; | |
} | |
/* Check if he write out last data */ | |
if ((chan_flags[chan] & DEV_FULL) == 0) { | |
if (chan_flags[chan] & DEV_WEOR) { | |
chan_flags[chan] |= DEV_WRITE; | |
chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT); | |
return END_RECORD; | |
} | |
if (chan_flags[chan] & STA_ACTIVE) { | |
chan_flags[chan] |= CHS_ATTN; | |
if ((flags & DEV_DISCO) == 0) | |
iocheck = 1; | |
} | |
chan_flags[chan] |= DEV_DISCO; | |
return TIME_ERROR; | |
} else { | |
int cnt = --bcnt[chan]; | |
t_uint64 wd = assembly[chan]; | |
*data = 077 & (wd >> 30); | |
wd <<= 6; | |
wd |= 077 & (wd >> 36); | |
wd &= 0777777777777LL; | |
if (chan == 0) | |
MQ = wd; | |
assembly[chan] = wd; | |
if (cnt == 0) { | |
chan_flags[chan] &= ~DEV_FULL; | |
bcnt[chan] = 6; | |
} | |
/* If end of record, don't transfer any data */ | |
if (flags & DEV_REOR) { | |
chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL); | |
chan_flags[chan] |= DEV_REOR; | |
} else | |
chan_flags[chan] |= DEV_WRITE; | |
} | |
return DATA_OK; | |
} | |
void | |
chan9_set_error(int chan, uint32 mask) | |
{ | |
} | |
t_stat | |
chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { | |
fprintf(st, "IBM 701 Channel\n\n"); | |
fprintf(st, "Psuedo device to display IBM 701 I/O. The IBM 701 used polled"); | |
fprintf(st, " I/O,\nThe assembly register and the flags can be displayed\n"); | |
fprintf(st, "There are no options for the this device\n"); | |
return SCPE_OK; | |
} | |
const char *chan_description (DEVICE *dptr) { | |
return "IBM 701 Psuedo Channel"; | |
} | |