| /* 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"; | |
| } | |