Card reader handle EOF correctly. Line printer proper handling of paging. Magtape clean up debug message. Update 7010, 7070, 7080 for EOF handling.
415 lines
12 KiB
C
415 lines
12 KiB
C
/* 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;
|
||
int32 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";
|
||
}
|
||
|