| 1 | /* h316_mt.c: H316/516 magnetic tape simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2003-2007, 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 516-4100 seven track magnetic tape\r |
| 27 | \r |
| 28 | 09-Jun-07 RMS Fixed bug in write without stop (from Theo Engel)\r |
| 29 | 16-Feb-06 RMS Added tape capacity checking\r |
| 30 | 26-Aug-05 RMS Revised to use API for write lock check\r |
| 31 | 08-Feb-05 RMS Fixed error reporting from OCP (found by Philipp Hachtmann)\r |
| 32 | 01-Dec-04 RMS Fixed bug in DMA/DMC support\r |
| 33 | \r |
| 34 | Magnetic tapes are represented as a series of variable records\r |
| 35 | of the form:\r |
| 36 | \r |
| 37 | 32b byte count\r |
| 38 | byte 0\r |
| 39 | byte 1\r |
| 40 | :\r |
| 41 | byte n-2\r |
| 42 | byte n-1\r |
| 43 | 32b byte count\r |
| 44 | \r |
| 45 | If the byte count is odd, the record is padded with an extra byte\r |
| 46 | of junk. File marks are represented by a byte count of 0.\r |
| 47 | */\r |
| 48 | \r |
| 49 | #include "h316_defs.h"\r |
| 50 | #include "sim_tape.h"\r |
| 51 | \r |
| 52 | #define MT_NUMDR 4 /* number of drives */\r |
| 53 | #define DB_N_SIZE 16 /* max data buf */\r |
| 54 | #define DBSIZE (1 << DB_N_SIZE) /* max data cmd */\r |
| 55 | #define FNC u3 /* function */\r |
| 56 | #define UST u4 /* unit status */\r |
| 57 | \r |
| 58 | /* Function codes */\r |
| 59 | \r |
| 60 | #define FNC_RBCD2 000\r |
| 61 | #define FNC_RBIN2 001\r |
| 62 | #define FNC_RBIN3 002\r |
| 63 | #define FNC_DMANM 003\r |
| 64 | #define FNC_WBCD2 004\r |
| 65 | #define FNC_WBIN2 005\r |
| 66 | #define FNC_WEOF 006\r |
| 67 | #define FNC_IOBUS 007\r |
| 68 | #define FNC_WBIN3 010\r |
| 69 | #define FNC_FSR 011\r |
| 70 | #define FNC_FSF 012\r |
| 71 | #define FNC_DMAAU 013\r |
| 72 | #define FNC_REW 014\r |
| 73 | #define FNC_BSR 015\r |
| 74 | #define FNC_BSF 016\r |
| 75 | #define FNC_STOPW 017\r |
| 76 | #define FNC_2ND 020 /* second state */\r |
| 77 | #define FNC_NOP (FNC_STOPW|FNC_2ND)\r |
| 78 | #define FNC_EOM 040 /* end of motion */\r |
| 79 | \r |
| 80 | /* Status - unit.UST */\r |
| 81 | \r |
| 82 | #define STA_BOT 0000002 /* beg of tape */\r |
| 83 | #define STA_EOT 0000001 /* end of tape */\r |
| 84 | \r |
| 85 | extern int32 dev_int, dev_enb, chan_req;\r |
| 86 | extern int32 stop_inst;\r |
| 87 | \r |
| 88 | uint32 mt_buf = 0; /* data buffer */\r |
| 89 | uint32 mt_usel = 0; /* unit select */\r |
| 90 | uint32 mt_busy = 0; /* ctlr busy */\r |
| 91 | uint32 mt_mdirq = 0; /* motion done int req */\r |
| 92 | uint32 mt_rdy = 0; /* transfer ready (int) */\r |
| 93 | uint32 mt_err = 0; /* error */\r |
| 94 | uint32 mt_eof = 0; /* end of file */\r |
| 95 | uint32 mt_eor = 0; /* transfer done */\r |
| 96 | uint32 mt_dma = 0; /* DMA/DMC */\r |
| 97 | uint32 mt_xtime = 16; /* transfer time */\r |
| 98 | uint32 mt_ctime = 3000; /* start/stop time */\r |
| 99 | uint32 mt_stopioe = 1; /* stop on I/O error */\r |
| 100 | uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */\r |
| 101 | t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */\r |
| 102 | \r |
| 103 | int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev);\r |
| 104 | void mt_updint (uint32 rdy, uint32 mdone);\r |
| 105 | t_stat mt_svc (UNIT *uptr);\r |
| 106 | t_stat mt_reset (DEVICE *dptr);\r |
| 107 | t_stat mt_attach (UNIT *uptr, char *cptr);\r |
| 108 | t_stat mt_detach (UNIT *uptr);\r |
| 109 | t_stat mt_map_err (UNIT *uptr, t_stat st);\r |
| 110 | void mt_wrwd (UNIT *uptr, uint32 dat);\r |
| 111 | \r |
| 112 | /* MT data structures\r |
| 113 | \r |
| 114 | mt_dev MT device descriptor\r |
| 115 | mt_unit MT unit list\r |
| 116 | mt_reg MT register list\r |
| 117 | mt_mod MT modifier list\r |
| 118 | */\r |
| 119 | \r |
| 120 | DIB mt_dib = { MT, IOBUS, MT_NUMDR, &mtio };\r |
| 121 | \r |
| 122 | UNIT mt_unit[] = {\r |
| 123 | { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) },\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 | };\r |
| 128 | \r |
| 129 | REG mt_reg[] = {\r |
| 130 | { ORDATA (BUF, mt_buf, 16) },\r |
| 131 | { ORDATA (USEL, mt_usel, 2) },\r |
| 132 | { FLDATA (BUSY, mt_busy, 0) },\r |
| 133 | { FLDATA (RDY, mt_rdy, 0) },\r |
| 134 | { FLDATA (ERR, mt_err, 0) },\r |
| 135 | { FLDATA (EOF, mt_eof, 0) },\r |
| 136 | { FLDATA (EOR, mt_eor, 0) },\r |
| 137 | { FLDATA (MDIRQ, mt_mdirq, 0) },\r |
| 138 | { FLDATA (DMA, mt_dma, 0) },\r |
| 139 | { FLDATA (INTREQ, dev_int, INT_V_MT) },\r |
| 140 | { FLDATA (ENABLE, dev_enb, INT_V_MT) },\r |
| 141 | { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) },\r |
| 142 | { DRDATA (BPTR, mt_ptr, DB_N_SIZE + 1) },\r |
| 143 | { DRDATA (BMAX, mt_max, DB_N_SIZE + 1) },\r |
| 144 | { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT },\r |
| 145 | { DRDATA (XTIME, mt_xtime, 24), REG_NZ + PV_LEFT },\r |
| 146 | { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT) },\r |
| 147 | { URDATA (FNC, mt_unit[0].FNC, 8, 8, 0, MT_NUMDR, REG_HRO) },\r |
| 148 | { URDATA (UST, mt_unit[0].UST, 8, 2, 0, MT_NUMDR, REG_HRO) },\r |
| 149 | { ORDATA (CHAN, mt_dib.chan, 5), REG_HRO },\r |
| 150 | { FLDATA (STOP_IOE, mt_stopioe, 0) },\r |
| 151 | { NULL }\r |
| 152 | };\r |
| 153 | \r |
| 154 | MTAB mt_mod[] = {\r |
| 155 | { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r |
| 156 | { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, \r |
| 157 | { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",\r |
| 158 | &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },\r |
| 159 | { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",\r |
| 160 | &sim_tape_set_capac, &sim_tape_show_capac, NULL },\r |
| 161 | { MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS",\r |
| 162 | &io_set_iobus, NULL, NULL },\r |
| 163 | { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC",\r |
| 164 | &io_set_dmc, NULL, NULL },\r |
| 165 | { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA",\r |
| 166 | &io_set_dma, NULL, NULL },\r |
| 167 | { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,\r |
| 168 | NULL, &io_show_chan, NULL },\r |
| 169 | { 0 }\r |
| 170 | };\r |
| 171 | \r |
| 172 | DEVICE mt_dev = {\r |
| 173 | "MT", mt_unit, mt_reg, mt_mod,\r |
| 174 | MT_NUMDR, 10, 31, 1, 8, 8,\r |
| 175 | NULL, NULL, &mt_reset,\r |
| 176 | NULL, &mt_attach, &mt_detach,\r |
| 177 | &mt_dib, DEV_DISABLE\r |
| 178 | };\r |
| 179 | \r |
| 180 | /* IO routine */\r |
| 181 | \r |
| 182 | int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev)\r |
| 183 | {\r |
| 184 | uint32 i, u = dev & 03;\r |
| 185 | UNIT *uptr = mt_dev.units + u;\r |
| 186 | static uint8 wrt_fnc[16] = { /* >0 = wr, 1 = chan op */\r |
| 187 | 0, 0, 0, 0, 1, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0\r |
| 188 | };\r |
| 189 | \r |
| 190 | switch (inst) { /* case on opcode */\r |
| 191 | \r |
| 192 | case ioOCP:\r |
| 193 | mt_updint (mt_rdy, 0); /* clear motion intr */\r |
| 194 | mt_eof = 0; /* clear eof */\r |
| 195 | switch (fnc) { /* case on function */\r |
| 196 | \r |
| 197 | case FNC_DMANM: /* set DMA/DMC */\r |
| 198 | case FNC_DMAAU:\r |
| 199 | mt_usel = u; /* save unit select */\r |
| 200 | if (mt_dib.chan) mt_dma = 1; /* set DMA if configured */\r |
| 201 | else mt_dma = 0;\r |
| 202 | break;\r |
| 203 | \r |
| 204 | case FNC_IOBUS: /* set IOBUS */\r |
| 205 | mt_usel = u; /* save unit select */\r |
| 206 | mt_dma = 0;\r |
| 207 | break;\r |
| 208 | \r |
| 209 | case FNC_STOPW: /* stop write */\r |
| 210 | mt_usel = u; /* save unit select */\r |
| 211 | mt_updint (0, mt_mdirq); /* clear ready */\r |
| 212 | if (wrt_fnc[uptr->FNC & 017] == 1) /* writing? */\r |
| 213 | mt_eor = 1; /* set transfer done */\r |
| 214 | break; \r |
| 215 | \r |
| 216 | default: /* motion command */\r |
| 217 | if (mt_busy) return dat; /* nop if ctlr busy */\r |
| 218 | mt_eor = 0; /* clr transfer done */\r |
| 219 | mt_err = 0; /* clr error */\r |
| 220 | mt_usel = u; /* save unit select */\r |
| 221 | if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */\r |
| 222 | return (((mt_stopioe? SCPE_UNATT: SCPE_OK) << IOT_V_REASON) | dat);\r |
| 223 | if (sim_is_active (uptr)) return dat; /* nop if busy */\r |
| 224 | if (wrt_fnc[fnc] && sim_tape_wrp (uptr))\r |
| 225 | return ((STOP_MTWRP << IOT_V_REASON) | dat);\r |
| 226 | uptr->FNC = fnc;\r |
| 227 | uptr->UST = 0;\r |
| 228 | mt_busy = 1;\r |
| 229 | for (i = 0; i < MT_NUMDR; i++) /* clear all EOT flags */\r |
| 230 | mt_unit[i].UST = mt_unit[i].UST & ~STA_EOT;\r |
| 231 | sim_activate (uptr, mt_ctime); /* schedule */\r |
| 232 | break;\r |
| 233 | }\r |
| 234 | break;\r |
| 235 | \r |
| 236 | case ioINA: /* INA */\r |
| 237 | if (fnc) return IOBADFNC (dat); /* fnc 0 only */\r |
| 238 | if (mt_rdy) { /* ready? */\r |
| 239 | mt_rdy = 0; /* clear ready */\r |
| 240 | return IOSKIP (dat | mt_buf); /* ret buf, skip */\r |
| 241 | }\r |
| 242 | break;\r |
| 243 | \r |
| 244 | case ioOTA: /* OTA */\r |
| 245 | if (fnc) return IOBADFNC (dat); /* fnc 0 only */\r |
| 246 | if (mt_rdy) { /* ready? */\r |
| 247 | mt_rdy = 0; /* clear ready */\r |
| 248 | mt_buf = dat; /* store buf */\r |
| 249 | return IOSKIP (dat); /* skip */\r |
| 250 | }\r |
| 251 | break;\r |
| 252 | \r |
| 253 | case ioSKS:\r |
| 254 | uptr = mt_dev.units + mt_usel; /* use saved unit sel */\r |
| 255 | switch (fnc) {\r |
| 256 | \r |
| 257 | case 000: /* ready */\r |
| 258 | if (mt_rdy) return IOSKIP (dat);\r |
| 259 | break;\r |
| 260 | \r |
| 261 | case 001: /* !busy */\r |
| 262 | if (!mt_busy) return IOSKIP (dat);\r |
| 263 | break;\r |
| 264 | \r |
| 265 | case 002: /* !error */\r |
| 266 | if (!mt_err) return IOSKIP (dat);\r |
| 267 | break;\r |
| 268 | \r |
| 269 | case 003: /* !BOT */\r |
| 270 | if (!(uptr->UST & STA_BOT)) return IOSKIP (dat);\r |
| 271 | break;\r |
| 272 | \r |
| 273 | case 004: /* !interrupting */\r |
| 274 | if (!TST_INTREQ (INT_MT)) return IOSKIP (dat);\r |
| 275 | break;\r |
| 276 | \r |
| 277 | case 005: /* !EOT */\r |
| 278 | if (!(uptr->UST & STA_EOT)) return IOSKIP (dat);\r |
| 279 | break;\r |
| 280 | \r |
| 281 | case 006: /* !EOF */\r |
| 282 | if (!mt_eof) return IOSKIP (dat);\r |
| 283 | break;\r |
| 284 | \r |
| 285 | case 007: /* !write prot */\r |
| 286 | if (!sim_tape_wrp (uptr)) return IOSKIP (dat);\r |
| 287 | break;\r |
| 288 | \r |
| 289 | case 011: /* operational */\r |
| 290 | if ((uptr->flags & UNIT_ATT) &&\r |
| 291 | ((uptr->FNC & 017) != FNC_REW)) return IOSKIP (dat);\r |
| 292 | break;\r |
| 293 | \r |
| 294 | case 012: /* skip if !chan 2 */\r |
| 295 | return IOSKIP (dat);\r |
| 296 | \r |
| 297 | case 013: /* skip if !auto */\r |
| 298 | return IOSKIP (dat);\r |
| 299 | \r |
| 300 | case 014: /* !rewinding */\r |
| 301 | uptr = mt_dev.units + (dev & 03); /* use specified unit */\r |
| 302 | if ((uptr->FNC & 017) != FNC_REW) return IOSKIP (dat);\r |
| 303 | break;\r |
| 304 | }\r |
| 305 | break;\r |
| 306 | \r |
| 307 | case ioEND: /* end of range */\r |
| 308 | mt_eor = 1; /* transfer done */\r |
| 309 | break;\r |
| 310 | }\r |
| 311 | \r |
| 312 | return dat;\r |
| 313 | }\r |
| 314 | \r |
| 315 | /* Unit service\r |
| 316 | \r |
| 317 | If rewind done, reposition to start of tape, set status\r |
| 318 | else, do operation, set done, interrupt\r |
| 319 | \r |
| 320 | Can't be write locked, can only write lock detached unit\r |
| 321 | */\r |
| 322 | \r |
| 323 | t_stat mt_svc (UNIT *uptr)\r |
| 324 | {\r |
| 325 | int32 ch = mt_dib.chan - 1; /* DMA/DMC ch */\r |
| 326 | uint32 i, c1, c2, c3;\r |
| 327 | t_mtrlnt tbc;\r |
| 328 | t_bool passed_eot;\r |
| 329 | t_stat st, r = SCPE_OK;\r |
| 330 | \r |
| 331 | if ((uptr->flags & UNIT_ATT) == 0) { /* offline? */\r |
| 332 | mt_err = 1;\r |
| 333 | mt_busy = 0;\r |
| 334 | mt_updint (0, 1); /* cmd done */\r |
| 335 | return IORETURN (mt_stopioe, SCPE_UNATT);\r |
| 336 | }\r |
| 337 | \r |
| 338 | passed_eot = sim_tape_eot (uptr); /* passed EOT? */\r |
| 339 | switch (uptr->FNC) { /* case on function */\r |
| 340 | \r |
| 341 | case FNC_REW: /* rewind (initial) */\r |
| 342 | mt_busy = 0; /* ctlr not busy */\r |
| 343 | uptr->FNC = uptr->FNC | FNC_2ND;\r |
| 344 | sim_activate (uptr, mt_ctime);\r |
| 345 | return SCPE_OK; /* continue */\r |
| 346 | \r |
| 347 | case FNC_REW | FNC_2ND: /* rewind done */\r |
| 348 | uptr->pos = 0; /* reposition file */\r |
| 349 | uptr->UST = STA_BOT; /* set BOT */\r |
| 350 | uptr->FNC = FNC_NOP; /* nop function */\r |
| 351 | for (i = 0; i < MT_NUMDR; i++) { /* last rewind? */\r |
| 352 | if ((mt_unit[i].FNC & 017) == FNC_REW) return SCPE_OK;\r |
| 353 | }\r |
| 354 | mt_updint (mt_rdy, 1); /* yes, motion done */\r |
| 355 | return SCPE_OK;\r |
| 356 | \r |
| 357 | case FNC_WEOF: /* write file mark */\r |
| 358 | if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */\r |
| 359 | r = mt_map_err (uptr, st); /* map error */\r |
| 360 | break; /* sched end motion */\r |
| 361 | \r |
| 362 | case FNC_FSR: /* space fwd rec */\r |
| 363 | if (st = sim_tape_sprecf (uptr, &tbc)) /* space fwd, err? */\r |
| 364 | r = mt_map_err (uptr, st); /* map error */\r |
| 365 | break; /* sched end motion */\r |
| 366 | \r |
| 367 | case FNC_BSR: /* space rev rec */\r |
| 368 | if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */\r |
| 369 | r = mt_map_err (uptr, st); /* map error */\r |
| 370 | break; /* sched end motion */\r |
| 371 | \r |
| 372 | case FNC_FSF: /* space fwd file */\r |
| 373 | while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;\r |
| 374 | r = mt_map_err (uptr, st); /* map error */\r |
| 375 | break; /* sched end motion */\r |
| 376 | \r |
| 377 | case FNC_BSF: /* space rev file */\r |
| 378 | while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;\r |
| 379 | r = mt_map_err (uptr, st); /* map error */\r |
| 380 | break; /* sched end motion */\r |
| 381 | \r |
| 382 | case FNC_EOM: /* end of motion */\r |
| 383 | uptr->FNC = FNC_NOP; /* nop function */\r |
| 384 | mt_busy = 0; /* not busy */\r |
| 385 | mt_updint (mt_rdy, 1); /* end of motion */\r |
| 386 | return SCPE_OK; /* done! */\r |
| 387 | \r |
| 388 | case FNC_RBCD2: case FNC_RBIN2: case FNC_RBIN3: /* read first */\r |
| 389 | mt_ptr = 0; /* clr buf ptr */\r |
| 390 | st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */\r |
| 391 | if (st != MTSE_OK) { /* error? */\r |
| 392 | r = mt_map_err (uptr, st); /* map error */\r |
| 393 | break; /* sched end motion */\r |
| 394 | }\r |
| 395 | uptr->FNC = uptr->FNC | FNC_2ND; /* next state */\r |
| 396 | sim_activate (uptr, mt_xtime); /* sched xfer */\r |
| 397 | return SCPE_OK;\r |
| 398 | \r |
| 399 | case FNC_RBCD2 | FNC_2ND: /* read, word */\r |
| 400 | case FNC_RBIN2 | FNC_2ND:\r |
| 401 | case FNC_RBIN3 | FNC_2ND:\r |
| 402 | if (mt_ptr >= mt_max) break; /* record done? */\r |
| 403 | c1 = mtxb[mt_ptr++] & 077; /* get 2 chars */\r |
| 404 | c2 = mtxb[mt_ptr++] & 077;\r |
| 405 | if (uptr->FNC == (FNC_RBCD2 | FNC_2ND)) { /* BCD? */\r |
| 406 | if (c1 == 012) c1 = 0; /* change 12 to 0 */\r |
| 407 | if (c2 == 012) c2 = 0;\r |
| 408 | }\r |
| 409 | if (uptr->FNC == (FNC_RBIN3 | FNC_2ND)) { /* read 3? */\r |
| 410 | if (mt_ptr >= mt_max) break; /* lose wd if not enuf */\r |
| 411 | c3 = mtxb[mt_ptr++] & 017; /* get 3rd char */\r |
| 412 | }\r |
| 413 | else c3 = 0;\r |
| 414 | sim_activate (uptr, mt_xtime); /* no, sched word */\r |
| 415 | if (mt_eor) return SCPE_OK; /* xfer done? */\r |
| 416 | mt_buf = (c1 << 10) | (c2 << 4) | c3; /* pack chars */\r |
| 417 | if (mt_rdy) mt_err = 1; /* buf full? err */\r |
| 418 | mt_updint (1, mt_mdirq); /* set ready */\r |
| 419 | if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */\r |
| 420 | return SCPE_OK; /* continue */\r |
| 421 | \r |
| 422 | case FNC_WBCD2: case FNC_WBIN2: case FNC_WBIN3: /* write first */\r |
| 423 | mt_ptr = 0; /* clear buf ptr */\r |
| 424 | mt_updint (1, mt_mdirq); /* set ready */\r |
| 425 | if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */\r |
| 426 | uptr->FNC = uptr->FNC | FNC_2ND; /* next state */\r |
| 427 | sim_activate (uptr, mt_xtime); /* sched xfer */\r |
| 428 | return SCPE_OK; /* continue */\r |
| 429 | \r |
| 430 | case FNC_WBCD2 | FNC_2ND: /* write, word */\r |
| 431 | case FNC_WBIN2 | FNC_2ND:\r |
| 432 | case FNC_WBIN3 | FNC_2ND:\r |
| 433 | if (mt_eor || mt_rdy) { /* done or no data? */\r |
| 434 | if (!mt_rdy) mt_wrwd (uptr, mt_buf); /* write last word */\r |
| 435 | else mt_rdy = 0; /* rdy must be clr */\r |
| 436 | if (mt_ptr) { /* any data? */\r |
| 437 | if (st = sim_tape_wrrecf (uptr, mtxb, mt_ptr)) /* write, err? */\r |
| 438 | r = mt_map_err (uptr, st); /* map error */\r |
| 439 | }\r |
| 440 | break; /* sched end motion */\r |
| 441 | }\r |
| 442 | mt_wrwd (uptr, mt_buf); /* write word */\r |
| 443 | sim_activate (uptr, mt_xtime); /* no, sched word */\r |
| 444 | mt_updint (1, mt_mdirq); /* set ready */\r |
| 445 | if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */\r |
| 446 | return SCPE_OK; /* continue */\r |
| 447 | \r |
| 448 | default: /* unknown */\r |
| 449 | break;\r |
| 450 | }\r |
| 451 | \r |
| 452 | /* End of command, process error or schedule end of motion */\r |
| 453 | \r |
| 454 | if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */\r |
| 455 | uptr->UST = uptr->UST | STA_EOT;\r |
| 456 | if (r != SCPE_OK) {\r |
| 457 | uptr->FNC = FNC_NOP; /* nop function */\r |
| 458 | mt_busy = 0; /* not busy */\r |
| 459 | mt_updint (mt_rdy, 1); /* end of motion */\r |
| 460 | return r;\r |
| 461 | }\r |
| 462 | uptr->FNC = FNC_EOM; /* sched end motion */\r |
| 463 | sim_activate (uptr, mt_ctime);\r |
| 464 | return SCPE_OK;\r |
| 465 | }\r |
| 466 | \r |
| 467 | /* Write word to buffer */\r |
| 468 | \r |
| 469 | void mt_wrwd (UNIT *uptr, uint32 dat)\r |
| 470 | {\r |
| 471 | uint32 c1, c2;\r |
| 472 | \r |
| 473 | c1 = (dat >> 10) & 077; /* get 2 chars */\r |
| 474 | c2 = (dat >> 4) & 077;\r |
| 475 | if (uptr->FNC == (FNC_WBCD2 | FNC_2ND)) { /* BCD? */\r |
| 476 | if (c1 == 0) c1 = 012; /* change 0 to 12 */\r |
| 477 | if (c2 == 0) c2 = 012;\r |
| 478 | }\r |
| 479 | if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c1; /* store 2 char */\r |
| 480 | if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c2;\r |
| 481 | if ((uptr->FNC == (FNC_WBIN3 | FNC_2ND)) && /* write 3? */\r |
| 482 | (mt_ptr < DBSIZE)) mtxb[mt_ptr++] = mt_buf & 017;\r |
| 483 | return;\r |
| 484 | }\r |
| 485 | \r |
| 486 | /* Map tape error status */\r |
| 487 | \r |
| 488 | t_stat mt_map_err (UNIT *uptr, t_stat st)\r |
| 489 | {\r |
| 490 | switch (st) {\r |
| 491 | \r |
| 492 | case MTSE_FMT: /* illegal fmt */\r |
| 493 | case MTSE_UNATT: /* unattached */\r |
| 494 | mt_err = 1; /* reject */\r |
| 495 | case MTSE_OK: /* no error */\r |
| 496 | return SCPE_IERR; /* never get here! */\r |
| 497 | \r |
| 498 | case MTSE_TMK: /* end of file */\r |
| 499 | mt_eof = 1; /* eof */\r |
| 500 | break;\r |
| 501 | \r |
| 502 | case MTSE_INVRL: /* invalid rec lnt */\r |
| 503 | mt_err = 1;\r |
| 504 | return SCPE_MTRLNT;\r |
| 505 | \r |
| 506 | case MTSE_IOERR: /* IO error */\r |
| 507 | mt_err = 1; /* error */\r |
| 508 | if (mt_stopioe) return SCPE_IOERR;\r |
| 509 | break;\r |
| 510 | \r |
| 511 | case MTSE_RECE: /* record in error */\r |
| 512 | case MTSE_EOM: /* end of medium */\r |
| 513 | mt_err = 1; /* error */\r |
| 514 | break;\r |
| 515 | \r |
| 516 | case MTSE_BOT: /* reverse into BOT */\r |
| 517 | uptr->UST = STA_BOT; /* set status */\r |
| 518 | break;\r |
| 519 | \r |
| 520 | case MTSE_WRP: /* write protect */\r |
| 521 | mt_err = 1; /* error */\r |
| 522 | return STOP_MTWRP;\r |
| 523 | }\r |
| 524 | \r |
| 525 | return SCPE_OK;\r |
| 526 | }\r |
| 527 | \r |
| 528 | /* Update interrupts */\r |
| 529 | \r |
| 530 | void mt_updint (uint32 rdy, uint32 mdirq)\r |
| 531 | {\r |
| 532 | mt_rdy = rdy; /* store new ready */\r |
| 533 | mt_mdirq = mdirq; /* store new motion irq */\r |
| 534 | if ((mt_rdy && !mt_dma) || mt_mdirq) SET_INT (INT_MT); /* update int request */\r |
| 535 | else CLR_INT (INT_MT);\r |
| 536 | return;\r |
| 537 | }\r |
| 538 | \r |
| 539 | /* Reset routine */\r |
| 540 | \r |
| 541 | t_stat mt_reset (DEVICE *dptr)\r |
| 542 | {\r |
| 543 | int32 i;\r |
| 544 | UNIT *uptr;\r |
| 545 | \r |
| 546 | mt_buf = 0; /* clear state */\r |
| 547 | mt_usel = 0;\r |
| 548 | mt_mdirq = 0;\r |
| 549 | mt_eor = 0;\r |
| 550 | mt_busy = 0;\r |
| 551 | mt_rdy = 0;\r |
| 552 | mt_eof = 0;\r |
| 553 | mt_err = 0;\r |
| 554 | mt_dma = 0;\r |
| 555 | CLR_INT (INT_MT); /* clear int, enb */\r |
| 556 | CLR_ENB (INT_MT);\r |
| 557 | for (i = 0; i < MT_NUMDR; i++) { /* loop thru units */\r |
| 558 | uptr = mt_dev.units + i;\r |
| 559 | sim_tape_reset (uptr); /* reset tape */\r |
| 560 | sim_cancel (uptr); /* cancel op */\r |
| 561 | uptr->UST = uptr->pos? 0: STA_BOT; /* update status */\r |
| 562 | uptr->FNC = FNC_NOP;\r |
| 563 | }\r |
| 564 | return SCPE_OK;\r |
| 565 | }\r |
| 566 | \r |
| 567 | /* Attach routine */\r |
| 568 | \r |
| 569 | t_stat mt_attach (UNIT *uptr, char *cptr)\r |
| 570 | {\r |
| 571 | t_stat r;\r |
| 572 | \r |
| 573 | r = sim_tape_attach (uptr, cptr); /* attach unit */\r |
| 574 | if (r != SCPE_OK) return r; /* update status */\r |
| 575 | uptr->UST = STA_BOT;\r |
| 576 | return r;\r |
| 577 | }\r |
| 578 | \r |
| 579 | /* Detach routine */\r |
| 580 | \r |
| 581 | t_stat mt_detach (UNIT* uptr)\r |
| 582 | {\r |
| 583 | uptr->UST = 0; /* update status */\r |
| 584 | uptr->FNC = FNC_NOP; /* nop function */\r |
| 585 | return sim_tape_detach (uptr); /* detach unit */\r |
| 586 | }\r |