First Commit of my working state
[simh.git] / PDP11 / pdp11_tm.c
1 /* pdp11_tm.c: PDP-11 magnetic tape simulator
2
3 Copyright (c) 1993-2006, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 tm TM11/TU10 magtape
27
28 16-Feb-06 RMS Added tape capacity checking
29 31-Oct-05 RMS Fixed address width for large files
30 16-Aug-05 RMS Fixed C++ declaration and cast problems
31 07-Jul-05 RMS Removed extraneous externs
32 18-Mar-05 RMS Added attached test to detach routine
33 07-Dec-04 RMS Added read-only file support
34 30-Sep-04 RMS Revised Unibus interface
35 25-Jan-04 RMS Revised for device debug support
36 29-Dec-03 RMS Added 18b Qbus support
37 25-Apr-03 RMS Revised for extended file support
38 28-Mar-03 RMS Added multiformat support
39 28-Feb-03 RMS Revised for magtape library, added logging
40 30-Oct-02 RMS Revised BOT handling, added error record handling
41 30-Sep-02 RMS Added variable address support to bootstrap
42 Added vector change/display support
43 Changed mapping mnemonics
44 New data structures
45 Updated error handling
46 28-Aug-02 RMS Added end of medium support
47 30-May-02 RMS Widened POS to 32b
48 22-Apr-02 RMS Fixed max record length, first block bootstrap
49 (found by Jonathan Engdahl)
50 26-Jan-02 RMS Revised bootstrap to conform to M9312
51 06-Jan-02 RMS Revised enable/disable support
52 30-Nov-01 RMS Added read only unit, extended SET/SHOW support
53 24-Nov-01 RMS Converted UST, POS, FLG to arrays
54 09-Nov-01 RMS Added bus map support
55 18-Oct-01 RMS Added stub diagnostic register (found by Thord Nilson)
56 07-Sep-01 RMS Revised device disable and interrupt mechanisms
57 26-Apr-01 RMS Added device enable/disable support
58 18-Apr-01 RMS Changed to rewind tape before boot
59 14-Apr-99 RMS Changed t_addr to unsigned
60 04-Oct-98 RMS V2.4 magtape format
61 10-May-98 RMS Fixed bug with non-zero unit operation (from Steven Schultz)
62 09-May-98 RMS Fixed problems in bootstrap (from Steven Schultz)
63 10-Apr-98 RMS Added 2nd block bootstrap (from John Holden,
64 University of Sydney)
65 31-Jul-97 RMS Added bootstrap (from Ethan Dicks, Ohio State)
66 22-Jan-97 RMS V2.3 magtape format
67 18-Jan-97 RMS Fixed double interrupt, error flag bugs
68 29-Jun-96 RMS Added unit disable support
69
70 Magnetic tapes are represented as a series of variable 8b records
71 of the form:
72
73 32b record length in bytes - exact number
74 byte 0
75 byte 1
76 :
77 byte n-2
78 byte n-1
79 32b record length in bytes - exact number
80
81 If the byte count is odd, the record is padded with an extra byte
82 of junk. File marks are represented by a single record length of 0.
83 End of tape is two consecutive end of file marks.
84 */
85
86 #include "pdp11_defs.h"
87 #include "sim_tape.h"
88
89 #define TM_NUMDR 8 /* #drives */
90 #define USTAT u3 /* unit status */
91
92 /* Command - tm_cmd */
93
94 #define MTC_ERR (1 << CSR_V_ERR) /* error */
95 #define MTC_V_DEN 13 /* density */
96 #define MTC_M_DEN 03
97 #define MTC_DEN (MTC_M_DEN << MTC_V_DEN)
98 #define MTC_INIT 0010000 /* init */
99 #define MTC_LPAR 0004000 /* parity select */
100 #define MTC_V_UNIT 8 /* unit */
101 #define MTC_M_UNIT 07
102 #define MTC_UNIT (MTC_M_UNIT << MTC_V_UNIT)
103 #define MTC_DONE (1 << CSR_V_DONE) /* done */
104 #define MTC_IE (1 << CSR_V_IE) /* interrupt enable */
105 #define MTC_V_EMA 4 /* ext mem address */
106 #define MTC_M_EMA 03
107 #define MTC_EMA (MTC_M_EMA << MTC_V_EMA)
108 #define MTC_V_FNC 1 /* function */
109 #define MTC_M_FNC 07
110 #define MTC_UNLOAD 00
111 #define MTC_READ 01
112 #define MTC_WRITE 02
113 #define MTC_WREOF 03
114 #define MTC_SPACEF 04
115 #define MTC_SPACER 05
116 #define MTC_WREXT 06
117 #define MTC_REWIND 07
118 #define MTC_FNC (MTC_M_FNC << MTC_V_FNC)
119 #define MTC_GO (1 << CSR_V_GO) /* go */
120 #define MTC_RW (MTC_DEN | MTC_LPAR | MTC_UNIT | MTC_IE | \
121 MTC_EMA | MTC_FNC)
122 #define GET_EMA(x) (((x) & MTC_EMA) << (16 - MTC_V_EMA))
123 #define GET_UNIT(x) (((x) >> MTC_V_UNIT) & MTC_M_UNIT)
124 #define GET_FNC(x) (((x) >> MTC_V_FNC) & MTC_M_FNC)
125
126 /* Status - stored in tm_sta or (*) uptr->USTAT or (+) calculated */
127
128 #define STA_ILL 0100000 /* illegal */
129 #define STA_EOF 0040000 /* *end of file */
130 #define STA_CRC 0020000 /* CRC error */
131 #define STA_PAR 0010000 /* parity error */
132 #define STA_DLT 0004000 /* data late */
133 #define STA_EOT 0002000 /* +end of tape */
134 #define STA_RLE 0001000 /* rec lnt error */
135 #define STA_BAD 0000400 /* bad tape error */
136 #define STA_NXM 0000200 /* non-existent mem */
137 #define STA_ONL 0000100 /* *online */
138 #define STA_BOT 0000040 /* *start of tape */
139 #define STA_7TK 0000020 /* 7 track */
140 #define STA_SDN 0000010 /* settle down */
141 #define STA_WLK 0000004 /* *write locked */
142 #define STA_REW 0000002 /* *rewinding */
143 #define STA_TUR 0000001 /* +unit ready */
144
145 #define STA_CLR (STA_7TK | STA_SDN) /* always clear */
146 #define STA_DYN (STA_EOF | STA_EOT | STA_ONL | STA_BOT | \
147 STA_WLK | STA_REW | STA_TUR) /* dynamic */
148 #define STA_EFLGS (STA_ILL | STA_EOF | STA_CRC | STA_PAR | \
149 STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM)
150 /* set error */
151
152 /* Read lines - tm_rdl */
153
154 #define RDL_CLK 0100000 /* 10 Khz clock */
155
156 extern uint16 *M; /* memory */
157 extern int32 int_req[IPL_HLVL];
158 extern FILE *sim_deb;
159
160 uint8 *tmxb = NULL; /* xfer buffer */
161 int32 tm_sta = 0; /* status register */
162 int32 tm_cmd = 0; /* command register */
163 int32 tm_ca = 0; /* current address */
164 int32 tm_bc = 0; /* byte count */
165 int32 tm_db = 0; /* data buffer */
166 int32 tm_rdl = 0; /* read lines */
167 int32 tm_time = 10; /* record latency */
168 int32 tm_stopioe = 1; /* stop on error */
169
170 DEVICE tm_dev;
171 t_stat tm_rd (int32 *data, int32 PA, int32 access);
172 t_stat tm_wr (int32 data, int32 PA, int32 access);
173 t_stat tm_svc (UNIT *uptr);
174 t_stat tm_reset (DEVICE *dptr);
175 t_stat tm_attach (UNIT *uptr, char *cptr);
176 t_stat tm_detach (UNIT *uptr);
177 t_stat tm_boot (int32 unitno, DEVICE *dptr);
178 void tm_go (UNIT *uptr);
179 int32 tm_updcsta (UNIT *uptr);
180 void tm_set_done (void);
181 t_stat tm_map_err (UNIT *uptr, t_stat st);
182 t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc);
183
184 /* MT data structures
185
186 tm_dev MT device descriptor
187 tm_unit MT unit list
188 tm_reg MT register list
189 tm_mod MT modifier list
190 */
191
192 DIB tm_dib = {
193 IOBA_TM, IOLN_TM, &tm_rd, &tm_wr,
194 1, IVCL (TM), VEC_TM, { NULL }
195 };
196
197 UNIT tm_unit[] = {
198 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
199 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
200 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
201 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
202 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
203 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
204 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },
205 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }
206 };
207
208 REG tm_reg[] = {
209 { ORDATA (MTS, tm_sta, 16) },
210 { ORDATA (MTC, tm_cmd, 16) },
211 { ORDATA (MTBRC, tm_bc, 16) },
212 { ORDATA (MTCMA, tm_ca, 16) },
213 { ORDATA (MTD, tm_db, 8) },
214 { ORDATA (MTRD, tm_rdl, 16) },
215 { FLDATA (INT, IREQ (TM), INT_V_TM) },
216 { FLDATA (ERR, tm_cmd, CSR_V_ERR) },
217 { FLDATA (DONE, tm_cmd, CSR_V_DONE) },
218 { FLDATA (IE, tm_cmd, CSR_V_IE) },
219 { FLDATA (STOP_IOE, tm_stopioe, 0) },
220 { DRDATA (TIME, tm_time, 24), PV_LEFT },
221 { URDATA (UST, tm_unit[0].USTAT, 8, 16, 0, TM_NUMDR, 0) },
222 { URDATA (POS, tm_unit[0].pos, 10, T_ADDR_W, 0,
223 TM_NUMDR, PV_LEFT | REG_RO) },
224 { ORDATA (DEVADDR, tm_dib.ba, 32), REG_HRO },
225 { ORDATA (DEVVEC, tm_dib.vec, 16), REG_HRO },
226 { NULL }
227 };
228
229 MTAB tm_mod[] = {
230 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &tm_vlock },
231 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &tm_vlock },
232 { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
233 &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
234 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",
235 &sim_tape_set_capac, &sim_tape_show_capac, NULL },
236 { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS",
237 &set_addr, &show_addr, NULL },
238 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
239 &set_vec, &show_vec, NULL },
240 { 0 }
241 };
242
243 DEVICE tm_dev = {
244 "TM", tm_unit, tm_reg, tm_mod,
245 TM_NUMDR, 10, T_ADDR_W, 1, 8, 8,
246 NULL, NULL, &tm_reset,
247 &tm_boot, &tm_attach, &tm_detach,
248 &tm_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG
249 };
250
251 /* I/O dispatch routines, I/O addresses 17772520 - 17772532
252
253 17772520 MTS read only, constructed from tm_sta
254 plus current drive status flags
255 17772522 MTC read/write
256 17772524 MTBRC read/write
257 17772526 MTCMA read/write
258 17772530 MTD read/write
259 17772532 MTRD read only
260 */
261
262 t_stat tm_rd (int32 *data, int32 PA, int32 access)
263 {
264 UNIT *uptr;
265
266 uptr = tm_dev.units + GET_UNIT (tm_cmd); /* get unit */
267 switch ((PA >> 1) & 07) { /* decode PA<3:1> */
268
269 case 0: /* MTS */
270 *data = tm_updcsta (uptr); /* update status */
271 break;
272
273 case 1: /* MTC */
274 tm_updcsta (uptr); /* update status */
275 *data = tm_cmd; /* return command */
276 break;
277
278 case 2: /* MTBRC */
279 *data = tm_bc; /* return byte count */
280 break;
281
282 case 3: /* MTCMA */
283 *data = tm_ca; /* return mem addr */
284 break;
285
286 case 4: /* MTD */
287 *data = tm_db; /* return data buffer */
288 break;
289
290 case 5: /* MTRD */
291 tm_rdl = tm_rdl ^ RDL_CLK; /* "clock" ticks */
292 *data = tm_rdl;
293 break;
294
295 default: /* unimplemented */
296 *data = 0;
297 break;
298 }
299
300 return SCPE_OK;
301 }
302
303 t_stat tm_wr (int32 data, int32 PA, int32 access)
304 {
305 UNIT *uptr;
306
307 switch ((PA >> 1) & 07) { /* decode PA<3:1> */
308
309 case 0: /* MTS: read only */
310 break;
311
312 case 1: /* MTC */
313 uptr = tm_dev.units + GET_UNIT (tm_cmd); /* select unit */
314 if ((tm_cmd & MTC_DONE) == 0) tm_sta = tm_sta | STA_ILL;
315 else {
316 if (access == WRITEB) data = (PA & 1)?
317 (tm_cmd & 0377) | (data << 8):
318 (tm_cmd & ~0377) | data;
319 if (data & MTC_INIT) { /* init? */
320 tm_reset (&tm_dev); /* reset device */
321 return SCPE_OK;
322 }
323 if ((data & MTC_IE) == 0) /* int disable? */
324 CLR_INT (TM); /* clr int request */
325 else if ((tm_cmd & (MTC_ERR + MTC_DONE)) && !(tm_cmd & MTC_IE))
326 SET_INT (TM); /* set int request */
327 tm_cmd = (tm_cmd & ~MTC_RW) | (data & MTC_RW);
328 uptr = tm_dev.units + GET_UNIT (tm_cmd); /* new unit */
329 if (data & MTC_GO) tm_go (uptr); /* new function? */
330 }
331 tm_updcsta (uptr); /* update status */
332 break;
333
334 case 2: /* MTBRC */
335 if (access == WRITEB) data = (PA & 1)?
336 (tm_bc & 0377) | (data << 8): (tm_bc & ~0377) | data;
337 tm_bc = data;
338 break;
339
340 case 3: /* MTCMA */
341 if (access == WRITEB) data = (PA & 1)?
342 (tm_ca & 0377) | (data << 8): (tm_ca & ~0377) | data;
343 tm_ca = data;
344 break;
345
346 case 4: /* MTD */
347 if ((access == WRITEB) && (PA & 1)) return SCPE_OK;
348 tm_db = data & 0377;
349 break;
350 } /* end switch */
351
352 return SCPE_OK;
353 }
354
355 /* New magtape command */
356
357 void tm_go (UNIT *uptr)
358 {
359 int32 f;
360
361 f = GET_FNC (tm_cmd); /* get function */
362 if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */
363 sim_is_active (uptr) || /* busy? */
364 (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) &&
365 sim_tape_wrp (uptr))) { /* write locked? */
366 tm_sta = tm_sta | STA_ILL; /* illegal */
367 tm_set_done (); /* set done */
368 return;
369 }
370 uptr->USTAT = uptr->USTAT & (STA_WLK | STA_ONL); /* clear status */
371 tm_sta = 0; /* clear errors */
372 if (f == MTC_UNLOAD) { /* unload? */
373 uptr->USTAT = (uptr->USTAT | STA_REW) & ~STA_ONL;
374 detach_unit (uptr); /* set offline */
375 }
376 else if (f == MTC_REWIND) /* rewind */
377 uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */
378 /* else /* uncomment this else if rewind/unload don't set done */
379 tm_cmd = tm_cmd & ~MTC_DONE; /* clear done */
380 CLR_INT (TM); /* clear int */
381 sim_activate (uptr, tm_time); /* start io */
382 return;
383 }
384
385 /* Unit service
386
387 If rewind done, reposition to start of tape, set status
388 else, do operation, set done, interrupt
389 */
390
391 t_stat tm_svc (UNIT *uptr)
392 {
393 int32 f, t, u;
394 uint32 xma;
395 t_mtrlnt tbc, cbc;
396 t_stat st, r = SCPE_OK;
397
398 u = (int32) (uptr - tm_dev.units); /* get unit number */
399 f = GET_FNC (tm_cmd); /* get command */
400 xma = GET_EMA (tm_cmd) | tm_ca; /* get mem addr */
401 cbc = 0200000 - tm_bc; /* get bc */
402
403 if (uptr->USTAT & STA_REW) { /* rewind? */
404 sim_tape_rewind (uptr); /* update position */
405 if (uptr->flags & UNIT_ATT) /* still on line? */
406 uptr->USTAT = STA_ONL | STA_BOT |
407 (sim_tape_wrp (uptr)? STA_WLK: 0);
408 else uptr->USTAT = 0;
409 if (u == GET_UNIT (tm_cmd)) { /* selected? */
410 tm_set_done (); /* set done */
411 tm_updcsta (uptr); /* update status */
412 }
413 return SCPE_OK;
414 }
415
416 if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */
417 uptr->USTAT = 0; /* unit off line */
418 tm_sta = tm_sta | STA_ILL; /* illegal operation */
419 tm_set_done (); /* set done */
420 tm_updcsta (uptr); /* update status */
421 return IORETURN (tm_stopioe, SCPE_UNATT);
422 }
423
424 if (DEBUG_PRS (tm_dev)) fprintf (sim_deb,
425 ">>TM: op=%o, ma=%o, bc=%o, pos=%d\n", f, xma, tm_bc, uptr->pos);
426 switch (f) { /* case on function */
427
428 case MTC_READ: /* read */
429 st = sim_tape_rdrecf (uptr, tmxb, &tbc, MT_MAXFR); /* read rec */
430 if (st == MTSE_RECE) tm_sta = tm_sta | STA_PAR; /* rec in error? */
431 else if (st != MTSE_OK) { /* other error? */
432 r = tm_map_err (uptr, st); /* map error */
433 break;
434 }
435 if (tbc > cbc) tm_sta = tm_sta | STA_RLE; /* wrong size? */
436 if (tbc < cbc) cbc = tbc; /* use smaller */
437 if (t = Map_WriteB (xma, cbc, tmxb)) { /* copy buf to mem */
438 tm_sta = tm_sta | STA_NXM; /* NXM, set err */
439 cbc = cbc - t; /* adj byte cnt */
440 }
441 xma = (xma + cbc) & 0777777; /* inc bus addr */
442 tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */
443 break;
444
445 case MTC_WRITE: /* write */
446 case MTC_WREXT: /* write ext gap */
447 if (t = Map_ReadB (xma, cbc, tmxb)) { /* copy mem to buf */
448 tm_sta = tm_sta | STA_NXM; /* NXM, set err */
449 cbc = cbc - t; /* adj byte cnt */
450 if (cbc == 0) break; /* no xfr? done */
451 }
452 if (st = sim_tape_wrrecf (uptr, tmxb, cbc)) /* write rec, err? */
453 r = tm_map_err (uptr, st); /* map error */
454 else {
455 xma = (xma + cbc) & 0777777; /* inc bus addr */
456 tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */
457 }
458 break;
459
460 case MTC_WREOF: /* write eof */
461 if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */
462 r = tm_map_err (uptr, st); /* map error */
463 break;
464
465 case MTC_SPACEF: /* space forward */
466 do {
467 tm_bc = (tm_bc + 1) & 0177777; /* incr wc */
468 if (st = sim_tape_sprecf (uptr, &tbc)) { /* spc rec fwd, err? */
469 r = tm_map_err (uptr, st); /* map error */
470 break;
471 }
472 } while (tm_bc != 0);
473 break;
474
475 case MTC_SPACER: /* space reverse */
476 do {
477 tm_bc = (tm_bc + 1) & 0177777; /* incr wc */
478 if (st = sim_tape_sprecr (uptr, &tbc)) { /* spc rec rev, err? */
479 r = tm_map_err (uptr, st); /* map error */
480 break;
481 }
482 } while (tm_bc != 0);
483 break;
484 } /* end case */
485
486 tm_cmd = (tm_cmd & ~MTC_EMA) | ((xma >> (16 - MTC_V_EMA)) & MTC_EMA);
487 tm_ca = xma & 0177777; /* update mem addr */
488 tm_set_done (); /* set done */
489 tm_updcsta (uptr); /* update status */
490 if (DEBUG_PRS (tm_dev)) fprintf (sim_deb,
491 ">>TM: sta=%o, ma=%o, bc=%o, pos=%d\n",
492 tm_sta, tm_ca, tm_bc, uptr->pos);
493 return r;
494 }
495
496 /* Update controller status */
497
498 int32 tm_updcsta (UNIT *uptr)
499 {
500 tm_sta = (tm_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN);
501 if (sim_tape_eot (uptr)) tm_sta = tm_sta | STA_EOT;
502 if (sim_is_active (uptr)) tm_sta = tm_sta & ~STA_TUR;
503 else tm_sta = tm_sta | STA_TUR;
504 if (tm_sta & STA_EFLGS) tm_cmd = tm_cmd | MTC_ERR;
505 else tm_cmd = tm_cmd & ~MTC_ERR;
506 if ((tm_cmd & MTC_IE) == 0) CLR_INT (TM);
507 return tm_sta;
508 }
509
510 /* Set done */
511
512 void tm_set_done (void)
513 {
514 tm_cmd = tm_cmd | MTC_DONE;
515 if (tm_cmd & MTC_IE) SET_INT (TM);
516 return;
517 }
518
519 /* Map tape error status */
520
521 t_stat tm_map_err (UNIT *uptr, t_stat st)
522 {
523 switch (st) {
524
525 case MTSE_FMT: /* illegal fmt */
526 case MTSE_UNATT: /* not attached */
527 tm_sta = tm_sta | STA_ILL;
528 case MTSE_OK: /* no error */
529 return SCPE_IERR;
530
531 case MTSE_TMK: /* tape mark */
532 uptr->USTAT = uptr->USTAT | STA_EOF; /* end of file */
533 break;
534
535 case MTSE_IOERR: /* IO error */
536 tm_sta = tm_sta | STA_PAR; /* parity error */
537 if (tm_stopioe) return SCPE_IOERR;
538 break;
539
540 case MTSE_INVRL: /* invalid rec lnt */
541 tm_sta = tm_sta | STA_PAR; /* parity error */
542 return SCPE_MTRLNT;
543
544 case MTSE_RECE: /* record in error */
545 tm_sta = tm_sta | STA_PAR; /* parity error */
546 break;
547
548 case MTSE_EOM: /* end of medium */
549 tm_sta = tm_sta | STA_BAD; /* bad tape */
550 break;
551
552 case MTSE_BOT: /* reverse into BOT */
553 uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */
554 break;
555
556 case MTSE_WRP: /* write protect */
557 tm_sta = tm_sta | STA_ILL; /* illegal operation */
558 break;
559 }
560
561 return SCPE_OK;
562 }
563
564 /* Reset routine */
565
566 t_stat tm_reset (DEVICE *dptr)
567 {
568 int32 u;
569 UNIT *uptr;
570
571 tm_cmd = MTC_DONE; /* set done */
572 tm_bc = tm_ca = tm_db = tm_sta = tm_rdl = 0;
573 CLR_INT (TM); /* clear interrupt */
574 for (u = 0; u < TM_NUMDR; u++) { /* loop thru units */
575 uptr = tm_dev.units + u;
576 sim_tape_reset (uptr); /* reset tape */
577 sim_cancel (uptr); /* cancel activity */
578 if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_ONL |
579 (sim_tape_bot (uptr)? STA_BOT: 0) |
580 (sim_tape_wrp (uptr)? STA_WLK: 0);
581 else uptr->USTAT = 0;
582 }
583 if (tmxb == NULL) tmxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8));
584 if (tmxb == NULL) return SCPE_MEM;
585 return SCPE_OK;
586 }
587
588 /* Attach routine */
589
590 t_stat tm_attach (UNIT *uptr, char *cptr)
591 {
592 t_stat r;
593 int32 u = uptr - tm_dev.units;
594
595 r = sim_tape_attach (uptr, cptr);
596 if (r != SCPE_OK) return r;
597 uptr->USTAT = STA_ONL | STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0);
598 if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr);
599 return r;
600 }
601
602 /* Detach routine */
603
604 t_stat tm_detach (UNIT* uptr)
605 {
606 int32 u = uptr - tm_dev.units;
607
608 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;
609 if (!sim_is_active (uptr)) uptr->USTAT = 0;
610 if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr);
611 return sim_tape_detach (uptr);
612 }
613
614 /* Write lock/enable routine */
615
616 t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc)
617 {
618 int32 u = uptr - tm_dev.units;
619
620 if ((uptr->flags & UNIT_ATT) &&
621 (val || sim_tape_wrp (uptr)))
622 uptr->USTAT = uptr->USTAT | STA_WLK;
623 else uptr->USTAT = uptr->USTAT & ~STA_WLK;
624 if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr);
625 return SCPE_OK;
626 }
627
628 /* Device bootstrap
629
630 Magtape boot format changed over time. Originally, a boot tape
631 contained a boot loader in the first block. Eventually, the first
632 block was reserved for a tape label, and the second block was
633 expected to contain a boot loader. BSD and DEC operating systems
634 use the second block scheme, so it is the default.
635
636 To boot from the first block, use boot -o (old).
637 */
638
639 #define BOOT_START 016000
640 #define BOOT_ENTRY (BOOT_START + 2)
641 #define BOOT_UNIT (BOOT_START + 010)
642 #define BOOT_CSR (BOOT_START + 014)
643 #define BOOT1_LEN (sizeof (boot1_rom) / sizeof (int16))
644 #define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16))
645
646 static const uint16 boot1_rom[] = {
647 0046524, /* boot_start: "TM" */
648 0012706, BOOT_START, /* mov #boot_start, sp */
649 0012700, 0000000, /* mov #unit_num, r0 */
650 0012701, 0172526, /* mov #172526, r1 ; mtcma */
651 0005011, /* clr (r1) */
652 0010141, /* mov r1, -(r1) ; mtbrc */
653 0010002, /* mov r0,r2 */
654 0000302, /* swab r2 */
655 0062702, 0060003, /* add #60003, r2 */
656 0010241, /* mov r2, -(r1) ; read + go */
657 0105711, /* tstb (r1) ; mtc */
658 0100376, /* bpl .-2 */
659 0005002, /* clr r2 */
660 0005003, /* clr r3 */
661 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */
662 0005005, /* clr r5 */
663 0005007 /* clr r7 */
664 };
665
666 static const uint16 boot2_rom[] = {
667 0046524, /* boot_start: "TM" */
668 0012706, BOOT_START, /* mov #boot_start, sp */
669 0012700, 0000000, /* mov #unit_num, r0 */
670 0012701, 0172526, /* mov #172526, r1 ; mtcma */
671 0005011, /* clr (r1) */
672 0012741, 0177777, /* mov #-1, -(r1) ; mtbrc */
673 0010002, /* mov r0,r2 */
674 0000302, /* swab r2 */
675 0062702, 0060011, /* add #60011, r2 */
676 0010241, /* mov r2, -(r1) ; space + go */
677 0105711, /* tstb (r1) ; mtc */
678 0100376, /* bpl .-2 */
679 0010002, /* mov r0,r2 */
680 0000302, /* swab r2 */
681 0062702, 0060003, /* add #60003, r2 */
682 0010211, /* mov r2, (r1) ; read + go */
683 0105711, /* tstb (r1) ; mtc */
684 0100376, /* bpl .-2 */
685 0005002, /* clr r2 */
686 0005003, /* clr r3 */
687 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */
688 0005005, /* clr r5 */
689 0005007 /* clr r7 */
690 };
691
692 t_stat tm_boot (int32 unitno, DEVICE *dptr)
693 {
694 int32 i;
695 extern int32 saved_PC;
696 extern int32 sim_switches;
697
698 sim_tape_rewind (&tm_unit[unitno]);
699 if (sim_switches & SWMASK ('O')) {
700 for (i = 0; i < BOOT1_LEN; i++)
701 M[(BOOT_START >> 1) + i] = boot1_rom[i];
702 }
703 else {
704 for (i = 0; i < BOOT2_LEN; i++)
705 M[(BOOT_START >> 1) + i] = boot2_rom[i];
706 }
707 M[BOOT_UNIT >> 1] = unitno;
708 M[BOOT_CSR >> 1] = (tm_dib.ba & DMASK) + 06;
709 saved_PC = BOOT_ENTRY;
710 return SCPE_OK;
711 }