| /* 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[5 * 512]; | |
| extern int chwait; | |
| extern uint16 selreg; | |
| extern uint16 selreg2; | |
| extern uint16 flags; | |
| extern uint32 MAC2; | |
| extern uint16 irqflags; | |
| extern uint8 ioflags[5000/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"; | |
| } | |