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