First Commit of my working state
[simh.git] / PDP8 / pdp8_ct.c
1 /* pdp8_ct.c: PDP-8 cassette tape simulator
2
3 Copyright (c) 2006-2007, 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 ct TA8E/TU60 cassette tape
27
28 13-Aug-07 RMS Fixed handling of BEOT
29 06-Aug-07 RMS Foward op at BOT skips initial file gap
30 30-May-2007 RMS Fixed typo (from Norm Lastovica)
31
32 Magnetic tapes are represented as a series of variable records
33 of the form:
34
35 32b byte count
36 byte 0
37 byte 1
38 :
39 byte n-2
40 byte n-1
41 32b byte count
42
43 If the byte count is odd, the record is padded with an extra byte
44 of junk. File marks are represented by a byte count of 0.
45
46 Cassette format differs in one very significant way: it has file gaps
47 rather than file marks. If the controller spaces or reads into a file
48 gap and then reverses direction, the file gap is not seen again. This
49 is in contrast to magnetic tapes, where the file mark is a character
50 sequence and is seen again if direction is reversed. In addition,
51 cassettes have an initial file gap which is automatically skipped on
52 forward operations from beginning of tape.
53
54 Note that the read and write sequences for the cassette are asymmetric:
55
56 Read: KLSA /SELECT READ
57 KGOA /INIT READ, CLEAR DF
58 <data flag sets, char in buf>
59 KGOA /READ 1ST CHAR, CLEAR DF
60 DCA CHAR
61 :
62 <data flag sets, char in buf>
63 KGOA /READ LAST CHAR, CLEAR DF
64 DCA CHAR
65 <data flag sets, CRC1 in buf>
66 KLSA /SELECT CRC MODE
67 KGOA /READ 1ST CRC
68 <data flag sets, CRC2 in buf>
69 KGOA /READ 2ND CRC
70 <ready flag/CRC error flag sets>
71
72 Write: KLSA /SELECT WRITE
73 TAD CHAR /1ST CHAR
74 KGOA /INIT WRITE, CHAR TO BUF, CLEAR DF
75 <data flag sets, char to tape>
76 :
77 TAD CHAR /LAST CHAR
78 KGOA /CHAR TO BUF, CLEAR DF
79 <data flag sets, char to tape>
80 KLSA /SELECT CRC MODE
81 KGOA /WRITE CRC, CLEAR DF
82 <ready flag sets, CRC on tape>
83 */
84
85 #include "pdp8_defs.h"
86 #include "sim_tape.h"
87
88 #define CT_NUMDR 2 /* #drives */
89 #define FNC u3 /* unit function */
90 #define UST u4 /* unit status */
91 #define CT_MAXFR (CT_SIZE) /* max record lnt */
92 #define CT_SIZE 93000 /* chars/tape */
93
94 /* Status Register A */
95
96 #define SRA_ENAB 0200 /* enable */
97 #define SRA_V_UNIT 6 /* unit */
98 #define SRA_M_UNIT (CT_NUMDR - 1)
99 #define SRA_V_FNC 3 /* function */
100 #define SRA_M_FNC 07
101 #define SRA_READ 00
102 #define SRA_REW 01
103 #define SRA_WRITE 02
104 #define SRA_SRF 03
105 #define SRA_WFG 04
106 #define SRA_SRB 05
107 #define SRA_CRC 06
108 #define SRA_SFF 07
109 #define SRA_2ND 010
110 #define SRA_IE 0001 /* int enable */
111 #define GET_UNIT(x) (((x) >> SRA_V_UNIT) & SRA_M_UNIT)
112 #define GET_FNC(x) (((x) >> SRA_V_FNC) & SRA_M_FNC)
113
114 /* Function code flags */
115
116 #define OP_WRI 01 /* op is a write */
117 #define OP_REV 02 /* op is rev motion */
118 #define OP_FWD 04 /* op is fwd motion */
119
120 /* Unit status flags */
121
122 #define UST_REV (OP_REV) /* last op was rev */
123 #define UST_GAP 01 /* last op hit gap */
124
125 /* Status Register B, ^ = computed on the fly */
126
127 #define SRB_WLE 0400 /* "write lock err" */
128 #define SRB_CRC 0200 /* CRC error */
129 #define SRB_TIM 0100 /* timing error */
130 #define SRB_BEOT 0040 /* ^BOT/EOT */
131 #define SRB_EOF 0020 /* end of file */
132 #define SRB_EMP 0010 /* ^drive empty */
133 #define SRB_REW 0004 /* rewinding */
134 #define SRB_WLK 0002 /* ^write locked */
135 #define SRB_RDY 0001 /* ^ready */
136 #define SRB_ALLERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_BEOT|SRB_EOF|SRB_EMP)
137 #define SRB_XFRERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_EOF)
138
139 extern int32 int_req, stop_inst;
140 extern UNIT cpu_unit;
141 extern FILE *sim_deb;
142
143 uint32 ct_sra = 0; /* status reg A */
144 uint32 ct_srb = 0; /* status reg B */
145 uint32 ct_db = 0; /* data buffer */
146 uint32 ct_df = 0; /* data flag */
147 uint32 ct_write = 0; /* TU60 write flag */
148 uint32 ct_bptr = 0; /* buf ptr */
149 uint32 ct_blnt = 0; /* buf length */
150 int32 ct_stime = 1000; /* start time */
151 int32 ct_ctime = 100; /* char latency */
152 uint32 ct_stopioe = 1; /* stop on error */
153 uint8 *ct_xb = NULL; /* transfer buffer */
154 static uint8 ct_fnc_tab[SRA_M_FNC + 1] = {
155 OP_FWD, 0 , OP_WRI|OP_FWD, OP_REV,
156 OP_WRI|OP_FWD, OP_REV, 0, OP_FWD
157 };
158
159 DEVICE ct_dev;
160 int32 ct70 (int32 IR, int32 AC);
161 t_stat ct_svc (UNIT *uptr);
162 t_stat ct_reset (DEVICE *dptr);
163 t_stat ct_attach (UNIT *uptr, char *cptr);
164 t_stat ct_detach (UNIT *uptr);
165 t_stat ct_boot (int32 unitno, DEVICE *dptr);
166 uint32 ct_updsta (UNIT *uptr);
167 int32 ct_go_start (int32 AC);
168 int32 ct_go_cont (UNIT *uptr, int32 AC);
169 t_stat ct_map_err (UNIT *uptr, t_stat st);
170 UNIT *ct_busy (void);
171 void ct_set_df (t_bool timchk);
172 t_bool ct_read_char (void);
173 uint32 ct_crc (uint8 *buf, uint32 cnt);
174
175 /* CT data structures
176
177 ct_dev CT device descriptor
178 ct_unit CT unit list
179 ct_reg CT register list
180 ct_mod CT modifier list
181 */
182
183 DIB ct_dib = { DEV_CT, 1, { &ct70 } };
184
185 UNIT ct_unit[] = {
186 { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) },
187 { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) },
188 };
189
190 REG ct_reg[] = {
191 { ORDATA (CTSRA, ct_sra, 8) },
192 { ORDATA (CTSRB, ct_srb, 8) },
193 { ORDATA (CTDB, ct_db, 8) },
194 { FLDATA (CTDF, ct_df, 0) },
195 { FLDATA (RDY, ct_srb, 0) },
196 { FLDATA (WLE, ct_srb, 8) },
197 { FLDATA (WRITE, ct_write, 0) },
198 { FLDATA (INT, int_req, INT_V_CT) },
199 { DRDATA (BPTR, ct_bptr, 17) },
200 { DRDATA (BLNT, ct_blnt, 17) },
201 { DRDATA (STIME, ct_stime, 24), PV_LEFT + REG_NZ },
202 { DRDATA (CTIME, ct_ctime, 24), PV_LEFT + REG_NZ },
203 { FLDATA (STOP_IOE, ct_stopioe, 0) },
204 { URDATA (UFNC, ct_unit[0].FNC, 8, 4, 0, CT_NUMDR, 0), REG_HRO },
205 { URDATA (UST, ct_unit[0].UST, 8, 2, 0, CT_NUMDR, 0), REG_HRO },
206 { URDATA (POS, ct_unit[0].pos, 10, T_ADDR_W, 0,
207 CT_NUMDR, PV_LEFT | REG_RO) },
208 { FLDATA (DEVNUM, ct_dib.dev, 6), REG_HRO },
209 { NULL }
210 };
211
212 MTAB ct_mod[] = {
213 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL },
214 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL },
215 // { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
216 // &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
217 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL,
218 NULL, &sim_tape_show_capac, NULL },
219 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
220 &set_dev, &show_dev, NULL },
221 { 0 }
222 };
223
224 DEVICE ct_dev = {
225 "CT", ct_unit, ct_reg, ct_mod,
226 CT_NUMDR, 10, 31, 1, 8, 8,
227 NULL, NULL, &ct_reset,
228 &ct_boot, &ct_attach, &ct_detach,
229 &ct_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG
230 };
231
232 /* IOT routines */
233
234 int32 ct70 (int32 IR, int32 AC)
235 {
236 int32 srb;
237 UNIT *uptr;
238
239 srb = ct_updsta (NULL); /* update status */
240 switch (IR & 07) { /* decode IR<9:11> */
241
242 case 0: /* KCLR */
243 ct_reset (&ct_dev); /* reset the world */
244 break;
245
246 case 1: /* KSDR */
247 if (ct_df) AC |= IOT_SKP;
248 break;
249
250 case 2: /* KSEN */
251 if (srb & SRB_ALLERR) AC |= IOT_SKP;
252 break;
253
254 case 3: /* KSBF */
255 if ((srb & SRB_RDY) && !(srb & SRB_EMP))
256 AC |= IOT_SKP;
257 break;
258
259 case 4: /* KLSA */
260 ct_sra = AC & 0377;
261 ct_updsta (NULL);
262 return ct_sra ^ 0377;
263
264 case 5: /* KSAF */
265 if (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))
266 AC |= IOT_SKP;
267 break;
268
269 case 6: /* KGOA */
270 ct_df = 0; /* clear data flag */
271 if (uptr = ct_busy ()) /* op in progress? */
272 AC = ct_go_cont (uptr, AC); /* yes */
273 else AC = ct_go_start (AC); /* no, start */
274 ct_updsta (NULL);
275 break;
276
277 case 7: /* KSRB */
278 return srb & 0377;
279 } /* end switch */
280
281 return AC;
282 }
283
284 /* Start a new operation - cassette is not busy */
285
286 int32 ct_go_start (int32 AC)
287 {
288 UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra);
289 uint32 fnc = GET_FNC (ct_sra);
290 uint32 flg = ct_fnc_tab[fnc];
291 uint32 old_ust = uptr->UST;
292
293 if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
294 ">>CT start: op=%o, old_sta = %o, pos=%d\n",
295 fnc, uptr->UST, uptr->pos);
296 if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) { /* enabled, att? */
297 ct_srb &= ~(SRB_XFRERR|SRB_REW); /* clear err, rew */
298 if (flg & OP_WRI) { /* write-type op? */
299 if (sim_tape_wrp (uptr)) { /* locked? */
300 ct_srb |= SRB_WLE; /* set flag, abort */
301 return AC;
302 }
303 ct_write = 1; /* set TU60 wr flag */
304 ct_db = AC & 0377;
305 }
306 else {
307 ct_write = 0;
308 ct_db = 0;
309 }
310 ct_srb &= ~SRB_BEOT; /* tape in motion */
311 if (fnc == SRA_REW) ct_srb |= SRB_REW; /* rew? set flag */
312 if ((fnc != SRA_REW) && !(flg & OP_WRI)) { /* read cmd? */
313 t_mtrlnt t;
314 t_stat st;
315 uptr->UST = flg & UST_REV; /* save direction */
316 if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */
317 st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */
318 if (st != MTSE_TMK) /* not there? */
319 sim_tape_rewind (uptr); /* restore tap pos */
320 else old_ust = 0; /* defang next */
321 }
322 if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */
323 if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
324 ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n",
325 fnc, uptr->UST, uptr->pos);
326 if (uptr->UST) /* skip file gap */
327 sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR);
328 else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR);
329 }
330 }
331 else uptr->UST = 0;
332 ct_bptr = 0; /* init buffer */
333 ct_blnt = 0;
334 uptr->FNC = fnc; /* save function */
335 sim_activate (uptr, ct_stime); /* schedule op */
336 }
337 if ((fnc == SRA_READ) || (fnc == SRA_CRC)) /* read or CRC? */
338 return 0; /* get "char" */
339 return AC;
340 }
341
342 /* Continue an in-progress operation - cassette is in motion */
343
344 int32 ct_go_cont (UNIT *uptr, int32 AC)
345 {
346 int32 fnc = GET_FNC (ct_sra);
347
348 switch (fnc) { /* case on function */
349
350 case SRA_READ: /* read */
351 return ct_db; /* return data */
352
353 case SRA_WRITE: /* write */
354 ct_db = AC & 0377; /* save data */
355 break;
356
357 case SRA_CRC: /* CRC */
358 if ((uptr->FNC & SRA_M_FNC) != SRA_CRC) /* if not CRC */
359 uptr->FNC = SRA_CRC; /* start CRC seq */
360 if (!ct_write) return ct_db; /* read? AC <- buf */
361 break;
362
363 default:
364 break;
365 }
366
367 return AC;
368 }
369
370 /* Unit service */
371
372 t_stat ct_svc (UNIT *uptr)
373 {
374 uint32 i, crc;
375 uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC];
376 t_mtrlnt tbc;
377 t_stat st, r;
378
379 if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
380 ct_updsta (uptr); /* update status */
381 return (ct_stopioe? SCPE_UNATT: SCPE_OK);
382 }
383 if (((flgs & OP_REV) && sim_tape_bot (uptr)) || /* rev at BOT or */
384 ((flgs & OP_FWD) && sim_tape_eot (uptr))) { /* fwd at EOT? */
385 ct_srb |= SRB_BEOT; /* error */
386 ct_updsta (uptr); /* op done */
387 return SCPE_OK;
388 }
389
390 r = SCPE_OK;
391 switch (uptr->FNC) { /* case on function */
392
393 case SRA_READ: /* read start */
394 st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */
395 if (st == MTSE_RECE) ct_srb |= SRB_CRC; /* rec in err? */
396 else if (st != MTSE_OK) { /* other error? */
397 r = ct_map_err (uptr, st); /* map error */
398 break;
399 }
400 crc = ct_crc (ct_xb, ct_blnt); /* calculate CRC */
401 ct_xb[ct_blnt++] = (crc >> 8) & 0377; /* append to buffer */
402 ct_xb[ct_blnt++] = crc & 0377;
403 uptr->FNC |= SRA_2ND; /* next state */
404 sim_activate (uptr, ct_ctime); /* sched next char */
405 return SCPE_OK;
406
407 case SRA_READ|SRA_2ND: /* read char */
408 if (!ct_read_char ()) break; /* read, overrun? */
409 ct_set_df (TRUE); /* set data flag */
410 sim_activate (uptr, ct_ctime); /* sched next char */
411 return SCPE_OK;
412
413 case SRA_WRITE: /* write start */
414 for (i = 0; i < CT_MAXFR; i++) ct_xb[i] = 0; /* clear buffer */
415 uptr->FNC |= SRA_2ND; /* next state */
416 sim_activate (uptr, ct_ctime); /* sched next char */
417 return SCPE_OK;
418
419 case SRA_WRITE|SRA_2ND: /* write char */
420 if ((ct_bptr < CT_MAXFR) && /* room in buf? */
421 ((uptr->pos + ct_bptr) < uptr->capac)) /* room on tape? */
422 ct_xb[ct_bptr++] = ct_db; /* store char */
423 ct_set_df (TRUE); /* set data flag */
424 sim_activate (uptr, ct_ctime); /* sched next char */
425 return SCPE_OK;
426
427 case SRA_CRC: /* CRC */
428 if (ct_write) { /* write? */
429 if (st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr)) /* write, err? */
430 r = ct_map_err (uptr, st); /* map error */
431 break; /* write done */
432 }
433 ct_read_char (); /* get second CRC */
434 ct_set_df (FALSE); /* set df */
435 uptr->FNC |= SRA_2ND; /* next state */
436 sim_activate (uptr, ct_ctime);
437 return SCPE_OK;
438
439 case SRA_CRC|SRA_2ND: /* second read CRC */
440 if (ct_bptr != ct_blnt) { /* partial read? */
441 crc = ct_crc (ct_xb, ct_bptr); /* actual CRC */
442 if (crc != 0) ct_srb |= SRB_CRC; /* must be zero */
443 }
444 break; /* read done */
445
446 case SRA_WFG: /* write file gap */
447 if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */
448 r = ct_map_err (uptr, st); /* map error */
449 break;
450
451 case SRA_REW: /* rewind */
452 sim_tape_rewind (uptr);
453 ct_srb |= SRB_BEOT; /* set BOT */
454 break;
455
456 case SRA_SRB: /* space rev blk */
457 if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */
458 r = ct_map_err (uptr, st); /* map error */
459 break;
460
461 case SRA_SRF: /* space rev file */
462 while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
463 r = ct_map_err (uptr, st); /* map error */
464 break;
465
466 case SRA_SFF: /* space fwd file */
467 while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;
468 r = ct_map_err (uptr, st); /* map error */
469 break;
470
471 default: /* never get here! */
472 return SCPE_IERR;
473 } /* end case */
474
475 ct_updsta (uptr); /* update status */
476 if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
477 ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n",
478 uptr->FNC, ct_sra, ct_srb, uptr->pos);
479 return r;
480 }
481
482 /* Update controller status */
483
484 uint32 ct_updsta (UNIT *uptr)
485 {
486 int32 srb;
487
488 if (uptr == NULL) { /* unit specified? */
489 uptr = ct_busy (); /* use busy unit */
490 if ((uptr == NULL) && (ct_sra & SRA_ENAB)) /* none busy? */
491 uptr = ct_dev.units + GET_UNIT (ct_sra); /* use sel unit */
492 }
493 else if (ct_srb & SRB_EOF) uptr->UST |= UST_GAP; /* save gap */
494 if (uptr) { /* any unit? */
495 ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY); /* clear dyn flags */
496 if ((uptr->flags & UNIT_ATT) == 0) /* unattached? */
497 ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */
498 if (!sim_is_active (uptr)) { /* not busy? */
499 ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW; /* ready, ~rew */
500 }
501 if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW)) /* locked or rew? */
502 ct_srb |= SRB_WLK; /* set locked */
503 }
504 if (ct_sra & SRA_ENAB) srb = ct_srb; /* can TA see TU60? */
505 else srb = 0; /* no */
506 if ((ct_sra & SRA_IE) && /* int enabled? */
507 (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))) /* any flag? */
508 int_req |= INT_CT; /* set int req */
509 else int_req &= ~INT_CT; /* no, clr int req */
510 return srb;
511 }
512
513 /* Set data flag */
514
515 void ct_set_df (t_bool timchk)
516 {
517 if (ct_df && timchk) ct_srb |= SRB_TIM; /* flag still set? */
518 ct_df = 1; /* set data flag */
519 if (ct_sra & SRA_IE) int_req |= INT_CT; /* if ie, int req */
520 return;
521 }
522
523 /* Read character */
524
525 t_bool ct_read_char (void)
526 {
527 if (ct_bptr < ct_blnt) { /* more chars? */
528 ct_db = ct_xb[ct_bptr++];
529 return TRUE;
530 }
531 ct_db = 0;
532 ct_srb |= SRB_CRC; /* overrun */
533 return FALSE;
534 }
535
536 /* Test if controller busy */
537
538 UNIT *ct_busy (void)
539 {
540 uint32 u;
541 UNIT *uptr;
542
543 for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */
544 uptr = ct_dev.units + u;
545 if (sim_is_active (uptr)) return uptr;
546 }
547 return NULL;
548 }
549
550 /* Calculate CRC on buffer */
551
552 uint32 ct_crc (uint8 *buf, uint32 cnt)
553 {
554 uint32 crc, i, j;
555
556 crc = 0;
557 for (i = 0; i < cnt; i++) {
558 crc = crc ^ (((uint32) buf[i]) << 8);
559 for (j = 0; j < 8; j++) {
560 if (crc & 1) crc = (crc >> 1) ^ 0xA001;
561 else crc = crc >> 1;
562 }
563 }
564 return crc;
565 }
566
567 /* Map error status */
568
569 t_stat ct_map_err (UNIT *uptr, t_stat st)
570 {
571 switch (st) {
572
573 case MTSE_FMT: /* illegal fmt */
574 case MTSE_UNATT: /* unattached */
575 ct_srb |= SRB_CRC;
576 case MTSE_OK: /* no error */
577 return SCPE_IERR; /* never get here! */
578
579 case MTSE_TMK: /* end of file */
580 ct_srb |= SRB_EOF;
581 break;
582
583 case MTSE_IOERR: /* IO error */
584 ct_srb |= SRB_CRC; /* set crc err */
585 if (ct_stopioe) return SCPE_IOERR;
586 break;
587
588 case MTSE_INVRL: /* invalid rec lnt */
589 ct_srb |= SRB_CRC; /* set crc err */
590 return SCPE_MTRLNT;
591
592 case MTSE_RECE: /* record in error */
593 case MTSE_EOM: /* end of medium */
594 ct_srb |= SRB_CRC; /* set crc err */
595 break;
596
597 case MTSE_BOT: /* reverse into BOT */
598 ct_srb |= SRB_BEOT; /* set BOT */
599 break;
600
601 case MTSE_WRP: /* write protect */
602 ct_srb |= SRB_WLE; /* set wlk err */
603 break;
604 }
605
606 return SCPE_OK;
607 }
608
609 /* Reset routine */
610
611 t_stat ct_reset (DEVICE *dptr)
612 {
613 uint32 u;
614 UNIT *uptr;
615
616 ct_sra = 0;
617 ct_srb = 0;
618 ct_df = 0;
619 ct_db = 0;
620 ct_write = 0;
621 ct_bptr = 0;
622 ct_blnt = 0;
623 int_req = int_req & ~INT_CT; /* clear interrupt */
624 for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */
625 uptr = ct_dev.units + u;
626 sim_cancel (uptr); /* cancel activity */
627 sim_tape_reset (uptr); /* reset tape */
628 }
629 if (ct_xb == NULL) ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8));
630 if (ct_xb == NULL) return SCPE_MEM;
631 return SCPE_OK;
632 }
633
634 /* Attach routine */
635
636 t_stat ct_attach (UNIT *uptr, char *cptr)
637 {
638 t_stat r;
639
640 r = sim_tape_attach (uptr, cptr);
641 if (r != SCPE_OK) return r;
642 ct_updsta (NULL);
643 uptr->UST = 0;
644 return r;
645 }
646
647 /* Detach routine */
648
649 t_stat ct_detach (UNIT* uptr)
650 {
651 t_stat r;
652
653 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* check attached */
654 r = sim_tape_detach (uptr);
655 ct_updsta (NULL);
656 uptr->UST = 0;
657 return r;
658 }
659
660 /* Bootstrap routine */
661
662 #define BOOT_START 04000
663 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
664
665 static const uint16 boot_rom[] = {
666 01237, /* BOOT, TAD M50 /change CRC to REW */
667 01206, /* CRCCHK, TAD L260 /crc op */
668 06704, /* KLSA /load op */
669 06706, /* KGOA /start */
670 06703, /* KSBF /ready? */
671 05204, /* RDCOD, JMP .-1 /loop */
672 07264, /* L260, CML STA RAL /L = 1, AC = halt */
673 06702, /* KSEN /error? */
674 07610, /* SKP CLA /halt on any error */
675 03211, /* DCA . /except REW or FFG */
676 03636, /* DCA I PTR /TAD I PTR mustn't change L */
677 01205, /* TAD RDCOD /read op */
678 06704, /* KLSA /load op */
679 06706, /* KGOA /start */
680 06701, /* LOOP, KSDF /data ready? */
681 05216, /* JMP .-1 /loop */
682 07002, /* BSW /to upper 6b */
683 07430, /* SZL /second byte? */
684 01636, /* TAD I PTR /yes */
685 07022, /* CML BSW /swap back */
686 03636, /* DCA I PTR /store in mem */
687 07420, /* SNL /done with both bytes? */
688 02236, /* ISZ PTR /yes, bump mem ptr */
689 02235, /* ISZ KNT /done with record? */
690 05215, /* JMP LOOP /next byte */
691 07346, /* STA CLL RTL */
692 07002, /* BSW /AC = 7757 */
693 03235, /* STA KNT /now read 200 byte record */
694 05201, /* JMP CRCCHK /go check CRC */
695 07737, /* KNT, 7737 /1's compl of byte count */
696 03557, /* PTR, 3557 /load point */
697 07730, /* M50, 7730 /CLA SPA SZL */
698 };
699
700 t_stat ct_boot (int32 unitno, DEVICE *dptr)
701 {
702 int32 i;
703 extern int32 saved_PC;
704 extern uint16 M[];
705
706 if ((ct_dib.dev != DEV_CT) || unitno) /* only std devno */
707 return STOP_NOTSTD;
708 for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
709 saved_PC = BOOT_START;
710 return SCPE_OK;
711 }