| /* pdp11_ta.c: PDP-11 cassette tape simulator | |
| Copyright (c) 2007-2008, Robert M Supnik | |
| 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 | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ATAION OF CONTRATA, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNETAION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| ta TA11/TU60 cassette tape | |
| 06-Aug-07 RMS Foward op at BOT skips initial file gap | |
| Magnetic tapes are represented as a series of variable records | |
| of the form: | |
| 32b byte count | |
| byte 0 | |
| byte 1 | |
| : | |
| byte n-2 | |
| byte n-1 | |
| 32b byte count | |
| If the byte count is odd, the record is padded with an extra byte | |
| of junk. File marks are represented by a byte count of 0. | |
| Cassette format differs in one very significant way: it has file gaps | |
| rather than file marks. If the controller spaces or reads into a file | |
| gap and then reverses direction, the file gap is not seen again. This | |
| is in contrast to magnetic tapes, where the file mark is a character | |
| sequence and is seen again if direction is reversed. In addition, | |
| cassettes have an initial file gap which is automatically skipped on | |
| forward operations from beginning of tape. | |
| */ | |
| #include "pdp11_defs.h" | |
| #include "sim_tape.h" | |
| #define TA_NUMDR 2 /* #drives */ | |
| #define FNC u3 /* unit function */ | |
| #define UST u4 /* unit status */ | |
| #define TA_SIZE 93000 /* chars/tape */ | |
| #define TA_MAXFR (TA_SIZE) /* max record lnt */ | |
| /* Control/status - TACS */ | |
| #define TACS_ERR (1 << CSR_V_ERR) /* error */ | |
| #define TACS_CRC 0040000 /* CRC */ | |
| #define TACS_BEOT 0020000 /* BOT/EOT */ | |
| #define TACS_WLK 0010000 /* write lock */ | |
| #define TACS_EOF 0004000 /* end file */ | |
| #define TACS_TIM 0002000 /* timing */ | |
| #define TACS_EMP 0001000 /* empty */ | |
| #define TACS_V_UNIT 8 /* unit */ | |
| #define TACS_M_UNIT (TA_NUMDR - 1) | |
| #define TACS_UNIT (TACS_M_UNIT << TACS_V_UNIT) | |
| #define TACS_TR (1 << CSR_V_DONE) /* transfer req */ | |
| #define TACS_IE (1 << CSR_V_IE) /* interrupt enable */ | |
| #define TACS_RDY 0000040 /* ready */ | |
| #define TACS_ILBS 0000020 /* start CRC */ | |
| #define TACS_V_FNC 1 /* function */ | |
| #define TACS_M_FNC 07 | |
| #define TACS_WFG 00 | |
| #define TACS_WRITE 01 | |
| #define TACS_READ 02 | |
| #define TACS_SRF 03 | |
| #define TACS_SRB 04 | |
| #define TACS_SFF 05 | |
| #define TACS_SFB 06 | |
| #define TACS_REW 07 | |
| #define TACS_2ND 010 | |
| #define TACS_3RD 030 | |
| #define TACS_FNC (TACS_M_FNC << TACS_V_FNC) | |
| #define TACS_GO (1 << CSR_V_GO) /* go */ | |
| #define TACS_W (TACS_UNIT|TACS_IE|TACS_ILBS|TACS_FNC) | |
| #define TACS_XFRERR (TACS_ERR|TACS_CRC|TACS_WLK|TACS_EOF|TACS_TIM) | |
| #define GET_UNIT(x) (((x) >> TACS_V_UNIT) & TACS_M_UNIT) | |
| #define GET_FNC(x) (((x) >> TACS_V_FNC) & TACS_M_FNC) | |
| /* Function code flags */ | |
| #define OP_WRI 01 /* op is a write */ | |
| #define OP_REV 02 /* op is rev motion */ | |
| #define OP_FWD 04 /* op is fwd motion */ | |
| /* Unit status flags */ | |
| #define UST_REV (OP_REV) /* last op was rev */ | |
| #define UST_GAP 01 /* last op hit gap */ | |
| extern int32 int_req[IPL_HLVL]; | |
| extern FILE *sim_deb; | |
| uint32 ta_cs = 0; /* control/status */ | |
| uint32 ta_idb = 0; /* input data buf */ | |
| uint32 ta_odb = 0; /* output data buf */ | |
| uint32 ta_write = 0; /* TU60 write flag */ | |
| uint32 ta_bptr = 0; /* buf ptr */ | |
| uint32 ta_blnt = 0; /* buf length */ | |
| int32 ta_stime = 1000; /* start time */ | |
| int32 ta_ctime = 100; /* char latency */ | |
| uint32 ta_stopioe = 1; /* stop on error */ | |
| uint8 *ta_xb = NULL; /* transfer buffer */ | |
| static uint8 ta_fnc_tab[TACS_M_FNC + 1] = { | |
| OP_WRI|OP_FWD, OP_WRI|OP_FWD, OP_FWD, OP_REV, | |
| OP_REV , OP_FWD, OP_FWD, 0 | |
| }; | |
| DEVICE ta_dev; | |
| t_stat ta_rd (int32 *data, int32 PA, int32 access); | |
| t_stat ta_wr (int32 data, int32 PA, int32 access); | |
| t_stat ta_svc (UNIT *uptr); | |
| t_stat ta_reset (DEVICE *dptr); | |
| t_stat ta_attach (UNIT *uptr, char *cptr); | |
| t_stat ta_detach (UNIT *uptr); | |
| void ta_go (void); | |
| t_stat ta_map_err (UNIT *uptr, t_stat st); | |
| UNIT *ta_busy (void); | |
| void ta_set_tr (void); | |
| uint32 ta_updsta (UNIT *uptr); | |
| uint32 ta_crc (uint8 *buf, uint32 cnt); | |
| /* TA data structures | |
| ta_dev TA device descriptor | |
| ta_unit TA unit list | |
| ta_reg TA register list | |
| ta_mod TA modifier list | |
| */ | |
| #define IOLN_TA 004 | |
| DIB ta_dib = { | |
| IOBA_AUTO, IOLN_TA, &ta_rd, &ta_wr, | |
| 1, IVCL (TA), VEC_AUTO, { NULL } | |
| }; | |
| UNIT ta_unit[] = { | |
| { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) }, | |
| { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) }, | |
| }; | |
| REG ta_reg[] = { | |
| { ORDATA (TACS, ta_cs, 16) }, | |
| { ORDATA (TAIDB, ta_idb, 8) }, | |
| { ORDATA (TAODB, ta_odb, 8) }, | |
| { FLDATA (WRITE, ta_write, 0) }, | |
| { FLDATA (INT, IREQ (TA), INT_V_TA) }, | |
| { FLDATA (ERR, ta_cs, CSR_V_ERR) }, | |
| { FLDATA (TR, ta_cs, CSR_V_DONE) }, | |
| { FLDATA (IE, ta_cs, CSR_V_IE) }, | |
| { DRDATA (BPTR, ta_bptr, 17) }, | |
| { DRDATA (BLNT, ta_blnt, 17) }, | |
| { DRDATA (STIME, ta_stime, 24), PV_LEFT + REG_NZ }, | |
| { DRDATA (CTIME, ta_ctime, 24), PV_LEFT + REG_NZ }, | |
| { FLDATA (STOP_IOE, ta_stopioe, 0) }, | |
| { URDATA (UFNC, ta_unit[0].FNC, 8, 5, 0, TA_NUMDR, 0), REG_HRO }, | |
| { URDATA (UST, ta_unit[0].UST, 8, 2, 0, TA_NUMDR, 0), REG_HRO }, | |
| { URDATA (POS, ta_unit[0].pos, 10, T_ADDR_W, 0, | |
| TA_NUMDR, PV_LEFT | REG_RO) }, | |
| { ORDATA (DEVADDR, ta_dib.ba, 32), REG_HRO }, | |
| { ORDATA (DEVVEC, ta_dib.vec, 16), REG_HRO }, | |
| { NULL } | |
| }; | |
| MTAB ta_mod[] = { | |
| { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, | |
| { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, | |
| // { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| // &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, | |
| { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, | |
| NULL, &sim_tape_show_capac, NULL }, | |
| { MTAB_XTD|MTAB_VDV, IOLN_TA, "ADDRESS", "ADDRESS", | |
| &set_addr, &show_addr, NULL }, | |
| { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", | |
| &set_vec, &show_vec, NULL }, | |
| { 0 } | |
| }; | |
| DEVICE ta_dev = { | |
| "TA", ta_unit, ta_reg, ta_mod, | |
| TA_NUMDR, 10, 31, 1, 8, 8, | |
| NULL, NULL, &ta_reset, | |
| NULL, &ta_attach, &ta_detach, | |
| &ta_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_TAPE | |
| }; | |
| /* I/O dispatch routines, I/O addresses 17777500 - 17777503 | |
| 17777500 TACS read/write | |
| 17777502 TADB read/write | |
| */ | |
| t_stat ta_rd (int32 *data, int32 PA, int32 access) | |
| { | |
| switch ((PA >> 1) & 01) { /* decode PA<1> */ | |
| case 0: /* TACSR */ | |
| *data = ta_updsta (NULL); /* update status */ | |
| break; | |
| case 1: /* TADB */ | |
| *data = ta_idb; /* return byte */ | |
| ta_cs &= ~TACS_TR; /* clear tra req */ | |
| ta_updsta (NULL); | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat ta_wr (int32 data, int32 PA, int32 access) | |
| { | |
| switch ((PA >> 1) & 01) { /* decode PA<1> */ | |
| case 0: /* TACS */ | |
| if (access == WRITEB) data = (PA & 1)? /* byte write? */ | |
| (ta_cs & 0377) | (data << 8): /* merge old */ | |
| (ta_cs & ~0377) | data; | |
| ta_cs = (ta_cs & ~TACS_W) | (data & TACS_W); /* merge new */ | |
| if ((data & CSR_GO) && !ta_busy ()) /* go, not busy? */ | |
| ta_go (); /* start operation */ | |
| if (ta_cs & TACS_ILBS) /* ILBS inhibits TR */ | |
| ta_cs &= ~TACS_TR; | |
| break; | |
| case 1: /* TADB */ | |
| if (PA & 1) /* ignore odd byte */ | |
| break; | |
| ta_odb = data; /* return byte */ | |
| ta_cs &= ~TACS_TR; /* clear tra req */ | |
| break; | |
| } /* end switch */ | |
| ta_updsta (NULL); /* update status */ | |
| return SCPE_OK; | |
| } | |
| /* Start a new operation - cassette is not busy */ | |
| void ta_go (void) | |
| { | |
| UNIT *uptr = ta_dev.units + GET_UNIT (ta_cs); | |
| uint32 fnc = GET_FNC (ta_cs); | |
| uint32 flg = ta_fnc_tab[fnc]; | |
| uint32 old_ust = uptr->UST; | |
| if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, | |
| ">>TA start: op=%o, old_sta = %o, pos=%d\n", | |
| fnc, uptr->UST, uptr->pos); | |
| ta_cs &= ~(TACS_XFRERR|TACS_EMP|TACS_TR|TACS_RDY); /* clr err, tr, rdy */ | |
| ta_bptr = 0; /* init buffer */ | |
| ta_blnt = 0; | |
| if ((uptr->flags & UNIT_ATT) == 0) { | |
| ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY; | |
| return; | |
| } | |
| if (flg & OP_WRI) { /* write op? */ | |
| if (sim_tape_wrp (uptr)) { /* locked? */ | |
| ta_cs |= TACS_ERR|TACS_WLK|TACS_RDY; /* don't start */ | |
| return; | |
| } | |
| ta_odb = 0; | |
| ta_write = 1; | |
| } | |
| else { | |
| ta_idb = 0; | |
| ta_write = 0; | |
| } | |
| ta_cs &= ~TACS_BEOT; /* tape in motion */ | |
| uptr->FNC = fnc; /* save function */ | |
| if ((fnc != TACS_REW) && !(flg & OP_WRI)) { /* spc/read cmd? */ | |
| t_mtrlnt t; | |
| t_stat st; | |
| uptr->UST = flg & UST_REV; /* save direction */ | |
| if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ | |
| st = sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); /* skip file gap */ | |
| if (st != MTSE_TMK) /* not there? */ | |
| sim_tape_rewind (uptr); /* restore tap pos */ | |
| else old_ust = 0; /* defang next */ | |
| } | |
| if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* reverse in gap? */ | |
| if (uptr->UST) /* skip file gap */ | |
| sim_tape_rdrecr (uptr, ta_xb, &t, TA_MAXFR); | |
| else sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); | |
| if (DEBUG_PRS (ta_dev)) | |
| fprintf (sim_deb, ">>TA skip gap: op=%o, old_sta = %o, pos=%d\n", | |
| fnc, uptr->UST, uptr->pos); | |
| } | |
| } | |
| else uptr->UST = 0; | |
| sim_activate (uptr, ta_stime); /* schedule op */ | |
| return; | |
| } | |
| /* Unit service */ | |
| t_stat ta_svc (UNIT *uptr) | |
| { | |
| uint32 i, crc; | |
| uint32 flg = ta_fnc_tab[uptr->FNC & TACS_M_FNC]; | |
| t_mtrlnt tbc; | |
| t_stat st, r; | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ | |
| ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY; | |
| ta_updsta (uptr); /* update status */ | |
| return (ta_stopioe? SCPE_UNATT: SCPE_OK); | |
| } | |
| if (((flg & OP_FWD) && sim_tape_eot (uptr)) || /* illegal motion? */ | |
| ((flg & OP_REV) && sim_tape_bot (uptr))) { | |
| ta_cs |= TACS_ERR|TACS_BEOT|TACS_RDY; /* error */ | |
| ta_updsta (uptr); | |
| return SCPE_OK; | |
| } | |
| r = SCPE_OK; | |
| switch (uptr->FNC) { /* case on function */ | |
| case TACS_READ: /* read start */ | |
| st = sim_tape_rdrecf (uptr, ta_xb, &ta_blnt, TA_MAXFR); /* get rec */ | |
| if (st == MTSE_RECE) /* rec in err? */ | |
| ta_cs |= TACS_ERR|TACS_CRC; | |
| else if (st != MTSE_OK) { /* other error? */ | |
| r = ta_map_err (uptr, st); /* map error */ | |
| break; | |
| } | |
| crc = ta_crc (ta_xb, ta_blnt); /* calculate CRC */ | |
| ta_xb[ta_blnt++] = (crc >> 8) & 0377; /* append to buffer */ | |
| ta_xb[ta_blnt++] = crc & 0377; | |
| uptr->FNC |= TACS_2ND; /* next state */ | |
| sim_activate (uptr, ta_ctime); /* sched next char */ | |
| return SCPE_OK; | |
| case TACS_READ|TACS_2ND: /* read char */ | |
| if (ta_bptr < ta_blnt) /* more chars? */ | |
| ta_idb = ta_xb[ta_bptr++]; | |
| else { /* no */ | |
| ta_idb = 0; | |
| ta_cs |= TACS_ERR|TACS_CRC; /* overrun */ | |
| break; /* tape stops */ | |
| } | |
| if (ta_cs & TACS_ILBS) { /* CRC seq? */ | |
| uptr->FNC |= TACS_3RD; /* next state */ | |
| sim_activate (uptr, ta_stime); /* sched CRC chk */ | |
| } | |
| else { | |
| ta_set_tr (); /* set tra req */ | |
| sim_activate (uptr, ta_ctime); /* sched next char */ | |
| } | |
| return SCPE_OK; | |
| case TACS_READ|TACS_3RD: /* second read CRC */ | |
| if (ta_bptr != ta_blnt) { /* partial read? */ | |
| crc = ta_crc (ta_xb, ta_bptr + 2); /* actual CRC */ | |
| if (crc != 0) /* must be zero */ | |
| ta_cs |= TACS_ERR|TACS_CRC; | |
| } | |
| break; /* read done */ | |
| case TACS_WRITE: /* write start */ | |
| for (i = 0; i < TA_MAXFR; i++) /* clear buffer */ | |
| ta_xb[i] = 0; | |
| ta_set_tr (); /* set tra req */ | |
| uptr->FNC |= TACS_2ND; /* next state */ | |
| sim_activate (uptr, ta_ctime); /* sched next char */ | |
| return SCPE_OK; | |
| case TACS_WRITE|TACS_2ND: /* write char */ | |
| if (ta_cs & TACS_ILBS) { /* CRC seq? */ | |
| uptr->FNC |= TACS_3RD; /* next state */ | |
| sim_activate (uptr, ta_stime); /* sched wri done */ | |
| } | |
| else { | |
| if ((ta_bptr < TA_MAXFR) && /* room in buf? */ | |
| ((uptr->pos + ta_bptr) < uptr->capac)) /* room on tape? */ | |
| ta_xb[ta_bptr++] = ta_odb; /* store char */ | |
| ta_set_tr (); /* set tra req */ | |
| sim_activate (uptr, ta_ctime); /* sched next char */ | |
| } | |
| return SCPE_OK; | |
| case TACS_WRITE|TACS_3RD: /* write CRC */ | |
| if (ta_bptr) { /* anything to write? */ | |
| if ((st = sim_tape_wrrecf (uptr, ta_xb, ta_bptr)))/* write, err? */ | |
| r = ta_map_err (uptr, st); /* map error */ | |
| } | |
| break; /* op done */ | |
| case TACS_WFG: /* write file gap */ | |
| if ((st = sim_tape_wrtmk (uptr))) /* write tmk, err? */ | |
| r = ta_map_err (uptr, st); /* map error */ | |
| break; | |
| case TACS_REW: /* rewind */ | |
| sim_tape_rewind (uptr); | |
| ta_cs |= TACS_BEOT; /* bot, no error */ | |
| break; | |
| case TACS_SRB: /* space rev blk */ | |
| if ((st = sim_tape_sprecr (uptr, &tbc))) /* space rev, err? */ | |
| r = ta_map_err (uptr, st); /* map error */ | |
| break; | |
| case TACS_SRF: /* space rev file */ | |
| while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; | |
| if (st == MTSE_TMK) /* if tape mark, */ | |
| ta_cs |= TACS_EOF; /* set EOF, no err */ | |
| else r = ta_map_err (uptr, st); /* else map error */ | |
| break; | |
| case TACS_SFB: /* space fwd blk */ | |
| if ((st = sim_tape_sprecf (uptr, &tbc))) /* space rev, err? */ | |
| r = ta_map_err (uptr, st); /* map error */ | |
| ta_cs |= TACS_CRC; /* CRC sets, no err */ | |
| break; | |
| case TACS_SFF: /* space fwd file */ | |
| while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; | |
| if (st == MTSE_TMK) /* if tape mark, */ | |
| ta_cs |= TACS_EOF; /* set EOF, no err */ | |
| else r = ta_map_err (uptr, st); /* else map error */ | |
| break; | |
| default: /* never get here! */ | |
| return SCPE_IERR; | |
| } /* end case */ | |
| ta_cs |= TACS_RDY; /* set ready */ | |
| ta_updsta (uptr); /* update status */ | |
| if (DEBUG_PRS (ta_dev)) | |
| fprintf (sim_deb, ">>TA done: op=%o, status = %o, pos=%d\n", | |
| uptr->FNC, ta_cs, uptr->pos); | |
| return r; | |
| } | |
| /* Update controller status */ | |
| uint32 ta_updsta (UNIT *uptr) | |
| { | |
| if (uptr == NULL) { /* unit specified? */ | |
| if ((uptr = ta_busy ()) == NULL) /* use busy */ | |
| uptr = ta_dev.units + GET_UNIT (ta_cs); /* use sel unit */ | |
| } | |
| else if (ta_cs & TACS_EOF) /* save EOF */ | |
| uptr->UST |= UST_GAP; | |
| if (uptr->flags & UNIT_ATT) /* attached? */ | |
| ta_cs &= ~TACS_EMP; | |
| else ta_cs |= TACS_EMP|TACS_RDY; /* no, empty, ready */ | |
| if ((ta_cs & TACS_IE) && /* int enabled? */ | |
| (ta_cs & (TACS_TR|TACS_RDY))) /* req or ready? */ | |
| SET_INT (TA); /* set int req */ | |
| else CLR_INT (TA); /* no, clr int req */ | |
| return ta_cs; | |
| } | |
| /* Set transfer request */ | |
| void ta_set_tr (void) | |
| { | |
| if (ta_cs & TACS_TR) /* flag still set? */ | |
| ta_cs |= (TACS_ERR|TACS_TIM); | |
| else ta_cs |= TACS_TR; /* set xfr req */ | |
| if (ta_cs & TACS_IE) /* if ie, int req */ | |
| SET_INT (TA); | |
| return; | |
| } | |
| /* Test if controller busy */ | |
| UNIT *ta_busy (void) | |
| { | |
| uint32 u; | |
| UNIT *uptr; | |
| for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */ | |
| uptr = ta_dev.units + u; | |
| if (sim_is_active (uptr)) | |
| return uptr; | |
| } | |
| return NULL; | |
| } | |
| /* Calculate CRC on buffer */ | |
| uint32 ta_crc (uint8 *buf, uint32 cnt) | |
| { | |
| uint32 crc, i, j; | |
| crc = 0; | |
| for (i = 0; i < cnt; i++) { | |
| crc = crc ^ (((uint32) buf[i]) << 8); | |
| for (j = 0; j < 8; j++) { | |
| if (crc & 1) | |
| crc = (crc >> 1) ^ 0xA001; | |
| else crc = crc >> 1; | |
| } | |
| } | |
| return crc; | |
| } | |
| /* Map error status */ | |
| t_stat ta_map_err (UNIT *uptr, t_stat st) | |
| { | |
| switch (st) { | |
| case MTSE_FMT: /* illegal fmt */ | |
| case MTSE_UNATT: /* unattached */ | |
| ta_cs |= TACS_ERR|TACS_CRC; | |
| case MTSE_OK: /* no error */ | |
| return SCPE_IERR; /* never get here! */ | |
| case MTSE_TMK: /* end of file */ | |
| ta_cs |= TACS_ERR|TACS_EOF; | |
| break; | |
| case MTSE_IOERR: /* IO error */ | |
| ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ | |
| if (ta_stopioe) | |
| return SCPE_IOERR; | |
| break; | |
| case MTSE_INVRL: /* invalid rec lnt */ | |
| ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ | |
| return SCPE_MTRLNT; | |
| case MTSE_RECE: /* record in error */ | |
| case MTSE_EOM: /* end of medium */ | |
| ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ | |
| break; | |
| case MTSE_BOT: /* reverse into BOT */ | |
| ta_cs |= TACS_ERR|TACS_BEOT; /* set bot */ | |
| break; | |
| case MTSE_WRP: /* write protect */ | |
| ta_cs |= TACS_ERR|TACS_WLK; /* set wlk err */ | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat ta_reset (DEVICE *dptr) | |
| { | |
| uint32 u; | |
| UNIT *uptr; | |
| ta_cs = 0; | |
| ta_idb = 0; | |
| ta_odb = 0; | |
| ta_write = 0; | |
| ta_bptr = 0; | |
| ta_blnt = 0; | |
| CLR_INT (TA); /* clear interrupt */ | |
| for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */ | |
| uptr = ta_dev.units + u; | |
| sim_cancel (uptr); /* cancel activity */ | |
| sim_tape_reset (uptr); /* reset tape */ | |
| } | |
| if (ta_xb == NULL) | |
| ta_xb = (uint8 *) calloc (TA_MAXFR + 2, sizeof (uint8)); | |
| if (ta_xb == NULL) | |
| return SCPE_MEM; | |
| return auto_config (0, 0); | |
| } | |
| /* Attach routine */ | |
| t_stat ta_attach (UNIT *uptr, char *cptr) | |
| { | |
| t_stat r; | |
| r = sim_tape_attach (uptr, cptr); | |
| if (r != SCPE_OK) | |
| return r; | |
| ta_updsta (NULL); | |
| uptr->UST = 0; | |
| return r; | |
| } | |
| /* Detach routine */ | |
| t_stat ta_detach (UNIT* uptr) | |
| { | |
| t_stat r; | |
| if (!(uptr->flags & UNIT_ATT)) /* check attached */ | |
| return SCPE_OK; | |
| r = sim_tape_detach (uptr); | |
| ta_updsta (NULL); | |
| uptr->UST = 0; | |
| return r; | |
| } |