| 1 | /* id_mt.c: Interdata magnetic tape simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2001-2006, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | mt M46-494 dual density 9-track magtape controller\r |
| 27 | \r |
| 28 | 16-Feb-06 RMS Added tape capacity checking\r |
| 29 | 18-Mar-05 RMS Added attached test to detach routine\r |
| 30 | 07-Dec-04 RMS Added read-only file support\r |
| 31 | 25-Apr-03 RMS Revised for extended file support\r |
| 32 | 28-Mar-03 RMS Added multiformat support\r |
| 33 | 28-Feb-03 RMS Revised for magtape library\r |
| 34 | 20-Feb-03 RMS Fixed read to stop selch on error\r |
| 35 | \r |
| 36 | Magnetic tapes are represented as a series of variable 8b records\r |
| 37 | of the form:\r |
| 38 | \r |
| 39 | 32b record length in bytes - exact number\r |
| 40 | byte 0\r |
| 41 | byte 1\r |
| 42 | :\r |
| 43 | byte n-2\r |
| 44 | byte n-1\r |
| 45 | 32b record length in bytes - exact number\r |
| 46 | \r |
| 47 | If the byte count is odd, the record is padded with an extra byte\r |
| 48 | of junk. File marks are represented by a single record length of 0.\r |
| 49 | End of tape is two consecutive end of file marks.\r |
| 50 | */\r |
| 51 | \r |
| 52 | #include "id_defs.h"\r |
| 53 | #include "sim_tape.h"\r |
| 54 | \r |
| 55 | #define UST u3 /* unit status */\r |
| 56 | #define UCMD u4 /* unit command */\r |
| 57 | #define MT_MAXFR (1 << 24) /* max transfer */\r |
| 58 | \r |
| 59 | /* Command - in UCMD */\r |
| 60 | \r |
| 61 | #define MTC_SPCR 0x11 /* backspace */\r |
| 62 | #define MTC_SKFR 0x13 /* space file rev */\r |
| 63 | #define MTC_CLR 0x20 /* clear */\r |
| 64 | #define MTC_RD 0x21 /* read */\r |
| 65 | #define MTC_WR 0x22 /* write */\r |
| 66 | #define MTC_SKFF 0x23 /* space file fwd */\r |
| 67 | #define MTC_WEOF 0x30 /* write eof */\r |
| 68 | #define MTC_REW 0x38 /* rewind */\r |
| 69 | #define MTC_MASK 0x3F\r |
| 70 | #define MTC_STOP1 0x40 /* stop, set EOM */\r |
| 71 | #define MTC_STOP2 0x80 /* stop, set NMTN */\r |
| 72 | \r |
| 73 | /* Status byte, * = in UST */\r |
| 74 | \r |
| 75 | #define STA_ERR 0x80 /* error */\r |
| 76 | #define STA_EOF 0x40 /* end of file */\r |
| 77 | #define STA_EOT 0x20 /* *end of tape */\r |
| 78 | #define STA_NMTN 0x10 /* *no motion */\r |
| 79 | #define STA_UFLGS (STA_EOT|STA_NMTN) /* unit flags */\r |
| 80 | #define STA_MASK (STA_ERR|STA_EOF|STA_BSY|STA_EOM)\r |
| 81 | #define SET_EX (STA_ERR|STA_EOF|STA_NMTN)\r |
| 82 | \r |
| 83 | extern uint32 int_req[INTSZ], int_enb[INTSZ];\r |
| 84 | \r |
| 85 | uint8 mtxb[MT_MAXFR]; /* xfer buffer */\r |
| 86 | uint32 mt_bptr = 0; /* pointer */\r |
| 87 | uint32 mt_blnt = 0; /* length */\r |
| 88 | uint32 mt_sta = 0; /* status byte */\r |
| 89 | uint32 mt_db = 0; /* data buffer */\r |
| 90 | uint32 mt_xfr = 0; /* data xfr in prog */\r |
| 91 | uint32 mt_arm[MT_NUMDR] = { 0 }; /* intr armed */\r |
| 92 | int32 mt_wtime = 10; /* byte latency */\r |
| 93 | int32 mt_rtime = 1000; /* record latency */\r |
| 94 | int32 mt_stopioe = 1; /* stop on error */\r |
| 95 | uint8 mt_tplte[] = { 0, o_MT0, o_MT0*2, o_MT0*3, TPL_END };\r |
| 96 | \r |
| 97 | static const uint8 bad_cmd[64] = {\r |
| 98 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r |
| 99 | 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r |
| 100 | 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r |
| 101 | 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1\r |
| 102 | };\r |
| 103 | \r |
| 104 | DEVICE mt_dev;\r |
| 105 | uint32 mt (uint32 dev, uint32 op, uint32 dat);\r |
| 106 | t_stat mt_svc (UNIT *uptr);\r |
| 107 | t_stat mt_reset (DEVICE *dptr);\r |
| 108 | t_stat mt_attach (UNIT *uptr, char *cptr);\r |
| 109 | t_stat mt_detach (UNIT *uptr);\r |
| 110 | t_stat mt_boot (int32 unitno, DEVICE *dptr);\r |
| 111 | t_stat mt_map_err (UNIT *uptr, t_stat st);\r |
| 112 | \r |
| 113 | /* MT data structures\r |
| 114 | \r |
| 115 | mt_dev MT device descriptor\r |
| 116 | mt_unit MT unit list\r |
| 117 | mt_reg MT register list\r |
| 118 | mt_mod MT modifier list\r |
| 119 | */\r |
| 120 | \r |
| 121 | DIB mt_dib = { d_MT, 0, v_MT, mt_tplte, &mt, NULL };\r |
| 122 | \r |
| 123 | UNIT mt_unit[] = {\r |
| 124 | { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r |
| 125 | { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r |
| 126 | { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\r |
| 127 | { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }\r |
| 128 | };\r |
| 129 | \r |
| 130 | REG mt_reg[] = {\r |
| 131 | { HRDATA (STA, mt_sta, 8) },\r |
| 132 | { HRDATA (BUF, mt_db, 8) },\r |
| 133 | { BRDATA (DBUF, mtxb, 16, 8, MT_MAXFR) },\r |
| 134 | { HRDATA (DBPTR, mt_bptr, 16) },\r |
| 135 | { HRDATA (DBLNT, mt_blnt, 17), REG_RO },\r |
| 136 | { FLDATA (XFR, mt_xfr, 0) },\r |
| 137 | { GRDATA (IREQ, int_req[l_MT], 16, MT_NUMDR, i_MT) },\r |
| 138 | { GRDATA (IENB, int_enb[l_MT], 16, MT_NUMDR, i_MT) },\r |
| 139 | { BRDATA (IARM, mt_arm, 16, 1, MT_NUMDR) },\r |
| 140 | { FLDATA (STOP_IOE, mt_stopioe, 0) },\r |
| 141 | { DRDATA (WTIME, mt_wtime, 24), PV_LEFT + REG_NZ },\r |
| 142 | { DRDATA (RTIME, mt_rtime, 24), PV_LEFT + REG_NZ },\r |
| 143 | { URDATA (UST, mt_unit[0].UST, 16, 8, 0, MT_NUMDR, 0) },\r |
| 144 | { URDATA (CMD, mt_unit[0].UCMD, 16, 8, 0, MT_NUMDR, 0) },\r |
| 145 | { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0,\r |
| 146 | MT_NUMDR, PV_LEFT | REG_RO) },\r |
| 147 | { HRDATA (DEVNO, mt_dib.dno, 8), REG_HRO },\r |
| 148 | { HRDATA (SELCH, mt_dib.sch, 1), REG_HRO },\r |
| 149 | { NULL }\r |
| 150 | };\r |
| 151 | \r |
| 152 | MTAB mt_mod[] = {\r |
| 153 | { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r |
| 154 | { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },\r |
| 155 | { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",\r |
| 156 | &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },\r |
| 157 | { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",\r |
| 158 | &sim_tape_set_capac, &sim_tape_show_capac, NULL },\r |
| 159 | { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",\r |
| 160 | &set_dev, &show_dev, NULL },\r |
| 161 | { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH",\r |
| 162 | &set_sch, &show_sch, NULL },\r |
| 163 | { 0 }\r |
| 164 | };\r |
| 165 | \r |
| 166 | DEVICE mt_dev = {\r |
| 167 | "MT", mt_unit, mt_reg, mt_mod,\r |
| 168 | MT_NUMDR, 10, 31, 1, 16, 8,\r |
| 169 | NULL, NULL, &mt_reset,\r |
| 170 | &mt_boot, &mt_attach, &mt_detach,\r |
| 171 | &mt_dib, DEV_DISABLE\r |
| 172 | };\r |
| 173 | \r |
| 174 | /* Magtape: IO routine */\r |
| 175 | \r |
| 176 | uint32 mt (uint32 dev, uint32 op, uint32 dat)\r |
| 177 | {\r |
| 178 | uint32 i, f, t;\r |
| 179 | uint32 u = (dev - mt_dib.dno) / o_MT0;\r |
| 180 | UNIT *uptr = mt_dev.units + u;\r |
| 181 | \r |
| 182 | switch (op) { /* case IO op */\r |
| 183 | \r |
| 184 | case IO_ADR: /* select */\r |
| 185 | sch_adr (mt_dib.sch, dev); /* inform sel ch */\r |
| 186 | return BY; /* byte only */\r |
| 187 | \r |
| 188 | case IO_RD: /* read data */\r |
| 189 | if (mt_xfr) mt_sta = mt_sta | STA_BSY; /* xfr? set busy */\r |
| 190 | return mt_db; /* return data */\r |
| 191 | \r |
| 192 | case IO_WD: /* write data */\r |
| 193 | if (mt_xfr) { /* transfer? */\r |
| 194 | mt_sta = mt_sta | STA_BSY; /* set busy */\r |
| 195 | if ((uptr->UCMD & (MTC_STOP1 | MTC_STOP2)) &&\r |
| 196 | ((uptr->UCMD & MTC_MASK) == MTC_WR)) /* while stopping? */\r |
| 197 | mt_sta = mt_sta | STA_ERR; /* write overrun */\r |
| 198 | }\r |
| 199 | mt_db = dat & DMASK8; /* store data */\r |
| 200 | break;\r |
| 201 | \r |
| 202 | case IO_SS: /* status */\r |
| 203 | mt_sta = mt_sta & STA_MASK; /* ctrl status */\r |
| 204 | if (uptr->flags & UNIT_ATT) /* attached? */\r |
| 205 | t = mt_sta | (uptr->UST & STA_UFLGS); /* yes, unit status */\r |
| 206 | else t = mt_sta | STA_DU; /* no, dev unavail */\r |
| 207 | if (t & SET_EX) t = t | STA_EX; /* test for ex */\r |
| 208 | return t;\r |
| 209 | \r |
| 210 | case IO_OC: /* command */\r |
| 211 | mt_arm[u] = int_chg (v_MT + u, dat, mt_arm[u]);\r |
| 212 | f = dat & MTC_MASK; /* get cmd */\r |
| 213 | if (f == MTC_CLR) { /* clear? */\r |
| 214 | mt_reset (&mt_dev); /* reset world */\r |
| 215 | break;\r |
| 216 | }\r |
| 217 | if (((uptr->flags & UNIT_ATT) == 0) || /* ignore if unatt */\r |
| 218 | bad_cmd[f] || /* or bad cmd */\r |
| 219 | (((f == MTC_WR) || (f == MTC_WEOF)) && /* or write */\r |
| 220 | sim_tape_wrp (uptr))) break; /* and protected */\r |
| 221 | for (i = 0; i < MT_NUMDR; i++) { /* check other drvs */\r |
| 222 | if (sim_is_active (&mt_unit[i]) && /* active? */\r |
| 223 | (mt_unit[i].UCMD != MTC_REW)) { /* not rewind? */\r |
| 224 | sim_cancel (&mt_unit[i]); /* stop */\r |
| 225 | mt_unit[i].UCMD = 0;\r |
| 226 | }\r |
| 227 | }\r |
| 228 | if (sim_is_active (uptr) && /* unit active? */\r |
| 229 | !(uptr->UCMD & (MTC_STOP1 | MTC_STOP2))) /* not stopping? */\r |
| 230 | break; /* ignore */\r |
| 231 | if ((f == MTC_WR) || (f == MTC_REW)) mt_sta = 0;/* write, rew: bsy=0 */\r |
| 232 | else mt_sta = STA_BSY; /* bsy=1,nmtn,eom,err=0 */\r |
| 233 | mt_bptr = mt_blnt = 0; /* not yet started */\r |
| 234 | if ((f == MTC_RD) || (f == MTC_WR)) /* data xfr? */\r |
| 235 | mt_xfr = 1; /* set xfr flag */\r |
| 236 | else mt_xfr = 0;\r |
| 237 | uptr->UCMD = f; /* save cmd */\r |
| 238 | uptr->UST = 0; /* clr tape stat */\r |
| 239 | sim_activate (uptr, mt_rtime); /* start op */\r |
| 240 | break;\r |
| 241 | }\r |
| 242 | \r |
| 243 | return 0;\r |
| 244 | }\r |
| 245 | \r |
| 246 | /* Unit service\r |
| 247 | \r |
| 248 | A given operation can generate up to three interrupts\r |
| 249 | \r |
| 250 | - EOF generates an interrupt when set (read, space, wreof)\r |
| 251 | BUSY will still be set, EOM and NMTN will be clear\r |
| 252 | - After operation complete + delay, EOM generates an interrupt\r |
| 253 | BUSY will be clear, EOM will be set, NMTN will be clear\r |
| 254 | - After a further delay, NMTN generates an interrupt\r |
| 255 | BUSY will be clear, EOM and NMTN will be set\r |
| 256 | \r |
| 257 | Rewind generates an interrupt when NMTN sets\r |
| 258 | */\r |
| 259 | \r |
| 260 | t_stat mt_svc (UNIT *uptr)\r |
| 261 | {\r |
| 262 | uint32 i;\r |
| 263 | int32 u = uptr - mt_dev.units;\r |
| 264 | uint32 dev = mt_dib.dno + (u * o_MT0);\r |
| 265 | t_mtrlnt tbc;\r |
| 266 | t_bool passed_eot;\r |
| 267 | t_stat st, r = SCPE_OK;\r |
| 268 | \r |
| 269 | if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */\r |
| 270 | uptr->UCMD = 0; /* clr cmd */\r |
| 271 | uptr->UST = 0; /* set status */\r |
| 272 | mt_xfr = 0; /* clr op flags */\r |
| 273 | mt_sta = STA_ERR | STA_EOM; /* set status */\r |
| 274 | if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */\r |
| 275 | return IORETURN (mt_stopioe, SCPE_UNATT);\r |
| 276 | }\r |
| 277 | \r |
| 278 | if (uptr->UCMD & MTC_STOP2) { /* stop, gen NMTN? */\r |
| 279 | uptr->UCMD = 0; /* clr cmd */\r |
| 280 | uptr->UST = uptr->UST | STA_NMTN; /* set nmtn */\r |
| 281 | mt_xfr = 0; /* clr xfr */\r |
| 282 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 283 | return SCPE_OK;\r |
| 284 | }\r |
| 285 | \r |
| 286 | if (uptr->UCMD & MTC_STOP1) { /* stop, gen EOM? */\r |
| 287 | uptr->UCMD = uptr->UCMD | MTC_STOP2; /* clr cmd */\r |
| 288 | mt_sta = (mt_sta & ~STA_BSY) | STA_EOM; /* clr busy, set eom */\r |
| 289 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 290 | sim_activate (uptr, mt_rtime); /* schedule */\r |
| 291 | return SCPE_OK;\r |
| 292 | }\r |
| 293 | \r |
| 294 | passed_eot = sim_tape_eot (uptr); /* passed EOT? */\r |
| 295 | switch (uptr->UCMD) { /* case on function */\r |
| 296 | \r |
| 297 | case MTC_REW: /* rewind */\r |
| 298 | sim_tape_rewind (uptr); /* reposition */\r |
| 299 | uptr->UCMD = 0; /* clr cmd */\r |
| 300 | uptr->UST = STA_NMTN | STA_EOT; /* update status */\r |
| 301 | mt_sta = mt_sta & ~STA_BSY; /* don't set EOM */\r |
| 302 | if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */\r |
| 303 | return SCPE_OK;\r |
| 304 | \r |
| 305 | /* For read, busy = 1 => buffer empty\r |
| 306 | For write, busy = 1 => buffer full\r |
| 307 | For read, data transfers continue for the full length of the\r |
| 308 | record, or the maximum size of the transfer buffer\r |
| 309 | For write, data transfers continue until a write is attempted\r |
| 310 | and the buffer is empty\r |
| 311 | */\r |
| 312 | \r |
| 313 | case MTC_RD: /* read */\r |
| 314 | if (mt_blnt == 0) { /* first time? */\r |
| 315 | st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */\r |
| 316 | if (st == MTSE_RECE) mt_sta = mt_sta | STA_ERR; /* rec in err? */\r |
| 317 | else if (st != SCPE_OK) { /* other error? */\r |
| 318 | r = mt_map_err (uptr, st); /* map error */\r |
| 319 | if (sch_actv (mt_dib.sch, dev)) /* if sch, stop */\r |
| 320 | sch_stop (mt_dib.sch);\r |
| 321 | break;\r |
| 322 | }\r |
| 323 | mt_blnt = tbc; /* set buf lnt */\r |
| 324 | }\r |
| 325 | \r |
| 326 | if (sch_actv (mt_dib.sch, dev)) { /* sch active? */\r |
| 327 | i = sch_wrmem (mt_dib.sch, mtxb, mt_blnt); /* store rec in mem */\r |
| 328 | if (sch_actv (mt_dib.sch, dev)) /* sch still active? */\r |
| 329 | sch_stop (mt_dib.sch); /* stop chan, long rd */\r |
| 330 | else if (i < mt_blnt) /* process entire rec? */\r |
| 331 | mt_sta = mt_sta | STA_ERR; /* no, overrun error */\r |
| 332 | }\r |
| 333 | else if (mt_bptr < mt_blnt) { /* no, if !eor */\r |
| 334 | if (!(mt_sta & STA_BSY)) /* busy still clr? */\r |
| 335 | mt_sta = mt_sta | STA_ERR; /* read overrun */\r |
| 336 | mt_db = mtxb[mt_bptr++]; /* get next byte */\r |
| 337 | mt_sta = mt_sta & ~STA_BSY; /* !busy = buf full */\r |
| 338 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 339 | sim_activate (uptr, mt_wtime); /* reschedule */\r |
| 340 | return SCPE_OK;\r |
| 341 | }\r |
| 342 | break; /* record done */\r |
| 343 | \r |
| 344 | case MTC_WR: /* write */\r |
| 345 | if (sch_actv (mt_dib.sch, dev)) { /* sch active? */\r |
| 346 | mt_bptr = sch_rdmem (mt_dib.sch, mtxb, MT_MAXFR); /* get rec */\r |
| 347 | if (sch_actv (mt_dib.sch, dev)) /* not done? */\r |
| 348 | sch_stop (mt_dib.sch); /* stop chan */\r |
| 349 | }\r |
| 350 | else if (mt_sta & STA_BSY) { /* no, if !eor */\r |
| 351 | if (mt_bptr < MT_MAXFR) /* if room */\r |
| 352 | mtxb[mt_bptr++] = mt_db; /* store in buf */\r |
| 353 | mt_sta = mt_sta & ~STA_BSY; /* !busy = buf emp */\r |
| 354 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 355 | sim_activate (uptr, mt_wtime); /* reschedule */\r |
| 356 | return SCPE_OK;\r |
| 357 | }\r |
| 358 | \r |
| 359 | if (mt_bptr) { /* any chars? */\r |
| 360 | if (st = sim_tape_wrrecf (uptr, mtxb, mt_bptr)) /* write, err? */\r |
| 361 | r = mt_map_err (uptr, st); /* map error */\r |
| 362 | }\r |
| 363 | break; /* record done */\r |
| 364 | \r |
| 365 | case MTC_WEOF: /* write eof */\r |
| 366 | if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */\r |
| 367 | r = mt_map_err (uptr, st); /* map error */\r |
| 368 | mt_sta = mt_sta | STA_EOF; /* set eof */\r |
| 369 | if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */\r |
| 370 | break;\r |
| 371 | \r |
| 372 | case MTC_SKFF: /* skip file fwd */\r |
| 373 | while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;\r |
| 374 | if (st == MTSE_TMK) { /* stopped by tmk? */\r |
| 375 | mt_sta = mt_sta | STA_EOF; /* set eof */\r |
| 376 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 377 | }\r |
| 378 | else r = mt_map_err (uptr, st); /* map error */\r |
| 379 | break;\r |
| 380 | \r |
| 381 | case MTC_SKFR: /* skip file rev */\r |
| 382 | while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;\r |
| 383 | if (st == MTSE_TMK) { /* stopped by tmk? */\r |
| 384 | mt_sta = mt_sta | STA_EOF; /* set eof */\r |
| 385 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 386 | }\r |
| 387 | else r = mt_map_err (uptr, st); /* map error */\r |
| 388 | break;\r |
| 389 | \r |
| 390 | case MTC_SPCR: /* backspace */\r |
| 391 | if (st = sim_tape_sprecr (uptr, &tbc)) /* skip rec rev, err? */\r |
| 392 | r = mt_map_err (uptr, st); /* map error */\r |
| 393 | break;\r |
| 394 | } /* end case */\r |
| 395 | \r |
| 396 | if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */\r |
| 397 | uptr->UST = uptr->UST | STA_EOT;\r |
| 398 | uptr->UCMD = uptr->UCMD | MTC_STOP1; /* set stop stage 1 */\r |
| 399 | sim_activate (uptr, mt_rtime); /* schedule */\r |
| 400 | return r;\r |
| 401 | }\r |
| 402 | \r |
| 403 | /* Map tape error status */\r |
| 404 | \r |
| 405 | t_stat mt_map_err (UNIT *uptr, t_stat st)\r |
| 406 | {\r |
| 407 | int32 u = uptr - mt_dev.units;\r |
| 408 | \r |
| 409 | switch (st) {\r |
| 410 | \r |
| 411 | case MTSE_FMT: /* illegal fmt */\r |
| 412 | case MTSE_UNATT: /* not attached */\r |
| 413 | mt_sta = mt_sta | STA_ERR;\r |
| 414 | case MTSE_OK: /* no error */\r |
| 415 | return SCPE_IERR;\r |
| 416 | \r |
| 417 | case MTSE_TMK: /* end of file */\r |
| 418 | mt_sta = mt_sta | STA_EOF; /* set eof */\r |
| 419 | if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */\r |
| 420 | break;\r |
| 421 | \r |
| 422 | case MTSE_IOERR: /* IO error */\r |
| 423 | mt_sta = mt_sta | STA_ERR; /* set err */\r |
| 424 | if (mt_stopioe) return SCPE_IOERR;\r |
| 425 | break;\r |
| 426 | \r |
| 427 | case MTSE_INVRL: /* invalid rec lnt */\r |
| 428 | mt_sta = mt_sta | STA_ERR;\r |
| 429 | return SCPE_MTRLNT;\r |
| 430 | \r |
| 431 | case MTSE_WRP: /* write protect */\r |
| 432 | case MTSE_RECE: /* record in error */\r |
| 433 | case MTSE_EOM: /* end of medium */\r |
| 434 | mt_sta = mt_sta | STA_ERR; /* set err */\r |
| 435 | break;\r |
| 436 | \r |
| 437 | case MTSE_BOT: /* reverse into BOT */\r |
| 438 | uptr->UST = uptr->UST | STA_EOT; /* set err */\r |
| 439 | break;\r |
| 440 | } /* end switch */\r |
| 441 | \r |
| 442 | return SCPE_OK;\r |
| 443 | }\r |
| 444 | \r |
| 445 | /* Reset routine */\r |
| 446 | \r |
| 447 | t_stat mt_reset (DEVICE *dptr)\r |
| 448 | {\r |
| 449 | uint32 u;\r |
| 450 | UNIT *uptr;\r |
| 451 | \r |
| 452 | mt_bptr = mt_blnt = 0; /* clr buf */\r |
| 453 | mt_sta = STA_BSY; /* clr flags */\r |
| 454 | mt_xfr = 0; /* clr controls */\r |
| 455 | for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */\r |
| 456 | CLR_INT (v_MT + u); /* clear int */\r |
| 457 | CLR_ENB (v_MT + u); /* disable int */\r |
| 458 | mt_arm[u] = 0; /* disarm int */\r |
| 459 | uptr = mt_dev.units + u;\r |
| 460 | sim_tape_reset (uptr); /* clear pos flag */\r |
| 461 | sim_cancel (uptr); /* cancel activity */\r |
| 462 | uptr->UST = (uptr->UST & STA_UFLGS) | STA_NMTN; /* init status */\r |
| 463 | uptr->UCMD = 0; /* init cmd */\r |
| 464 | }\r |
| 465 | return SCPE_OK;\r |
| 466 | }\r |
| 467 | \r |
| 468 | /* Attach routine */\r |
| 469 | \r |
| 470 | t_stat mt_attach (UNIT *uptr, char *cptr)\r |
| 471 | {\r |
| 472 | int32 u = uptr - mt_dev.units;\r |
| 473 | t_stat r;\r |
| 474 | \r |
| 475 | r = sim_tape_attach (uptr, cptr);\r |
| 476 | if (r != SCPE_OK) return r;\r |
| 477 | uptr->UST = STA_EOT;\r |
| 478 | if (mt_arm[u]) SET_INT (v_MT + u);\r |
| 479 | return r;\r |
| 480 | }\r |
| 481 | \r |
| 482 | /* Detach routine */\r |
| 483 | \r |
| 484 | t_stat mt_detach (UNIT* uptr)\r |
| 485 | {\r |
| 486 | int32 u = uptr - mt_dev.units;\r |
| 487 | t_stat r;\r |
| 488 | \r |
| 489 | if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r |
| 490 | r = sim_tape_detach (uptr);\r |
| 491 | if (r != SCPE_OK) return r;\r |
| 492 | if (mt_arm[u]) SET_INT (v_MT + u);\r |
| 493 | uptr->UST = 0;\r |
| 494 | return SCPE_OK;\r |
| 495 | }\r |
| 496 | \r |
| 497 | /* Bootstrap routine */\r |
| 498 | \r |
| 499 | #define BOOT_START 0x50\r |
| 500 | #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8))\r |
| 501 | \r |
| 502 | static uint8 boot_rom[] = {\r |
| 503 | 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */\r |
| 504 | 0x43, 0x00, 0x00, 0x80 /* BR 80 */\r |
| 505 | };\r |
| 506 | \r |
| 507 | t_stat mt_boot (int32 unitno, DEVICE *dptr)\r |
| 508 | {\r |
| 509 | extern uint32 PC, dec_flgs;\r |
| 510 | extern uint16 decrom[];\r |
| 511 | extern DIB sch_dib;\r |
| 512 | uint32 sch_dev;\r |
| 513 | \r |
| 514 | if (decrom[0xD5] & dec_flgs) return SCPE_NOFNC; /* AL defined? */\r |
| 515 | sim_tape_rewind (&mt_unit[unitno]); /* rewind */\r |
| 516 | sch_dev = sch_dib.dno + mt_dib.sch; /* sch dev # */\r |
| 517 | IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */\r |
| 518 | IOWriteB (AL_DEV, mt_dib.dno + (unitno * o_MT0)); /* set dev no for unit */\r |
| 519 | IOWriteB (AL_IOC, 0xA1); /* set dev cmd */\r |
| 520 | IOWriteB (AL_SCH, sch_dev); /* set dev no for chan */\r |
| 521 | PC = BOOT_START;\r |
| 522 | return SCPE_OK;\r |
| 523 | }\r |