/* Map simH errors into machine errors */ t_stat mt_error(UNIT * uptr, int chan, t_stat r, DEVICE * dptr) { switch (r) { case MTSE_OK: /* no error */ sim_debug(DEBUG_EXP, dptr, "OK "); break; case MTSE_EOM: /* end of medium */ sim_debug(DEBUG_EXP, dptr, "EOT "); if (uptr->u5 & MT_BOT) { chan_set_blank(chan); } else { uptr->u5 &= ~MT_BOT; uptr->u5 |= MT_EOT; chan_set_eot(chan); } break; case MTSE_TMK: /* tape mark */ sim_debug(DEBUG_EXP, dptr, "MARK "); uptr->u5 &= ~(MT_BOT|MT_EOT); chan_set_eof(chan); break; case MTSE_WRP: /* write protected */ sim_debug(DEBUG_EXP, dptr, "WriteLocked "); chan_set_wrp(chan); break; case MTSE_INVRL: /* invalid rec lnt */ case MTSE_IOERR: /* IO error */ case MTSE_FMT: /* invalid format */ case MTSE_RECE: /* error in record */ chan_set_error(chan); /* Force redundency error */ sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r); break; case MTSE_BOT: /* beginning of tape */ uptr->u5 &= ~MT_EOT; uptr->u5 |= MT_BOT; chan_set_bot(chan); /* Set flag */ sim_debug(DEBUG_EXP, dptr, "BOT "); break; case MTSE_UNATT: /* unattached */ default: sim_debug(DEBUG_EXP, dptr, "%d ", r); } uptr->u5 &= ~(MT_CMD|MT_BIN); uptr->u5 |= MT_RDY|MT_IDLE; chan_set_end(chan); return SCPE_OK; }
/* Handle processing terminal controller commands */ t_stat dtc_srv(UNIT * uptr) { int chan = uptr->CMD & DTC_CHAN; uint8 ch; int ttu; int buf; int i; int line = uptr->LINE; /* Process interrage command */ if (uptr->CMD & DTC_INQ) { if (line == -1) { buf = -1; for(i = 0; i < DTC_MLINES; i++) { if (dtc_lstatus[i]& BufIRQ) { if ((dtc_lstatus[i] & BufSMASK) == BufReadRdy) buf = i; if ((dtc_lstatus[i] & BufSMASK) == BufWriteRdy || (dtc_lstatus[i] & BufSMASK) == BufIdle) { line = i; break; } } } sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm inqury found %d %d ", line, buf); if (line != -1) { if ((dtc_lstatus[line] & BufSMASK) == BufWriteRdy) { chan_set_eof(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " writerdy "); } else { sim_debug(DEBUG_DETAIL, &dtc_dev, " idle "); } } else if (buf != -1) { chan_set_read(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " readrdy "); line = buf; } if (line != -1) { if (dtc_lstatus[line] & BufAbnormal) { chan_set_wcflg(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " abnormal "); } dtc_lstatus[line] &= ~BufIRQ; } } else { if (line > dtc_desc.lines) { chan_set_notrdy(chan); } else { switch(dtc_lstatus[line] & BufSMASK) { case BufReadRdy: chan_set_read(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " readrdy "); break; case BufWriteRdy: chan_set_eof(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " writerdy "); break; case BufIdle: sim_debug(DEBUG_DETAIL, &dtc_dev, " idle "); break; default: chan_set_error(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " busy "); break; } } if (dtc_lstatus[line] & BufAbnormal) { chan_set_wcflg(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " abnormal "); } dtc_lstatus[line] &= ~BufIRQ; chan_set_wc(uptr->LINE, 0); chan_set_end(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, " %03o ", dtc_lstatus[line]); } if (line != -1) { for (ttu = 1; line > 15; ttu++) line -= 15; } else { ttu = line = 0; } chan_set_wc(chan, (ttu << 5) | line); chan_set_end(chan); uptr->CMD = DTC_RDY; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm inqury %d %d\n", ttu, line); } /* Process for each unit */ if (uptr->CMD & DTC_WR) { if (line > dtc_desc.lines || line == -1 || dtc_lstatus[line] & BufDisco) { sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write invalid %d\n", line); chan_set_notrdy(chan); chan_set_end(chan); uptr->CMD = DTC_RDY; return SCPE_OK; } /* Validate that we can send data to buffer */ i = dtc_lstatus[line] & BufSMASK; switch(i) { case BufNotReady: chan_set_notrdy(chan); /* Fall through */ case BufInputBusy: /* Fall through */ case BufRead: /* Fall through */ case BufReadRdy: chan_set_error(chan); /* Fall through */ case BufOutBusy: chan_set_eof(chan); chan_set_end(chan); uptr->CMD = DTC_RDY; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write busy %d %d\n", line, i); return SCPE_OK; /* Ok to start filling */ case BufIdle: case BufWriteRdy: dtc_bufptr[line] = 0; dtc_bsize[line] = 0; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write start %d\n", line); break; /* Continue filling */ case BufWrite: break; } if (chan_read_char(chan, &ch, dtc_bufptr[line] >= dtc_blimit[line])) { sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write done %d %d ", line, dtc_bufptr[line]); dtc_bsize[line] = dtc_bufptr[line]; dtc_bufptr[line] = 0; if (dtc_lstatus[line] & BufAbnormal) { chan_set_wcflg(chan); } /* Empty write, clears flags */ if (dtc_bsize[line] == 0) { sim_debug(DEBUG_DETAIL, &dtc_dev, "empty\n"); if ((dtc_lstatus[line] & BufSMASK) != BufIdle) { dtc_lstatus[line] = BufIRQ|BufIdle; IAR |= IRQ_12; } /* Check if we filled up buffer */ } else if (dtc_bsize[line] >= dtc_blimit[line]) { dtc_lstatus[line] = BufOutBusy; chan_set_gm(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, "full "); } else { dtc_lstatus[line] = BufOutBusy|BufGM; sim_debug(DEBUG_DETAIL, &dtc_dev, "gm "); } sim_debug(DEBUG_DETAIL, &dtc_dev, "\n"); for (ttu = 1; line > 15; ttu++) line -= 15; chan_set_wc(chan, (ttu << 5) | line); chan_set_end(chan); uptr->CMD = DTC_RDY; return SCPE_OK; } else { dtc_lstatus[line] = BufWrite; dtc_buf[line][dtc_bufptr[line]++] = ch & 077; sim_debug(DEBUG_DATA, &dtc_dev, "Datacomm write data %d %02o %d\n", line, ch&077, dtc_bufptr[line]); } sim_activate(uptr, 5000); } if (uptr->CMD & DTC_RD) { if (line > dtc_desc.lines || line == -1 || dtc_lstatus[line] & BufDisco) { sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read nothing %d\n", line); chan_set_notrdy(chan); chan_set_end(chan); uptr->CMD = DTC_RDY; return SCPE_OK; } /* Validate that we can send data to buffer */ i = dtc_lstatus[line] & BufSMASK; switch(i) { case BufNotReady: chan_set_notrdy(chan); /* Fall through */ case BufInputBusy: chan_set_error(chan); /* Fall through */ case BufWriteRdy: /* Fall through */ case BufOutBusy: /* Fall through */ case BufIdle: /* Fall through */ case BufWrite: chan_set_eof(chan); chan_set_end(chan); uptr->CMD = DTC_RDY; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read busy %d %d\n", line, i); return SCPE_OK; /* Ok to start filling */ case BufReadRdy: dtc_lstatus[line] = (dtc_lstatus[line] & 030) | BufRead; dtc_bufptr[line] = 0; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read starting %d\n", line); break; /* Continue filling */ case BufRead: break; } ch = dtc_buf[line][dtc_bufptr[line]++]; /* If no buffer, error out */ if (chan_write_char(chan, &ch, dtc_bufptr[line] >= dtc_bsize[line])) { /* Check if we filled up buffer */ if (dtc_lstatus[line] & BufGM) { chan_set_gm(chan); sim_debug(DEBUG_DETAIL, &dtc_dev, "gm "); } if (dtc_lstatus[line] & BufAbnormal) chan_set_wcflg(chan); if (dtc_ldsc[line].conn == 0) /* connected? */ dtc_lstatus[line] = BufIRQ|BufAbnormal|BufIRQ|BufIdle; else dtc_lstatus[line] = BufIRQ|BufIdle; dtc_bsize[line] = 0; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read done %d\n", line); for (ttu = 1; line > 15; ttu++) line -= 15; chan_set_wc(chan, (ttu << 5) | line); chan_set_end(chan); uptr->CMD = DTC_RDY; IAR |= IRQ_12; return SCPE_OK; } else { sim_debug(DEBUG_DATA, &dtc_dev, "Datacomm read data %d %02o %d\n", line, ch & 077, dtc_bufptr[line]); } sim_activate(uptr, 5000); } return SCPE_OK; }
/* Handle processing of tape requests. */ t_stat mt_srv(UNIT * uptr) { int chan = uptr->u5 & MT_CHAN; int unit = uptr - mt_unit; int cmd = uptr->u5 & MT_CMD; DEVICE *dptr = find_dev_from_unit(uptr); t_mtrlnt reclen; t_stat r = SCPE_ARG; /* Force error if not set */ uint8 ch; int mode; t_mtrlnt loc; /* Simulate tape load delay */ if (uptr->u5 & MT_LOADED) { uptr->u5 &= ~MT_LOADED; uptr->u5 |= MT_BSY|MT_RDY; sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Loaded\n", unit); sim_activate(uptr, 50000); return SCPE_OK; } if (uptr->u5 & MT_BSY) { uptr->u5 &= ~MT_BSY; sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Online\n", unit); iostatus |= 1 << (uptr - mt_unit); if (uptr->u5 & MT_IDLE) sim_activate(uptr, 50000); return SCPE_OK; } if (uptr->u5 & MT_IDLE) { uptr->u5 &= ~MT_IDLE; if (uptr->u5 & MT_RDY) { sim_debug(DEBUG_DETAIL, dptr, "Unit=%d idling\n", unit); return SCPE_OK; } sim_debug(DEBUG_DETAIL, dptr, "Unit=%d start %02o\n", unit, cmd); } switch (cmd) { /* Handle interrogate */ case MT_INT: if (sim_tape_wrp(uptr)) chan_set_wrp(chan); uptr->u5 &= ~(MT_CMD|MT_BIN); uptr->u5 |= MT_RDY; chan_set_end(chan); sim_debug(DEBUG_DETAIL, dptr, "Status\n"); return SCPE_OK; case MT_RD: /* Read */ /* If at end of record, fill buffer */ if (BUF_EMPTY(uptr)) { sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d %s ", unit, (uptr->u5 & MT_BIN)? "bin": "bcd"); if (sim_tape_eot(uptr)) { sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_EOM, dptr); } r = sim_tape_rdrecf(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); if (r != MTSE_OK) { if (r == MTSE_TMK) { sim_debug(DEBUG_DETAIL, dptr, "TM\n"); ch = 017; (void)chan_write_char(chan, &ch, 1); sim_activate(uptr, 4000); } else { sim_debug(DEBUG_DETAIL, dptr, "r=%d\n", r); sim_activate(uptr, 5000); } return mt_error(uptr, chan, r, dptr); } else { uptr->u5 &= ~(MT_BOT|MT_EOT); uptr->hwmark = reclen; } sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark); uptr->u6 = 0; if ((uptr->u5 & MT_BIN) == 0) mode = 0100; else mode = 0; for (loc = 0; loc < reclen; loc++) { ch = mt_buffer[chan][loc] & 0177; if (((parity_table[ch & 077]) ^ (ch & 0100) ^ mode) == 0) { chan_set_error(chan); break; } } } ch = mt_buffer[chan][uptr->u6++] & 0177; /* 00 characters are not transfered in BCD mode */ if (ch == 0) { if (((uint32)uptr->u6) >= uptr->hwmark) { sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_OK, dptr); } else { sim_activate(uptr, HT); return SCPE_OK; } } if (chan_write_char(chan, &ch, (((uint32)uptr->u6) >= uptr->hwmark) ? 1 : 0)) { sim_debug(DEBUG_DATA, dptr, "Read unit=%d %d EOR\n", unit, uptr->hwmark-uptr->u6); sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_OK, dptr); } else { sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %03o\n", unit, uptr->u6, ch); sim_activate(uptr, HT); } return SCPE_OK; case MT_RDBK: /* Read Backword */ /* If at end of record, fill buffer */ if (BUF_EMPTY(uptr)) { sim_debug(DEBUG_DETAIL, dptr, "Read back unit=%d %s ", unit, (uptr->u5 & MT_BIN)? "bin": "bcd"); if (sim_tape_bot(uptr)) { sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_BOT, dptr); } r = sim_tape_rdrecr(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); if (r != MTSE_OK) { if (r == MTSE_TMK) { sim_debug(DEBUG_DETAIL, dptr, "TM\n"); ch = 017; (void)chan_write_char(chan, &ch, 1); sim_activate(uptr, 4000); } else { uptr->u5 |= MT_BSY; sim_debug(DEBUG_DETAIL, dptr, "r=%d\n", r); sim_activate(uptr, 100); } return mt_error(uptr, chan, r, dptr); } else { uptr->u5 &= ~(MT_BOT|MT_EOT); uptr->hwmark = reclen; } sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark); uptr->u6 = uptr->hwmark; if ((uptr->u5 & MT_BIN) == 0) mode = 0100; else mode = 0; for (loc = 0; loc < reclen; loc++) { ch = mt_buffer[chan][loc] & 0177; if (((parity_table[ch & 077]) ^ (ch & 0100) ^ mode) == 0) { chan_set_error(chan); break; } } } ch = mt_buffer[chan][--uptr->u6] & 0177; /* 00 characters are not transfered in BCD mode */ if (ch == 0) { if (uptr->u6 <= 0) { sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_OK, dptr); } else { sim_activate(uptr, HT); return SCPE_OK; } } if (chan_write_char(chan, &ch, (uptr->u6 > 0) ? 0 : 1)) { sim_debug(DEBUG_DATA, dptr, "Read back unit=%d %d EOR\n", unit, uptr->hwmark-uptr->u6); sim_activate(uptr, 100); return mt_error(uptr, chan, MTSE_OK, dptr); } else { sim_debug(DEBUG_DATA, dptr, "Read back data unit=%d %d %03o\n", unit, uptr->u6, ch); sim_activate(uptr, HT); } return SCPE_OK; case MT_WR: /* Write */ /* Check if write protected */ if (uptr->u6 == 0 && sim_tape_wrp(uptr)) { sim_activate(uptr, 100); return mt_error(uptr, chan, MTSE_WRP, dptr); } if (chan_read_char(chan, &ch, (uptr->u6 > BUFFSIZE) ? 1 : 0)) { reclen = uptr->u6; /* If no transfer, then either erase */ if (reclen == 0) { sim_debug(DEBUG_DETAIL, dptr, "Erase\n"); r = MTSE_OK; } else if ((reclen == 1) && (cmd & MT_BIN) == 0 && (mt_buffer[chan][0] == 017)) { /* Check if write rtape mark */ sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit); r = sim_tape_wrtmk(uptr); } else { sim_debug(DEBUG_DETAIL, dptr, "Write unit=%d Block %d %s chars\n", unit, reclen, (uptr->u5 & MT_BIN)? "bin": "bcd"); r = sim_tape_wrrecf(uptr, &mt_buffer[chan][0], reclen); } uptr->u5 &= ~(MT_BOT|MT_EOT); sim_activate(uptr, 4000); return mt_error(uptr, chan, r, dptr); /* Record errors */ } else { /* Copy data to buffer */ ch &= 077; ch |= parity_table[ch]; if ((uptr->u5 & MT_BIN)) ch ^= 0100; /* Don't write out even parity zeros */ if (ch != 0) mt_buffer[chan][uptr->u6++] = ch; sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %03o\n", unit, uptr->u6, ch); uptr->hwmark = uptr->u6; } sim_activate(uptr, HT); return SCPE_OK; case MT_FSR: /* Space forward one record */ if (BUF_EMPTY(uptr)) { /* If at end of record, fill buffer */ sim_debug(DEBUG_DETAIL, dptr, "Space unit=%d ", unit); if (sim_tape_eot(uptr)) { uptr->u5 &= ~MT_BOT; sim_debug(DEBUG_DETAIL, dptr, "EOT\n"); sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_EOM, dptr); } r = sim_tape_rdrecf(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); if (r != MTSE_OK) { if (r == MTSE_TMK) { sim_debug(DEBUG_DETAIL, dptr, "TM "); reclen = 1; chan_set_eof(chan); } else { sim_debug(DEBUG_DETAIL, dptr, "r=%d ", r); reclen = 10; } } uptr->u5 &= ~(MT_BOT|MT_EOT); uptr->hwmark = reclen; sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark); sim_activate(uptr, uptr->hwmark * HT); return SCPE_OK; } sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_OK, dptr); case MT_BSR: /* Backspace record */ if (BUF_EMPTY(uptr)) { /* If at end of record, fill buffer */ sim_debug(DEBUG_DETAIL, dptr, "backspace unit=%d ", unit); if (sim_tape_bot(uptr)) { sim_debug(DEBUG_DETAIL, dptr, "BOT\n"); sim_activate(uptr, 100); return mt_error(uptr, chan, MTSE_BOT, dptr); } r = sim_tape_rdrecr(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); if (r != MTSE_OK) { if (r == MTSE_TMK) { sim_debug(DEBUG_DETAIL, dptr, "TM "); reclen = 1; chan_set_eof(chan); } else { reclen = 10; sim_debug(DEBUG_DETAIL, dptr, "r=%d ", r); } } uptr->u5 &= ~(MT_BOT|MT_EOT); uptr->hwmark = reclen; sim_debug(DEBUG_DETAIL, dptr, "%d chars\n", uptr->hwmark); sim_activate(uptr, uptr->hwmark * HT); return SCPE_OK; } sim_activate(uptr, 4000); return mt_error(uptr, chan, MTSE_OK, dptr); case MT_REW: /* Rewind */ sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d pos=%d\n", unit, uptr->pos); uptr->u5 &= ~(MT_CMD | MT_BIN | MT_IDLE | MT_RDY); uptr->u5 |= MT_BSY|MT_RDY; iostatus &= ~(1 << (uptr - mt_unit)); sim_activate(uptr, (uptr->pos/100) + 100); r = sim_tape_rewind(uptr); uptr->u5 &= ~MT_EOT; uptr->u5 |= MT_BOT; chan_set_end(chan); return r; } return mt_error(uptr, chan, r, dptr); }