First Commit of my working state
[simh.git] / PDP8 / pdp8_td.c
1 /* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) 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 This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones'
27 PDP8 simulator but tracks the hardware implementation more closely.
28
29 td TD8E/TU56 DECtape
30
31 23-Jun-06 RMS Fixed switch conflict in ATTACH
32 16-Aug-05 RMS Fixed C++ declaration and cast problems
33 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR
34
35 PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words.
36 Three file formats are supported:
37
38 18b/36b 256 words per block [256 x 18b]
39 16b 256 words per block [256 x 16b]
40 12b 129 words per block [129 x 12b]
41
42 When a 16b or 18/36b DECtape file is read in, it is converted to 12b format.
43
44 DECtape motion is measured in 3b lines. Time between lines is 33.33us.
45 Tape density is nominally 300 lines per inch. The format of a DECtape (as
46 taken from the TD8E formatter) is:
47
48 reverse end zone 8192 reverse end zone codes ~ 10 feet
49 reverse buffer 200 interblock codes
50 block 0
51 :
52 block n
53 forward buffer 200 interblock codes
54 forward end zone 8192 forward end zone codes ~ 10 feet
55
56 A block consists of five 18b header words, a tape-specific number of data
57 words, and five 18b trailer words. All systems except the PDP-8 use a
58 standard block length of 256 words; the PDP-8 uses a standard block length
59 of 86 words (x 18b = 129 words x 12b).
60
61 Because a DECtape file only contains data, the simulator cannot support
62 write timing and mark track and can only do a limited implementation
63 of non-data words. Read assumes that the tape has been conventionally
64 written forward:
65
66 header word 0 0
67 header word 1 block number (for forward reads)
68 header words 2,3 0
69 header word 4 checksum (for reverse reads)
70 :
71 trailer word 4 checksum (for forward reads)
72 trailer words 3,2 0
73 trailer word 1 block number (for reverse reads)
74 trailer word 0 0
75
76 Write modifies only the data words and dumps the non-data words in the
77 bit bucket.
78 */
79
80 #include "pdp8_defs.h"
81
82 #define DT_NUMDR 2 /* #drives */
83 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
84 #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */
85 #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */
86 #define UNIT_WLK (1 << UNIT_V_WLK)
87 #define UNIT_8FMT (1 << UNIT_V_8FMT)
88 #define UNIT_11FMT (1 << UNIT_V_11FMT)
89 #define STATE u3 /* unit state */
90 #define LASTT u4 /* last time update */
91 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
92
93 /* System independent DECtape constants */
94
95 #define DT_LPERMC 6 /* lines per mark track */
96 #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */
97 #define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */
98 #define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */
99
100 /* 16b, 18b, 36b DECtape constants */
101
102 #define D18_WSIZE 6 /* word sizein lines */
103 #define D18_BSIZE 384 /* block size in 12b */
104 #define D18_TSIZE 578 /* tape size */
105 #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
106 #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))
107 #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */
108
109 #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)
110 #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32))
111 #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16))
112
113 /* 12b DECtape constants */
114
115 #define D8_WSIZE 4 /* word size in lines */
116 #define D8_BSIZE 129 /* block size in 12b */
117 #define D8_TSIZE 1474 /* tape size */
118 #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
119 #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))
120 #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */
121 #define D8_FILSIZ (D8_CAPAC * sizeof (int16))
122
123 /* This controller */
124
125 #define DT_CAPAC D8_CAPAC /* default */
126 #define DT_WSIZE D8_WSIZE
127
128 /* Calculated constants, per unit */
129
130 #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
131 #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
132 #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
133 #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
134 #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)
135
136 #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))
137 #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))
138
139 /* Command register */
140
141 #define TDC_UNIT 04000 /* unit select */
142 #define TDC_FWDRV 02000 /* fwd/rev */
143 #define TDC_STPGO 01000 /* stop/go */
144 #define TDC_RW 00400 /* read/write */
145 #define TDC_MASK 07400 /* implemented */
146 #define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0)
147
148 /* Status register */
149
150 #define TDS_WLO 00200 /* write lock */
151 #define TDS_TME 00100 /* timing/sel err */
152
153 /* Mark track register and codes */
154
155 #define MTK_MASK 077
156 #define MTK_REV_END 055 /* rev end zone */
157 #define MTK_INTER 025 /* interblock */
158 #define MTK_FWD_BLK 026 /* fwd block */
159 #define MTK_REV_GRD 032 /* reverse guard */
160 #define MTK_FWD_PRE 010 /* lock, etc */
161 #define MTK_DATA 070 /* data */
162 #define MTK_REV_PRE 073 /* lock, etc */
163 #define MTK_FWD_GRD 051 /* fwd guard */
164 #define MTK_REV_BLK 045 /* rev block */
165 #define MTK_FWD_END 022 /* fwd end zone */
166
167 /* DECtape state */
168
169 #define STA_STOP 0 /* stopped */
170 #define STA_DEC 2 /* decelerating */
171 #define STA_ACC 4 /* accelerating */
172 #define STA_UTS 6 /* up to speed */
173 #define STA_DIR 1 /* fwd/rev */
174
175 #define ABS(x) (((x) < 0)? (-(x)): (x))
176 #define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1)
177
178 /* State and declarations */
179
180 int32 td_cmd = 0; /* command */
181 int32 td_dat = 0; /* data */
182 int32 td_mtk = 0; /* mark track */
183 int32 td_slf = 0; /* single line flag */
184 int32 td_qlf = 0; /* quad line flag */
185 int32 td_tme = 0; /* timing error flag */
186 int32 td_csum = 0; /* save check sum */
187 int32 td_qlctr = 0; /* quad line ctr */
188 int32 td_ltime = 20; /* interline time */
189 int32 td_dctime = 40000; /* decel time */
190 int32 td_stopoffr = 0;
191 static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */
192
193 DEVICE td_dev;
194 int32 td77 (int32 IR, int32 AC);
195 t_stat td_svc (UNIT *uptr);
196 t_stat td_reset (DEVICE *dptr);
197 t_stat td_attach (UNIT *uptr, char *cptr);
198 t_stat td_detach (UNIT *uptr);
199 t_stat td_boot (int32 unitno, DEVICE *dptr);
200 t_bool td_newsa (int32 newf);
201 t_bool td_setpos (UNIT *uptr);
202 int32 td_header (UNIT *uptr, int32 blk, int32 line);
203 int32 td_trailer (UNIT *uptr, int32 blk, int32 line);
204 int32 td_read (UNIT *uptr, int32 blk, int32 line);
205 void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb);
206 int32 td_set_mtk (int32 code, int32 u, int32 k);
207 t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc);
208
209 extern uint16 M[];
210 extern int32 sim_switches;
211 extern int32 sim_is_running;
212
213 /* TD data structures
214
215 td_dev DT device descriptor
216 td_unit DT unit list
217 td_reg DT register list
218 td_mod DT modifier list
219 */
220
221 DIB td_dib = { DEV_TD8E, 1, { &td77 } };
222
223 UNIT td_unit[] = {
224 { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
225 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
226 { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
227 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }
228 };
229
230 REG td_reg[] = {
231 { GRDATA (TDCMD, td_cmd, 8, 4, 8) },
232 { ORDATA (TDDAT, td_dat, 12) },
233 { ORDATA (TDMTK, td_mtk, 6) },
234 { FLDATA (TDSLF, td_slf, 0) },
235 { FLDATA (TDQLF, td_qlf, 0) },
236 { FLDATA (TDTME, td_tme, 0) },
237 { ORDATA (TDQL, td_qlctr, 2) },
238 { ORDATA (TDCSUM, td_csum, 6), REG_RO },
239 { DRDATA (LTIME, td_ltime, 31), REG_NZ | PV_LEFT },
240 { DRDATA (DCTIME, td_dctime, 31), REG_NZ | PV_LEFT },
241 { URDATA (POS, td_unit[0].pos, 10, T_ADDR_W, 0,
242 DT_NUMDR, PV_LEFT | REG_RO) },
243 { URDATA (STATT, td_unit[0].STATE, 8, 18, 0,
244 DT_NUMDR, REG_RO) },
245 { URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0,
246 DT_NUMDR, REG_HRO) },
247 { FLDATA (STOP_OFFR, td_stopoffr, 0) },
248 { ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO },
249 { NULL }
250 };
251
252 MTAB td_mod[] = {
253 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
254 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
255 { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },
256 { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },
257 { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },
258 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
259 &set_dev, &show_dev, NULL },
260 { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos },
261 { 0 }
262 };
263
264 DEVICE td_dev = {
265 "TD", td_unit, td_reg, td_mod,
266 DT_NUMDR, 8, 24, 1, 8, 12,
267 NULL, NULL, &td_reset,
268 &td_boot, &td_attach, &td_detach,
269 &td_dib, DEV_DISABLE | DEV_DIS
270 };
271
272 /* IOT routines */
273
274 int32 td77 (int32 IR, int32 AC)
275 {
276 int32 pulse = IR & 07;
277 int32 u = TDC_GETUNIT (td_cmd); /* get unit */
278 int32 diff, t;
279
280 switch (pulse) {
281
282 case 01: /* SDSS */
283 if (td_slf) return AC | IOT_SKP;
284 break;
285
286 case 02: /* SDST */
287 if (td_tme) return AC | IOT_SKP;
288 break;
289
290 case 03: /* SDSQ */
291 if (td_qlf) return AC | IOT_SKP;
292 break;
293
294 case 04: /* SDLC */
295 td_tme = 0; /* clear tim err */
296 diff = (td_cmd ^ AC) & TDC_MASK; /* cmd changes */
297 td_cmd = AC & TDC_MASK; /* update cmd */
298 if ((diff != 0) && (diff != TDC_RW)) { /* signif change? */
299 if (td_newsa (td_cmd)) /* new command */
300 return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON);
301 }
302 break;
303
304 case 05: /* SDLD */
305 td_slf = 0; /* clear flags */
306 td_qlf = 0;
307 td_qlctr = 0;
308 td_dat = AC; /* load data reg */
309 break;
310
311 case 06: /* SDRC */
312 td_slf = 0; /* clear flags */
313 td_qlf = 0;
314 td_qlctr = 0;
315 t = td_cmd | td_mtk; /* form status */
316 if (td_tme || !(td_unit[u].flags & UNIT_ATT)) /* tim/sel err? */
317 t = t | TDS_TME;
318 if (td_unit[u].flags & UNIT_WPRT) /* write locked? */
319 t = t | TDS_WLO;
320 return t; /* return status */
321
322 case 07: /* SDRD */
323 td_slf = 0; /* clear flags */
324 td_qlf = 0;
325 td_qlctr = 0;
326 return td_dat; /* return data */
327 }
328
329 return AC;
330 }
331
332 /* Command register change (start/stop, forward/reverse, new unit)
333
334 1. If change in motion, stop to start
335 - schedule up to speed
336 - set function as next state
337 2. If change in motion, start to stop, or change in direction
338 - schedule stop
339 */
340
341 t_bool td_newsa (int32 newf)
342 {
343 int32 prev_mving, new_mving, prev_dir, new_dir;
344 UNIT *uptr;
345
346 uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */
347 if ((uptr->flags & UNIT_ATT) == 0) return FALSE; /* new unit attached? */
348
349 new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */
350 prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */
351 new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */
352 prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */
353
354 td_mtk = 0; /* mark trk reg cleared */
355
356 if (!prev_mving && !new_mving) return FALSE; /* stop from stop? */
357
358 if (new_mving && !prev_mving) { /* start from stop? */
359 if (td_setpos (uptr)) return TRUE; /* update pos */
360 sim_cancel (uptr); /* stop current */
361 sim_activate (uptr, td_dctime - (td_dctime >> 2)); /* sched accel */
362 uptr->STATE = STA_ACC | new_dir; /* set status */
363 td_slf = td_qlf = td_qlctr = 0; /* clear state */
364 return FALSE;
365 }
366
367 if ((prev_mving && !new_mving) || /* stop from moving? */
368 (prev_dir != new_dir)) { /* dir chg while moving? */
369 if (uptr->STATE >= STA_ACC) { /* not stopping? */
370 if (td_setpos (uptr)) return TRUE; /* update pos */
371 sim_cancel (uptr); /* stop current */
372 sim_activate (uptr, td_dctime); /* schedule decel */
373 uptr->STATE = STA_DEC | prev_dir; /* set status */
374 td_slf = td_qlf = td_qlctr = 0; /* clear state */
375 }
376 return FALSE;
377 }
378
379 return FALSE;
380 }
381
382 /* Update DECtape position
383
384 DECtape motion is modeled as a constant velocity, with linear
385 acceleration and deceleration. The motion equations are as follows:
386
387 t = time since operation started
388 tmax = time for operation (accel, decel only)
389 v = at speed velocity in lines (= 1/td_ltime)
390
391 Then:
392 at speed dist = t * v
393 accel dist = (t^2 * v) / (2 * tmax)
394 decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)
395
396 This routine uses the relative (integer) time, rather than the absolute
397 (floating point) time, to allow save and restore of the start times.
398 */
399
400 t_bool td_setpos (UNIT *uptr)
401 {
402 uint32 new_time, ut, ulin, udelt;
403 int32 delta;
404
405 new_time = sim_grtime (); /* current time */
406 ut = new_time - uptr->LASTT; /* elapsed time */
407 if (ut == 0) return FALSE; /* no time gone? exit */
408 uptr->LASTT = new_time; /* update last time */
409 switch (uptr->STATE & ~STA_DIR) { /* case on motion */
410
411 case STA_STOP: /* stop */
412 delta = 0;
413 break;
414
415 case STA_DEC: /* slowing */
416 ulin = ut / (uint32) td_ltime;
417 udelt = td_dctime / td_ltime;
418 delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
419 break;
420
421 case STA_ACC: /* accelerating */
422 ulin = ut / (uint32) td_ltime;
423 udelt = (td_dctime - (td_dctime >> 2)) / td_ltime;
424 delta = (ulin * ulin) / (2 * udelt);
425 break;
426
427 case STA_UTS: /* at speed */
428 delta = ut / (uint32) td_ltime;
429 break;
430 }
431
432 if (uptr->STATE & STA_DIR) uptr->pos = uptr->pos - delta; /* update pos */
433 else uptr->pos = uptr->pos + delta;
434 if (((int32) uptr->pos < 0) ||
435 ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {
436 detach_unit (uptr); /* off reel */
437 sim_cancel (uptr); /* no timing pulses */
438 return TRUE;
439 }
440 return FALSE;
441 }
442
443 /* Unit service - unit is either changing speed, or it is up to speed */
444
445 t_stat td_svc (UNIT *uptr)
446 {
447 int32 mot = uptr->STATE & ~STA_DIR;
448 int32 dir = uptr->STATE & STA_DIR;
449 int32 unum = uptr - td_dev.units;
450 int32 su = TDC_GETUNIT (td_cmd);
451 int32 mtkb, datb;
452
453 /* Motion cases
454
455 Decelerating - if go, next state must be accel as specified by td_cmd
456 Accelerating - next state must be up to speed, fall through
457 Up to speed - process line */
458
459 if (mot == STA_STOP) return SCPE_OK; /* stopped? done */
460 if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
461 uptr->STATE = uptr->pos = 0; /* also done */
462 return SCPE_UNATT;
463 }
464
465 switch (mot) { /* case on motion */
466
467 case STA_DEC: /* deceleration */
468 if (td_setpos (uptr)) /* upd pos; off reel? */
469 return IORETURN (td_stopoffr, STOP_DTOFF);
470 if ((unum != su) || !(td_cmd & TDC_STPGO)) /* not sel or stop? */
471 uptr->STATE = 0; /* stop */
472 else { /* selected and go */
473 uptr->STATE = STA_ACC | /* accelerating */
474 ((td_cmd & TDC_FWDRV)? STA_DIR: 0); /* in new dir */
475 sim_activate (uptr, td_dctime - (td_dctime >> 2));
476 }
477 return SCPE_OK;
478
479 case STA_ACC: /* accelerating */
480 if (td_setpos (uptr)) /* upd pos; off reel? */
481 return IORETURN (td_stopoffr, STOP_DTOFF);
482 uptr->STATE = STA_UTS | dir; /* set up to speed */
483 break;
484
485 case STA_UTS: /* up to speed */
486 if (dir) uptr->pos = uptr->pos - 1; /* adjust position */
487 else uptr->pos = uptr->pos + 1;
488 uptr->LASTT = sim_grtime (); /* save time */
489 if (((int32) uptr->pos < 0) || /* off reel? */
490 (uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) {
491 detach_unit (uptr);
492 return IORETURN (td_stopoffr, STOP_DTOFF);
493 }
494 break; /* check function */
495 }
496
497 /* At speed - process the current line
498
499 Once the TD8E is running at speed, it operates line by line. If reading,
500 the current mark track bit is shifted into the mark track register, and
501 the current data nibble (3b) is shifted into the data register. If
502 writing, the current mark track bit is shifted into the mark track
503 register, the top nibble from the data register is written to tape, and
504 the data register is shifted up. The complexity here comes from
505 synthesizing the mark track, based on tape position, and the header data. */
506
507 sim_activate (uptr, td_ltime); /* sched next line */
508 if (unum != su) return SCPE_OK; /* not sel? done */
509 td_slf = 1; /* set single */
510 td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */
511 if (td_qlctr == 0) { /* lines mod 4? */
512 if (td_qlf) { /* quad line set? */
513 td_tme = 1; /* timing error */
514 td_cmd = td_cmd & ~TDC_RW; /* clear write */
515 }
516 else td_qlf = 1; /* no, set quad */
517 }
518
519 datb = 0; /* assume no data */
520 if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */
521 mtkb = MTK_BIT (MTK_REV_END, uptr->pos);
522 else if (uptr->pos < DT_EZLIN) /* rev buffer? */
523 mtkb = MTK_BIT (MTK_INTER, uptr->pos);
524 else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */
525 int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */
526 int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */
527 if (lineno < DT_HTLIN) { /* header? */
528 if ((td_cmd & TDC_RW) == 0) /* read? */
529 datb = td_header (uptr, blkno, lineno); /* get nibble */
530 }
531 else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) { /* data? */
532 if (td_cmd & TDC_RW) /* write? */
533 td_write (uptr, blkno, /* write data nibble */
534 lineno - DT_HTLIN, /* data rel line num */
535 (td_dat >> 9) & 07);
536 else datb = td_read (uptr, blkno, /* no, read */
537 lineno - DT_HTLIN);
538 }
539 else if ((td_cmd & TDC_RW) == 0) /* trailer; read? */
540 datb = td_trailer (uptr, blkno, lineno - /* get trlr nibble */
541 (DTU_LPERB (uptr) - DT_HTLIN));
542 mtkb = tdb_mtk[unum][lineno];
543 }
544 else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN))
545 mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */
546 else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */
547
548 if (dir) { /* reverse? */
549 mtkb = mtkb ^ 01; /* complement mark bit, */
550 datb = datb ^ 07; /* data bits */
551 }
552 td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */
553 td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */
554 return SCPE_OK;
555 }
556
557 /* Header read - reads out 18b words in 3b increments
558
559 word lines contents
560 0 0-5 0
561 1 6-11 block number
562 2 12-17 0
563 3 18-23 0
564 4 24-29 reverse checksum (0777777)
565 */
566
567 int32 td_header (UNIT *uptr, int32 blk, int32 line)
568 {
569 int32 nibp;
570
571 switch (line) {
572
573 case 8: case 9: case 10: case 11: /* block num */
574 nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC));
575 return (blk >> nibp) & 07;
576
577 case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */
578 return 07; /* 777777 */
579
580 default:
581 return 0;
582 }
583 }
584
585 /* Trailer read - reads out 18b words in 3b increments
586 Checksum is stored to avoid double calculation
587
588 word lines contents
589 0 0-5 forward checksum (lines 0-1, rest 0)
590 1 6-11 0
591 2 12-17 0
592 3 18-23 reverse block mark
593 4 24-29 0
594
595 Note that the reverse block mark (when read forward) appears
596 as the complement obverse (3b nibbles swapped end for end and
597 complemented).
598 */
599
600 int32 td_trailer (UNIT *uptr, int32 blk, int32 line)
601 {
602 int32 nibp, i, ba;
603 int16 *fbuf= (int16 *) uptr->filebuf;
604
605 switch (line) {
606
607 case 0:
608 td_csum = 07777; /* init csum */
609 ba = blk * DTU_BSIZE (uptr);
610 for (i = 0; i < DTU_BSIZE (uptr); i++) /* loop thru buf */
611 td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777;
612 td_csum = ((td_csum >> 6) ^ td_csum) & 077;
613 return (td_csum >> 3) & 07;
614
615 case 1:
616 return (td_csum & 07);
617
618 case 18: case 19: case 20: case 21:
619 nibp = 3 * (line % DT_LPERMC);
620 return ((blk >> nibp) & 07) ^ 07;
621
622 default:
623 return 0;
624 }
625 }
626
627 /* Data read - convert block number/data line # to offset in data array */
628
629 int32 td_read (UNIT *uptr, int32 blk, int32 line)
630 {
631 int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */
632 uint32 ba = blk * DTU_BSIZE (uptr); /* block base */
633 int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */
634
635 ba = ba + (line / DT_WSIZE); /* block addr */
636 return (fbuf[ba] >> nibp) & 07; /* get data nibble */
637 }
638
639 /* Data write - convert block number/data line # to offset in data array */
640
641 void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat)
642 {
643 int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */
644 uint32 ba = blk * DTU_BSIZE (uptr); /* block base */
645 int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */
646
647 ba = ba + (line / DT_WSIZE); /* block addr */
648 fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */
649 if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; /* upd length */
650 return;
651 }
652
653 /* Reset routine */
654
655 t_stat td_reset (DEVICE *dptr)
656 {
657 int32 i;
658 UNIT *uptr;
659
660 for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */
661 uptr = td_dev.units + i;
662 if (sim_is_running) { /* CAF? */
663 if (uptr->STATE >= STA_ACC) { /* accel or uts? */
664 if (td_setpos (uptr)) continue; /* update pos */
665 sim_cancel (uptr);
666 sim_activate (uptr, td_dctime); /* sched decel */
667 uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR);
668 }
669 }
670 else {
671 sim_cancel (uptr); /* sim reset */
672 uptr->STATE = 0;
673 uptr->LASTT = sim_grtime ();
674 }
675 }
676 td_slf = td_qlf = td_qlctr = 0; /* clear state */
677 td_cmd = td_dat = td_mtk = 0;
678 td_csum = 0;
679 return SCPE_OK;
680 }
681
682 /* Bootstrap routine - OS/8 only
683
684 1) Read reverse until reverse end zone (mark track is complement obverse)
685 2) Read forward until mark track code 031. This is a composite code from
686 the last 4b of the forward block number and the first two bits of the
687 reverse guard (01 -0110 01- 1010). There are 16 lines before the first
688 data word.
689 3) Store data words from 7354 to end of page. This includes header and
690 trailer words.
691 4) Continue at location 7400.
692 */
693
694 #define BOOT_START 07300
695 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
696
697 static const uint16 boot_rom[] = {
698 01312, /* ST, TAD L4MT ;=2000, reverse */
699 04312, /* JMS L4MT ; rev lk for 022 */
700 04312, /* JMS L4MT ; fwd lk for 031 */
701 06773, /* DAT, SDSQ ; wait for 12b */
702 05303, /* JMP .-1 */
703 06777, /* SDRD ; read word */
704 03726, /* DCA I BUF ; store */
705 02326, /* ISZ BUF ; incr ptr */
706 05303, /* JMP DAT ; if not 0, cont */
707 05732, /* JMP I SCB ; jump to boot */
708 02000, /* L4MT,2000 ; overwritten */
709 01300, /* TAD ST ; =1312, go */
710 06774, /* SDLC ; new command */
711 06771, /* MTK, SDSS ; wait for mark */
712 05315, /* JMP .-1 */
713 06776, /* SDRC ; get mark code */
714 00331, /* AND K77 ; mask to 6b */
715 01327, /* CMP, TAD MCD ; got target code? */
716 07640, /* SZA CLA ; skip if yes */
717 05315, /* JMP MTK ; wait for mark */
718 02321, /* ISZ CMP ; next target */
719 05712, /* JMP I L4MT ; exit */
720 07354, /* BUF, 7354 ; loading point */
721 07756, /* MCD, -22 ; target 1 */
722 07747, /* -31 ; target 2 */
723 00077, /* 77 ; mask */
724 07400 /* SCB, 7400 ; secondary boot */
725 };
726
727 t_stat td_boot (int32 unitno, DEVICE *dptr)
728 {
729 int32 i;
730 extern int32 saved_PC;
731
732 if (unitno) return SCPE_ARG; /* only unit 0 */
733 if (td_dib.dev != DEV_TD8E) return STOP_NOTSTD; /* only std devno */
734 td_unit[unitno].pos = DT_EZLIN;
735 for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
736 saved_PC = BOOT_START;
737 return SCPE_OK;
738 }
739
740 /* Attach routine
741
742 Determine 12b, 16b, or 18b/36b format
743 Allocate buffer
744 If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
745 If 12b, read data into buffer
746 Set up mark track bit array
747 */
748
749 t_stat td_attach (UNIT *uptr, char *cptr)
750 {
751 uint32 pdp18b[D18_NBSIZE];
752 uint16 pdp11b[D18_NBSIZE], *fbuf;
753 int32 i, k, mtkpb;
754 int32 u = uptr - td_dev.units;
755 t_stat r;
756 uint32 ba, sz;
757
758 r = attach_unit (uptr, cptr); /* attach */
759 if (r != SCPE_OK) return r; /* fail? */
760 if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */
761 uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
762 if (sim_switches & SWMASK ('F')) /* att 18b? */
763 uptr->flags = uptr->flags & ~UNIT_8FMT;
764 else if (sim_switches & SWMASK ('S')) /* att 16b? */
765 uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
766 else if (!(sim_switches & SWMASK ('A')) && /* autosize? */
767 (sz = sim_fsize (uptr->fileref))) {
768 if (sz == D11_FILSIZ)
769 uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
770 else if (sz > D8_FILSIZ)
771 uptr->flags = uptr->flags & ~UNIT_8FMT;
772 }
773 }
774 uptr->capac = DTU_CAPAC (uptr); /* set capacity */
775 uptr->filebuf = calloc (uptr->capac, sizeof (int16));
776 if (uptr->filebuf == NULL) { /* can't alloc? */
777 detach_unit (uptr);
778 return SCPE_MEM;
779 }
780 fbuf = (uint16 *) uptr->filebuf; /* file buffer */
781 printf ("%s%d: ", sim_dname (&td_dev), u);
782 if (uptr->flags & UNIT_8FMT) printf ("12b format");
783 else if (uptr->flags & UNIT_11FMT) printf ("16b format");
784 else printf ("18b/36b format");
785 printf (", buffering file in memory\n");
786 if (uptr->flags & UNIT_8FMT) /* 12b? */
787 uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16),
788 uptr->capac, uptr->fileref);
789 else { /* 16b/18b */
790 for (ba = 0; ba < uptr->capac; ) { /* loop thru file */
791 if (uptr->flags & UNIT_11FMT) {
792 k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref);
793 for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i];
794 }
795 else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref);
796 if (k == 0) break;
797 for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0;
798 for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */
799 fbuf[ba] = (pdp18b[k] >> 6) & 07777;
800 fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |
801 ((pdp18b[k + 1] >> 12) & 077);
802 fbuf[ba + 2] = pdp18b[k + 1] & 07777;
803 ba = ba + 3;
804 } /* end blk loop */
805 } /* end file loop */
806 uptr->hwmark = ba;
807 } /* end else */
808 uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */
809 uptr->pos = DT_EZLIN; /* beyond leader */
810 uptr->LASTT = sim_grtime (); /* last pos update */
811 uptr->STATE = STA_STOP; /* stopped */
812
813 mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */
814 k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */
815 k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */
816 k = td_set_mtk (MTK_REV_GRD, u, k);
817 for (i = 0; i < 4; i++) k = td_set_mtk (MTK_FWD_PRE, u, k);
818 for (i = 0; i < (mtkpb - 4); i++) k = td_set_mtk (MTK_DATA, u, k);
819 for (i = 0; i < 4; i++) k = td_set_mtk (MTK_REV_PRE, u, k);
820 k = td_set_mtk (MTK_FWD_GRD, u, k);
821 k = td_set_mtk (MTK_REV_BLK, u, k);
822 k = td_set_mtk (MTK_INTER, u, k);
823 return SCPE_OK;
824 }
825
826 /* Detach routine
827
828 If 12b, write buffer to file
829 If 16b or 18b, convert 12b buffer to 16b or 18b and write to file
830 Deallocate buffer
831 */
832
833 t_stat td_detach (UNIT* uptr)
834 {
835 uint32 pdp18b[D18_NBSIZE];
836 uint16 pdp11b[D18_NBSIZE], *fbuf;
837 int32 i, k;
838 int32 u = uptr - td_dev.units;
839 uint32 ba;
840
841 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;
842 fbuf = (uint16 *) uptr->filebuf; /* file buffer */
843 if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */
844 printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u);
845 rewind (uptr->fileref); /* start of file */
846 if (uptr->flags & UNIT_8FMT) /* PDP8? */
847 fxwrite (uptr->filebuf, sizeof (uint16), /* write file */
848 uptr->hwmark, uptr->fileref);
849 else { /* 16b/18b */
850 for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */
851 for (k = 0; k < D18_NBSIZE; k = k + 2) {
852 pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |
853 ((uint32) (fbuf[ba + 1] >> 6) & 077);
854 pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |
855 ((uint32) (fbuf[ba + 2] & 07777));
856 ba = ba + 3;
857 } /* end loop blk */
858 if (uptr->flags & UNIT_11FMT) { /* 16b? */
859 for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i];
860 fxwrite (pdp11b, sizeof (uint16),
861 D18_NBSIZE, uptr->fileref);
862 }
863 else fxwrite (pdp18b, sizeof (uint32),
864 D18_NBSIZE, uptr->fileref);
865 } /* end loop buf */
866 } /* end else */
867 if (ferror (uptr->fileref)) perror ("I/O error");
868 } /* end if hwmark */
869 free (uptr->filebuf); /* release buf */
870 uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */
871 uptr->filebuf = NULL; /* clear buf ptr */
872 uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */
873 uptr->capac = DT_CAPAC; /* default size */
874 uptr->pos = uptr->STATE = 0;
875 sim_cancel (uptr); /* no more pulses */
876 return detach_unit (uptr);
877 }
878
879 /* Set mark track code into bit array */
880
881 int32 td_set_mtk (int32 code, int32 u, int32 k)
882 {
883 int32 i;
884
885 for (i = 5; i >= 0; i--) tdb_mtk[u][k++] = (code >> i) & 1;
886 return k;
887 }
888
889 /* Show position */
890
891 t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc)
892 {
893 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;
894 if (uptr->pos < DT_EZLIN) /* rev end zone? */
895 fprintf (st, "Reverse end zone\n");
896 else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */
897 int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */
898 int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */
899 fprintf (st, "Block %d, line %d, ", blkno, lineno);
900 if (lineno < DT_HTLIN) /* header? */
901 fprintf (st, "header cell %d, nibble %d\n",
902 lineno / DT_LPERMC, lineno % DT_LPERMC);
903 else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) /* data? */
904 fprintf (st, "data word %d, nibble %d\n",
905 (lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE);
906 else fprintf (st, "trailer cell %d, nibble %d\n",
907 (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC,
908 (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC);
909 }
910 else fprintf (st, "Forward end zone\n"); /* fwd end zone */
911 return SCPE_OK;
912 }
913