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