1 /* pdp8_ct.c: PDP-8 cassette tape simulator
2 
3    Copyright (c) 2006-2011, 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 (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 
ct70(int32 IR,int32 AC)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)
248             AC |= IOT_SKP;
249         break;
250 
251     case 2:                                             /* KSEN */
252         if (srb & SRB_ALLERR)
253             AC |= IOT_SKP;
254         break;
255 
256     case 3:                                             /* KSBF */
257         if ((srb & SRB_RDY) && !(srb & SRB_EMP))
258             AC |= IOT_SKP;
259         break;
260 
261     case 4:                                             /* KLSA */
262         ct_sra = AC & 0377;
263         ct_updsta (NULL);
264         return ct_sra ^ 0377;
265 
266     case 5:                                             /* KSAF */
267         if (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))
268             AC |= IOT_SKP;
269         break;
270 
271     case 6:                                             /* KGOA */
272         ct_df = 0;                                      /* clear data flag */
273         if ((uptr = ct_busy ()))                        /* op in progress? */
274             AC = ct_go_cont (uptr, AC);                 /* yes */
275         else AC = ct_go_start (AC);                     /* no, start */
276         ct_updsta (NULL);
277         break;
278 
279     case 7:                                             /* KSRB */
280         return srb & 0377;
281         }                                               /* end switch */
282 
283 return AC;
284 }
285 
286 /* Start a new operation - cassette is not busy */
287 
ct_go_start(int32 AC)288 int32 ct_go_start (int32 AC)
289 {
290 UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra);
291 uint32 fnc = GET_FNC (ct_sra);
292 uint32 flg = ct_fnc_tab[fnc];
293 uint32 old_ust = uptr->UST;
294 
295 if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
296     ">>CT start: op=%o, old_sta = %o, pos=%d\n",
297     fnc, uptr->UST, uptr->pos);
298 if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) {  /* enabled, att? */
299     ct_srb &= ~(SRB_XFRERR|SRB_REW);                    /* clear err, rew */
300     if (flg & OP_WRI) {                                 /* write-type op? */
301         if (sim_tape_wrp (uptr)) {                      /* locked? */
302             ct_srb |= SRB_WLE;                          /* set flag, abort */
303             return AC;
304             }
305         ct_write = 1;                                   /* set TU60 wr flag */
306         ct_db = AC & 0377;
307         }
308     else {
309         ct_write = 0;
310         ct_db = 0;
311         }
312     ct_srb &= ~SRB_BEOT;                                /* tape in motion */
313     if (fnc == SRA_REW)                                 /* rew? set flag */
314         ct_srb |= SRB_REW;
315     if ((fnc != SRA_REW) && !(flg & OP_WRI)) {          /* read cmd? */
316         t_mtrlnt t;
317         t_stat st;
318         uptr->UST = flg & UST_REV;                      /* save direction */
319         if (sim_tape_bot (uptr) && (flg & OP_FWD)) {    /* spc/read fwd bot? */
320             st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */
321             if (st != MTSE_TMK)                         /* not there? */
322                 sim_tape_rewind (uptr);                 /* restore tap pos */
323             else old_ust = 0;                           /* defang next */
324             }
325         if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */
326             if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
327                 ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n",
328                 fnc, uptr->UST, uptr->pos);
329             if (uptr->UST)                              /* skip file gap */
330                 sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR);
331             else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR);
332             }
333         }
334     else uptr->UST = 0;
335     ct_bptr = 0;                                        /* init buffer */
336     ct_blnt = 0;
337     uptr->FNC = fnc;                                    /* save function */
338     sim_activate (uptr, ct_stime);                      /* schedule op */
339     }
340 if ((fnc == SRA_READ) || (fnc == SRA_CRC))              /* read or CRC? */
341     return 0;                                           /* get "char" */
342 return AC;
343 }
344 
345 /* Continue an in-progress operation - cassette is in motion */
346 
ct_go_cont(UNIT * uptr,int32 AC)347 int32 ct_go_cont (UNIT *uptr, int32 AC)
348 {
349 int32 fnc = GET_FNC (ct_sra);
350 
351 switch (fnc) {                                          /* case on function */
352 
353     case SRA_READ:                                      /* read */
354         return ct_db;                                   /* return data */
355 
356     case SRA_WRITE:                                     /* write */
357         ct_db = AC & 0377;                              /* save data */
358         break;
359 
360     case SRA_CRC:                                       /* CRC */
361         if ((uptr->FNC & SRA_M_FNC) != SRA_CRC)         /* if not CRC */
362             uptr->FNC = SRA_CRC;                        /* start CRC seq */
363         if (!ct_write)                                  /* read? AC <- buf */
364             return ct_db;
365         break;
366 
367     default:
368         break;
369     }
370 
371 return AC;
372 }
373 
374 /* Unit service */
375 
ct_svc(UNIT * uptr)376 t_stat ct_svc (UNIT *uptr)
377 {
378 uint32 i, crc;
379 uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC];
380 t_mtrlnt tbc;
381 t_stat st, r;
382 
383 if ((uptr->flags & UNIT_ATT) == 0) {                    /* not attached? */
384     ct_updsta (uptr);                                   /* update status */
385     return (ct_stopioe? SCPE_UNATT: SCPE_OK);
386     }
387 if (((flgs & OP_REV) && sim_tape_bot (uptr)) ||         /* rev at BOT or */
388     ((flgs & OP_FWD) && sim_tape_eot (uptr))) {         /* fwd at EOT? */
389     ct_srb |= SRB_BEOT;                                 /* error */
390     ct_updsta (uptr);                                   /* op done */
391     return SCPE_OK;
392     }
393 
394 r = SCPE_OK;
395 switch (uptr->FNC) {                                    /* case on function */
396 
397     case SRA_READ:                                      /* read start */
398         st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */
399         if (st == MTSE_RECE)                            /* rec in err? */
400             ct_srb |= SRB_CRC;
401         else if (st != MTSE_OK) {                       /* other error? */
402             r = ct_map_err (uptr, st);                  /* map error */
403             break;
404             }
405         crc = ct_crc (ct_xb, ct_blnt);                  /* calculate CRC */
406         ct_xb[ct_blnt++] = (crc >> 8) & 0377;           /* append to buffer */
407         ct_xb[ct_blnt++] = crc & 0377;
408         uptr->FNC |= SRA_2ND;                           /* next state */
409         sim_activate (uptr, ct_ctime);                  /* sched next char */
410         return SCPE_OK;
411 
412     case SRA_READ|SRA_2ND:                              /* read char */
413         if (!ct_read_char ())                           /* read, overrun? */
414             break;
415         ct_set_df (TRUE);                               /* set data flag */
416         sim_activate (uptr, ct_ctime);                  /* sched next char */
417         return SCPE_OK;
418 
419     case SRA_WRITE:                                     /* write start */
420         for (i = 0; i < CT_MAXFR; i++)                  /* clear buffer */
421             ct_xb[i] = 0;
422         uptr->FNC |= SRA_2ND;                           /* next state */
423         sim_activate (uptr, ct_ctime);                  /* sched next char */
424         return SCPE_OK;
425 
426     case SRA_WRITE|SRA_2ND:                             /* write char */
427         if ((ct_bptr < CT_MAXFR) &&                     /* room in buf? */
428             ((uptr->pos + ct_bptr) < uptr->capac))      /* room on tape? */
429             ct_xb[ct_bptr++] = ct_db;                   /* store char */
430         ct_set_df (TRUE);                               /* set data flag */
431         sim_activate (uptr, ct_ctime);                  /* sched next char */
432         return SCPE_OK;
433 
434     case SRA_CRC:                                       /* CRC */
435         if (ct_write) {                                 /* write? */
436            if ((st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr))) /* write, err? */
437                r = ct_map_err (uptr, st);               /* map error */
438            break;                                       /* write done */
439            }
440         ct_read_char ();                                /* get second CRC */
441         ct_set_df (FALSE);                              /* set df */
442         uptr->FNC |= SRA_2ND;                           /* next state */
443         sim_activate (uptr, ct_ctime);
444         return SCPE_OK;
445 
446     case SRA_CRC|SRA_2ND:                               /* second read CRC */
447         if (ct_bptr != ct_blnt) {                       /* partial read? */
448             crc = ct_crc (ct_xb, ct_bptr);              /* actual CRC */
449             if (crc != 0)                               /* must be zero */
450                 ct_srb |= SRB_CRC;
451             }
452          break;                                         /* read done */
453 
454     case SRA_WFG:                                       /* write file gap */
455         if ((st = sim_tape_wrtmk (uptr)))               /* write tmk, err? */
456             r = ct_map_err (uptr, st);                  /* map error */
457         break;
458 
459     case SRA_REW:                                       /* rewind */
460         sim_tape_rewind (uptr);
461         ct_srb |= SRB_BEOT;                             /* set BOT */
462         break;
463 
464     case SRA_SRB:                                       /* space rev blk */
465         if ((st = sim_tape_sprecr (uptr, &tbc)))        /* space rev, err? */
466             r = ct_map_err (uptr, st);                  /* map error */
467          break;
468 
469     case SRA_SRF:                                       /* space rev file */
470         while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
471         r = ct_map_err (uptr, st);                      /* map error */
472         break;
473 
474     case SRA_SFF:                                       /* space fwd file */
475         while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ;
476         r = ct_map_err (uptr, st);                      /* map error */
477         break;
478 
479     default:                                            /* never get here! */
480         return SCPE_IERR;
481         }                                               /* end case */
482 
483 ct_updsta (uptr);                                       /* update status */
484 if (DEBUG_PRS (ct_dev)) fprintf (sim_deb,
485     ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n",
486     uptr->FNC, ct_sra, ct_srb, uptr->pos);
487 return r;
488 }
489 
490 /* Update controller status */
491 
ct_updsta(UNIT * uptr)492 uint32 ct_updsta (UNIT *uptr)
493 {
494 int32 srb;
495 
496 if (uptr == NULL) {                                     /* unit specified? */
497     uptr = ct_busy ();                                  /* use busy unit */
498     if ((uptr == NULL) && (ct_sra & SRA_ENAB))          /* none busy? */
499         uptr = ct_dev.units + GET_UNIT (ct_sra);        /* use sel unit */
500     }
501 else if (ct_srb & SRB_EOF)                              /* save gap */
502     uptr->UST |= UST_GAP;
503 if (uptr) {                                             /* any unit? */
504     ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY);               /* clear dyn flags */
505     if ((uptr->flags & UNIT_ATT) == 0)                  /* unattached? */
506         ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */
507     if (!sim_is_active (uptr)) {                        /* not busy? */
508         ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW;         /* ready, ~rew */
509         }
510     if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW))      /* locked or rew? */
511         ct_srb |= SRB_WLK;                              /* set locked */
512     }
513 if (ct_sra & SRA_ENAB)                                  /* can TA see TU60? */
514     srb = ct_srb;
515 else srb = 0;                                           /* no */
516 if ((ct_sra & SRA_IE) &&                                /* int enabled? */
517     (ct_df || (srb & (SRB_ALLERR|SRB_RDY))))            /* any flag? */
518     int_req |= INT_CT;                                  /* set int req */
519 else int_req &= ~INT_CT;                                /* no, clr int req */
520 return srb;
521 }
522 
523 /* Set data flag */
524 
ct_set_df(t_bool timchk)525 void ct_set_df (t_bool timchk)
526 {
527 if (ct_df && timchk)                                    /* flag still set? */
528     ct_srb |= SRB_TIM;
529 ct_df = 1;                                              /* set data flag */
530 if (ct_sra & SRA_IE)                                    /* if ie, int req */
531     int_req |= INT_CT;
532 return;
533 }
534 
535 /* Read character */
536 
ct_read_char(void)537 t_bool ct_read_char (void)
538 {
539 if (ct_bptr < ct_blnt) {                                /* more chars? */
540     ct_db = ct_xb[ct_bptr++];
541     return TRUE;
542     }
543 ct_db = 0;
544 ct_srb |= SRB_CRC;                                      /* overrun */
545 return FALSE;
546 }
547 
548 /* Test if controller busy */
549 
ct_busy(void)550 UNIT *ct_busy (void)
551 {
552 uint32 u;
553 UNIT *uptr;
554 
555 for (u = 0; u < CT_NUMDR; u++) {                        /* loop thru units */
556     uptr = ct_dev.units + u;
557     if (sim_is_active (uptr))
558         return uptr;
559     }
560 return NULL;
561 }
562 
563 /* Calculate CRC on buffer */
564 
ct_crc(uint8 * buf,uint32 cnt)565 uint32 ct_crc (uint8 *buf, uint32 cnt)
566 {
567 uint32 crc, i, j;
568 
569 crc = 0;
570 for (i = 0; i < cnt; i++) {
571     crc = crc ^ (((uint32) buf[i]) << 8);
572     for (j = 0; j < 8; j++) {
573         if (crc & 1)
574             crc = (crc >> 1) ^ 0xA001;
575         else crc = crc >> 1;
576         }
577     }
578 return crc;
579 }
580 
581 /* Map error status */
582 
ct_map_err(UNIT * uptr,t_stat st)583 t_stat ct_map_err (UNIT *uptr, t_stat st)
584 {
585 switch (st) {
586 
587     case MTSE_FMT:                                      /* illegal fmt */
588     case MTSE_UNATT:                                    /* unattached */
589         ct_srb |= SRB_CRC;
590     case MTSE_OK:                                       /* no error */
591         return SCPE_IERR;                               /* never get here! */
592 
593     case MTSE_TMK:                                      /* end of file */
594         ct_srb |= SRB_EOF;
595         break;
596 
597     case MTSE_IOERR:                                    /* IO error */
598         ct_srb |= SRB_CRC;                              /* set crc err */
599         if (ct_stopioe)
600             return SCPE_IOERR;
601         break;
602 
603     case MTSE_INVRL:                                    /* invalid rec lnt */
604         ct_srb |= SRB_CRC;                              /* set crc err */
605         return SCPE_MTRLNT;
606 
607     case MTSE_RECE:                                     /* record in error */
608     case MTSE_EOM:                                      /* end of medium */
609         ct_srb |= SRB_CRC;                              /* set crc err */
610         break;
611 
612     case MTSE_BOT:                                      /* reverse into BOT */
613         ct_srb |= SRB_BEOT;                             /* set BOT */
614         break;
615 
616     case MTSE_WRP:                                      /* write protect */
617         ct_srb |= SRB_WLE;                              /* set wlk err */
618         break;
619         }
620 
621 return SCPE_OK;
622 }
623 
624 /* Reset routine */
625 
ct_reset(DEVICE * dptr)626 t_stat ct_reset (DEVICE *dptr)
627 {
628 uint32 u;
629 UNIT *uptr;
630 
631 ct_sra = 0;
632 ct_srb = 0;
633 ct_df = 0;
634 ct_db = 0;
635 ct_write = 0;
636 ct_bptr = 0;
637 ct_blnt = 0;
638 int_req = int_req & ~INT_CT;                            /* clear interrupt */
639 for (u = 0; u < CT_NUMDR; u++) {                        /* loop thru units */
640     uptr = ct_dev.units + u;
641     sim_cancel (uptr);                                  /* cancel activity */
642     sim_tape_reset (uptr);                              /* reset tape */
643     }
644 if (ct_xb == NULL)
645     ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8));
646 if (ct_xb == NULL)
647     return SCPE_MEM;
648 return SCPE_OK;
649 }
650 
651 /* Attach routine */
652 
ct_attach(UNIT * uptr,char * cptr)653 t_stat ct_attach (UNIT *uptr, char *cptr)
654 {
655 t_stat r;
656 
657 r = sim_tape_attach (uptr, cptr);
658 if (r != SCPE_OK)
659     return r;
660 ct_updsta (NULL);
661 uptr->UST = 0;
662 return r;
663 }
664 
665 /* Detach routine */
666 
ct_detach(UNIT * uptr)667 t_stat ct_detach (UNIT* uptr)
668 {
669 t_stat r;
670 
671 if (!(uptr->flags & UNIT_ATT))                          /* check attached */
672     return SCPE_OK;
673 r = sim_tape_detach (uptr);
674 ct_updsta (NULL);
675 uptr->UST = 0;
676 return r;
677 }
678 
679 /* Bootstrap routine */
680 
681 #define BOOT_START 04000
682 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
683 
684 static const uint16 boot_rom[] = {
685     01237,          /* BOOT,    TAD M50     /change CRC to REW */
686     01206,          /* CRCCHK,  TAD L260    /crc op */
687     06704,          /*          KLSA        /load op */
688     06706,          /*          KGOA        /start */
689     06703,          /*          KSBF        /ready? */
690     05204,          /* RDCOD,   JMP .-1     /loop */
691     07264,          /* L260,    CML STA RAL /L = 1, AC = halt */
692     06702,          /*          KSEN        /error? */
693     07610,          /*          SKP CLA     /halt on any error */
694     03211,          /*          DCA .       /except REW or FFG */
695     03636,          /*          DCA I PTR   /TAD I PTR mustn't change L */
696     01205,          /*          TAD RDCOD   /read op */
697     06704,          /*          KLSA        /load op */
698     06706,          /*          KGOA        /start */
699     06701,          /* LOOP,    KSDF        /data ready? */
700     05216,          /*          JMP .-1     /loop */
701     07002,          /*          BSW         /to upper 6b */
702     07430,          /*          SZL         /second byte? */
703     01636,          /*          TAD I PTR   /yes */
704     07022,          /*          CML BSW     /swap back */
705     03636,          /*          DCA I PTR   /store in mem */
706     07420,          /*          SNL         /done with both bytes? */
707     02236,          /*          ISZ PTR     /yes, bump mem ptr */
708     02235,          /*          ISZ KNT     /done with record? */
709     05215,          /*          JMP LOOP    /next byte */
710     07346,          /*          STA CLL RTL */
711     07002,          /*          BSW         /AC = 7757 */
712     03235,          /*          STA KNT     /now read 200 byte record */
713     05201,          /*          JMP CRCCHK  /go check CRC */
714     07737,          /* KNT,     7737        /1's compl of byte count */
715     03557,          /* PTR,     3557        /load point */
716     07730,          /* M50,     7730        /CLA SPA SZL */
717     };
718 
ct_boot(int32 unitno,DEVICE * dptr)719 t_stat ct_boot (int32 unitno, DEVICE *dptr)
720 {
721 int32 i;
722 extern int32 saved_PC;
723 extern uint16 M[];
724 
725 if ((ct_dib.dev != DEV_CT) || unitno)                   /* only std devno */
726      return STOP_NOTSTD;
727 for (i = 0; i < BOOT_LEN; i++)
728     M[BOOT_START + i] = boot_rom[i];
729 saved_PC = BOOT_START;
730 return SCPE_OK;
731 }
732