1 /* pdp18b_dt.c: 18b DECtape simulator
3 Copyright (c) 1993-2006, Robert M Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
26 dt (PDP-4, PDP-7) Type 550/555 DECtape
27 (PDP-9) TC02/TU55 DECtape
28 (PDP-15) TC15/TU56 DECtape
30 23-Jun-06 RMS Fixed switch conflict in ATTACH
31 Revised Type 550 header based on DECTOG formatter
32 13-Jun-06 RMS Fixed checksum calculation bug in Type 550
33 16-Aug-05 RMS Fixed C++ declaration and cast problems
34 25-Jan-04 RMS Revised for device debug support
35 14-Jan-04 RMS Revised IO device call interface
36 Changed sim_fsize calling sequence, added STOP_OFFR
37 26-Oct-03 RMS Cleaned up buffer copy code
38 18-Oct-03 RMS Fixed reverse checksum in read all
39 Added DECtape off reel message
41 25-Apr-03 RMS Revised for extended file support
42 14-Mar-03 RMS Fixed variable size interaction with save/restore
43 17-Oct-02 RMS Fixed bug in end of reel logic
44 05-Oct-02 RMS Added DIB, device number support
45 12-Sep-02 RMS Added 16b format support
46 13-Aug-02 RMS Corrected Type 550 unit select logic
47 25-Jul-02 RMS Added PDP-4 support
48 30-May-02 RMS Widened POS to 32b
49 10-Feb-02 RMS Added PDP-7 support
50 06-Jan-02 RMS Revised enable/disable support
51 29-Nov-01 RMS Added read only unit support
52 25-Nov-01 RMS Revised interrupt structure
53 Changed POS, STATT, LASTT, FLG to arrays
54 29-Aug-01 RMS Added casts to PDP-8 unpack routine
55 17-Jul-01 RMS Moved function prototype
56 11-May-01 RMS Fixed bug in reset
57 26-Apr-01 RMS Added device enable/disable support
58 15-Mar-01 RMS Added 129th word to PDP-8 format
60 18b DECtapes are represented in memory by fixed length buffer of 32b words.
61 Three file formats are supported:
63 18b/36b 256 words per block [256 x 18b]
64 16b 256 words per block [256 x 16b]
65 12b 129 words per block [129 x 12b]
67 When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format.
69 DECtape motion is measured in 3b lines. Time between lines is 33.33us.
70 Tape density is nominally 300 lines per inch. The format of a DECtape (as
71 taken from the PDP-7 formatter) is:
73 reverse end zone 7144 reverse end zone codes ~ 12 feet
74 reverse buffer 200 interblock codes
78 forward buffer 200 interblock codes
79 forward end zone 7144 forward end zone codes ~ 12 feet
81 A block consists of five 18b header words, a tape-specific number of data
82 words, and five 18b trailer words. All systems except the PDP-8 use a
83 standard block length of 256 words; the PDP-8 uses a standard block length
84 of 86 words (x 18b = 129 words x 12b). PDP-4/7 DECtapes came in two
85 formats. The first 5 controllers used a 4 word header/trailer (missing
86 word 0/4). All later serial numbers used the standard header. The later,
87 standard header/trailer is simulated here.
89 Because a DECtape file only contains data, the simulator cannot support
90 write timing and mark track and can only do a limited implementation
91 of read all and write all. Read all assumes that the tape has been
92 conventionally written forward:
95 header word 1 block number (for forward reads)
97 header word 4 checksum (for reverse reads)
99 trailer word 4 checksum (for forward reads)
101 trailer word 1 block number (for reverse reads)
104 Write all writes only the data words and dumps the interblock words in the
107 The Type 550 controller has a 4b unit select field, for units 1-8; the TC02
108 has a 3b unit select field, with unit 8 being represented as 0. The code
109 assumes that the GETUNIT macro returns a unit number in the range of 0-7,
110 with 8 represented as 0, and an invalid unit as -1.
113 #include "pdp18b_defs.h"
115 #define DT_NUMDR 8 /* #drives */
116 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
117 #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */
118 #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */
119 #define UNIT_WLK (1 << UNIT_V_WLK)
120 #define UNIT_8FMT (1 << UNIT_V_8FMT)
121 #define UNIT_11FMT (1 << UNIT_V_11FMT)
122 #define STATE u3 /* unit state */
123 #define LASTT u4 /* last time update */
124 #define DT_WC 030 /* word count */
125 #define DT_CA 031 /* current addr */
126 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
128 /* System independent DECtape constants */
130 #define DT_LPERMC 6 /* lines per mark track */
131 #define DT_BLKWD 1 /* blk no word in h/t */
132 #define DT_CSMWD 4 /* checksum word in h/t */
133 #define DT_HTWRD 5 /* header/trailer words */
134 #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */
135 #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */
136 #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */
137 #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */
138 #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */
140 /* 16b, 18b, 36b DECtape constants */
142 #define D18_WSIZE 6 /* word size in lines */
143 #define D18_BSIZE 256 /* block size in 18b */
144 #define D18_TSIZE 578 /* tape size */
145 #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
146 #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))
147 #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */
148 #define D11_FILSIZ (D18_CAPAC * sizeof (int16))
150 /* 12b DECtape constants */
152 #define D8_WSIZE 4 /* word size in lines */
153 #define D8_BSIZE 86 /* block size in 18b */
154 #define D8_TSIZE 1474 /* tape size */
155 #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
156 #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))
157 #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */
159 #define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE)
160 #define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16))
162 /* This controller */
164 #define DT_CAPAC D18_CAPAC /* default */
165 #define DT_WSIZE D18_WSIZE
167 /* Calculated constants, per unit */
169 #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
170 #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
171 #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
172 #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
173 #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)
175 #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))
176 #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))
177 #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE)
178 #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN)
179 #define DT_QREZ(u) (((u)->pos) < DT_EZLIN)
180 #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u)))
181 #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u))
183 /* Status register A */
185 #if defined (TC02) /* TC02/TC15 */
186 #define DTA_V_UNIT 15 /* unit select */
187 #define DTA_M_UNIT 07
188 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT)
189 #define DTA_V_MOT 13 /* motion */
191 #define DTA_V_MODE 12 /* mode */
192 #define DTA_V_FNC 9 /* function */
194 #define FNC_MOVE 00 /* move */
195 #define FNC_SRCH 01 /* search */
196 #define FNC_READ 02 /* read */
197 #define FNC_RALL 03 /* read all */
198 #define FNC_WRIT 04 /* write */
199 #define FNC_WALL 05 /* write all */
200 #define FNC_WMRK 06 /* write timing */
201 #define DTA_V_ENB 8 /* int enable */
202 #define DTA_V_CERF 7 /* clr error flag */
203 #define DTA_V_CDTF 6 /* clr DECtape flag */
204 #define DTA_FWDRV (1u << (DTA_V_MOT + 1))
205 #define DTA_STSTP (1u << DTA_V_MOT)
206 #define DTA_MODE (1u << DTA_V_MODE)
207 #define DTA_ENB (1u << DTA_V_ENB)
208 #define DTA_CERF (1u << DTA_V_CERF)
209 #define DTA_CDTF (1u << DTA_V_CDTF)
210 #define DTA_RW (0777700 & ~(DTA_CERF | DTA_CDTF))
211 #define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT)
212 #define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \
217 #define DTA_V_UNIT 12 /* unit select */
218 #define DTA_M_UNIT 017
219 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT)
220 #define DTA_V_MOT 4 /* motion */
222 #define DTA_V_FNC 0 /* function */
224 #define FNC_MOVE 00 /* move */
225 #define FNC_SRCH 01 /* search */
226 #define FNC_READ 02 /* read */
227 #define FNC_WRIT 03 /* write */
228 #define FNC_RALL 05 /* read all */
229 #define FNC_WALL 06 /* write all */
230 #define FNC_WMRK 07 /* write timing */
231 #define DTA_STSTP (1u << (DTA_V_MOT + 1))
232 #define DTA_FWDRV (1u << DTA_V_MOT)
233 #define DTA_MODE 0 /* not implemented */
235 #define DTA_GETUNIT(x) map_unit[(((x) >> DTA_V_UNIT) & DTA_M_UNIT)]
236 #define DT_UPDINT if (dtsb & (DTB_DTF | DTB_BEF | DTB_ERF)) \
241 #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT)
242 #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC)
244 /* Status register B */
246 #if defined (TC02) /* TC02/TC15 */
247 #define DTB_V_ERF 17 /* error flag */
248 #define DTB_V_MRK 16 /* mark trk err */
249 #define DTB_V_END 15 /* end zone err */
250 #define DTB_V_SEL 14 /* select err */
251 #define DTB_V_PAR 13 /* parity err */
252 #define DTB_V_TIM 12 /* timing err */
253 #define DTB_V_DTF 6 /* DECtape flag */
254 #define DTB_ERF (1u << DTB_V_ERF)
255 #define DTB_MRK (1u << DTB_V_MRK)
256 #define DTB_END (1u << DTB_V_END)
257 #define DTB_SEL (1u << DTB_V_SEL)
258 #define DTB_PAR (1u << DTB_V_PAR)
259 #define DTB_TIM (1u << DTB_V_TIM)
260 #define DTB_DTF (1u << DTB_V_DTF)
261 #define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \
265 #define DTB_V_DTF 17 /* data flag */
266 #define DTB_V_BEF 16 /* block end flag */
267 #define DTB_V_ERF 15 /* error flag */
268 #define DTB_V_END 14 /* end of tape */
269 #define DTB_V_TIM 13 /* timing err */
270 #define DTB_V_REV 12 /* reverse */
271 #define DTB_V_GO 11 /* go */
272 #define DTB_V_MRK 10 /* mark trk err */
273 #define DTB_V_SEL 9 /* select err */
274 #define DTB_DTF (1u << DTB_V_DTF)
275 #define DTB_BEF (1u << DTB_V_BEF)
276 #define DTB_ERF (1u << DTB_V_ERF)
277 #define DTB_END (1u << DTB_V_END)
278 #define DTB_TIM (1u << DTB_V_TIM)
279 #define DTB_REV (1u << DTB_V_REV)
280 #define DTB_GO (1u << DTB_V_GO)
281 #define DTB_MRK (1u << DTB_V_MRK)
282 #define DTB_SEL (1u << DTB_V_SEL)
283 #define DTB_ALLERR (DTB_END | DTB_TIM | DTB_MRK | DTB_SEL)
288 #define DTS_V_MOT 3 /* motion */
290 #define DTS_STOP 0 /* stopped */
291 #define DTS_DECF 2 /* decel, fwd */
292 #define DTS_DECR 3 /* decel, rev */
293 #define DTS_ACCF 4 /* accel, fwd */
294 #define DTS_ACCR 5 /* accel, rev */
295 #define DTS_ATSF 6 /* @speed, fwd */
296 #define DTS_ATSR 7 /* @speed, rev */
297 #define DTS_DIR 01 /* dir mask */
298 #define DTS_V_FNC 0 /* function */
300 #define DTS_OFR 7 /* "off reel" */
301 #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT)
302 #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC)
303 #define DTS_V_2ND 6 /* next state */
304 #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */
305 #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC))
306 #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z)
307 #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \
308 ((DTS_STA (y, z)) << DTS_V_2ND)
309 #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \
310 ((DTS_STA (y, z)) << DTS_V_3RD)
311 #define DTS_NXTSTA(x) (x >> DTS_V_2ND)
313 /* Operation substates */
315 #define DTO_WCO 1 /* wc overflow */
316 #define DTO_SOB 2 /* start of block */
320 #define LOG_MS 001 /* move, search */
321 #define LOG_RW 002 /* read, write */
322 #define LOG_RA 004 /* read all */
323 #define LOG_BL 010 /* block # lblk */
325 #define ABS(x) (((x) < 0)? (-(x)): (x))
328 extern int32 int_hwre
[API_HLVL
+1];
329 extern UNIT cpu_unit
;
330 extern int32 sim_switches
;
331 extern int32 sim_is_running
;
332 extern FILE *sim_deb
;
334 int32 dtsa
= 0; /* status A */
335 int32 dtsb
= 0; /* status B */
336 int32 dtdb
= 0; /* data buffer */
337 int32 dt_ltime
= 12; /* interline time */
338 int32 dt_dctime
= 40000; /* decel time */
339 int32 dt_substate
= 0;
341 int32 dt_stopoffr
= 0; /* stop on off reel */
342 static const int32 map_unit
[16] = { /* Type 550 unit map */
343 -1, 1, 2, 3, 4, 5, 6, 7,
344 0, -1, -1, -1, -1, -1, -1, -1
348 int32
dt75 (int32 dev
, int32 pulse
, int32 dat
);
349 int32
dt76 (int32 dev
, int32 pulse
, int32 dat
);
350 int32
dt_iors (void);
351 t_stat
dt_svc (UNIT
*uptr
);
352 t_stat
dt_reset (DEVICE
*dptr
);
353 t_stat
dt_attach (UNIT
*uptr
, char *cptr
);
354 t_stat
dt_detach (UNIT
*uptr
);
355 void dt_deselect (int32 oldf
);
356 void dt_newsa (int32 newf
);
357 void dt_newfnc (UNIT
*uptr
, int32 newsta
);
358 t_bool
dt_setpos (UNIT
*uptr
);
359 void dt_schedez (UNIT
*uptr
, int32 dir
);
360 void dt_seterr (UNIT
*uptr
, int32 e
);
361 int32
dt_comobv (int32 val
);
362 int32
dt_csum (UNIT
*uptr
, int32 blk
);
363 int32
dt_gethdr (UNIT
*uptr
, int32 blk
, int32 relpos
);
365 /* DT data structures
367 dt_dev DT device descriptor
369 dt_reg DT register list
370 dt_mod DT modifier list
373 DIB dt_dib
= { DEV_DTA
, 2, &dt_iors
, { &dt75
, &dt76
} };
376 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
377 UNIT_ROABLE
, DT_CAPAC
) },
378 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
379 UNIT_ROABLE
, DT_CAPAC
) },
380 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
381 UNIT_ROABLE
, DT_CAPAC
) },
382 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
383 UNIT_ROABLE
, DT_CAPAC
) },
384 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
385 UNIT_ROABLE
, DT_CAPAC
) },
386 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
387 UNIT_ROABLE
, DT_CAPAC
) },
388 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
389 UNIT_ROABLE
, DT_CAPAC
) },
390 { UDATA (&dt_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+
391 UNIT_ROABLE
, DT_CAPAC
) }
395 { ORDATA (DTSA
, dtsa
, 18) },
396 { ORDATA (DTSB
, dtsb
, 18) },
397 { ORDATA (DTDB
, dtdb
, 18) },
398 { FLDATA (INT
, int_hwre
[API_DTA
], INT_V_DTA
) },
399 #if defined (DTA_V_ENB)
400 { FLDATA (ENB
, dtsa
, DTA_V_ENB
) },
402 { FLDATA (DTF
, dtsb
, DTB_V_DTF
) },
403 #if defined (DTB_V_BEF)
404 { FLDATA (BEF
, dtsb
, DTB_V_BEF
) },
406 { FLDATA (ERF
, dtsb
, DTB_V_ERF
) },
407 #if defined (TC02) /* TC02/TC15 */
408 { ORDATA (WC
, M
[DT_WC
], 18) },
409 { ORDATA (CA
, M
[DT_CA
], 18) },
411 { DRDATA (LTIME
, dt_ltime
, 31), REG_NZ
},
412 { DRDATA (DCTIME
, dt_dctime
, 31), REG_NZ
},
413 { ORDATA (SUBSTATE
, dt_substate
, 2) },
414 { DRDATA (LBLK
, dt_logblk
, 12), REG_HIDDEN
},
415 { URDATA (POS
, dt_unit
[0].pos
, 10, T_ADDR_W
, 0,
416 DT_NUMDR
, PV_LEFT
| REG_RO
) },
417 { URDATA (STATT
, dt_unit
[0].STATE
, 8, 18, 0,
419 { URDATA (LASTT
, dt_unit
[0].LASTT
, 10, T_ADDR_W
, 0,
420 DT_NUMDR
, REG_HRO
) },
421 { ORDATA (DEVNO
, dt_dib
.dev
, 6), REG_HRO
},
422 { FLDATA (STOP_OFFR
, dt_stopoffr
, 0) },
427 { UNIT_WLK
, 0, "write enabled", "WRITEENABLED", NULL
},
428 { UNIT_WLK
, UNIT_WLK
, "write locked", "LOCKED", NULL
},
429 { UNIT_8FMT
+ UNIT_11FMT
, 0, "18b", NULL
, NULL
},
430 { UNIT_8FMT
+ UNIT_11FMT
, UNIT_8FMT
, "12b", NULL
, NULL
},
431 { UNIT_8FMT
+ UNIT_11FMT
, UNIT_11FMT
, "16b", NULL
, NULL
},
432 { MTAB_XTD
|MTAB_VDV
, 0, "DEVNO", "DEVNO", &set_devno
, &show_devno
},
437 { "MOTION", LOG_MS
},
439 { "READALL", LOG_RA
},
445 "DT", dt_unit
, dt_reg
, dt_mod
,
446 DT_NUMDR
, 8, 24, 1, 8, 18,
447 NULL
, NULL
, &dt_reset
,
448 NULL
, &dt_attach
, &dt_detach
,
449 &dt_dib
, DEV_DISABLE
| DEV_DEBUG
, 0,
455 #if defined (TC02) /* TC02/TC15 */
456 int32
dt75 (int32 dev
, int32 pulse
, int32 dat
)
458 int32 old_dtsa
= dtsa
, fnc
;
461 if (((pulse
& 060) == 040) && (pulse
& 05)) { /* select */
462 if (pulse
& 01) dtsa
= 0; /* DTCA */
463 if (pulse
& 02) dat
= dtsa
; /* DTRA!... */
464 if (pulse
& 04) { /* DTXA */
465 if ((dat
& DTA_CERF
) == 0) dtsb
= dtsb
& ~DTB_ALLERR
;
466 if ((dat
& DTA_CDTF
) == 0) dtsb
= dtsb
& ~DTB_DTF
;
467 dtsa
= dtsa
^ (dat
& DTA_RW
);
469 if ((old_dtsa
^ dtsa
) & DTA_UNIT
) dt_deselect (old_dtsa
);
470 uptr
= dt_dev
.units
+ DTA_GETUNIT (dtsa
); /* get unit */
471 fnc
= DTA_GETFNC (dtsa
); /* get fnc */
472 if (((uptr
->flags
) & UNIT_DIS
) || /* disabled? */
473 (fnc
>= FNC_WMRK
) || /* write mark? */
474 ((fnc
== FNC_WRIT
) && (uptr
->flags
& UNIT_WPRT
)) ||
475 ((fnc
== FNC_WALL
) && (uptr
->flags
& UNIT_WPRT
)))
476 dt_seterr (uptr
, DTB_SEL
); /* select err */
477 else dt_newsa (dtsa
); /* new func */
481 if ((pulse
& 067) == 042) return dtsa
; /* DTRA */
482 if ((pulse
& 067) == 061) /* DTEF */
483 return ((dtsb
& DTB_ERF
)? IOT_SKP
+ dat
: dat
);
484 if ((pulse
& 067) == 062) return dtsb
; /* DTRB */
485 if ((pulse
& 067) == 063) /* DTEF!DTRB */
486 return ((dtsb
& DTB_ERF
)? IOT_SKP
+ dtsb
: dtsb
);
490 int32
dt76 (int32 dev
, int32 pulse
, int32 dat
)
492 if ((pulse
& 01) && (dtsb
& DTB_DTF
)) /* DTDF */
493 return IOT_SKP
+ dat
;
498 int32
dt75 (int32 dev
, int32 pulse
, int32 dat
)
500 if (((pulse
& 041) == 001) && (dtsb
& DTB_DTF
)) /* MMDF */
502 else if (((pulse
& 041) == 041) && (dtsb
& DTB_ERF
)) /* MMEF */
504 if (pulse
& 002) { /* MMRD */
505 dat
= (dat
& ~DMASK
) | dtdb
;
506 dtsb
= dtsb
& ~(DTB_DTF
| DTB_BEF
);
508 if (pulse
& 004) { /* MMWR */
510 dtsb
= dtsb
& ~(DTB_DTF
| DTB_BEF
);
516 int32
dt76 (int32 dev
, int32 pulse
, int32 dat
)
518 int32 fnc
, mot
, unum
;
521 unum
= DTA_GETUNIT (dtsa
); /* get unit no */
522 if (unum
>= 0) uptr
= dt_dev
.units
+ unum
; /* get unit */
523 if ((pulse
& 001) && (dtsb
& DTB_BEF
)) /* MMBF */
525 if (pulse
& 002) { /* MMRS */
526 dtsb
= dtsb
& ~(DTB_REV
| DTB_GO
); /* clr rev, go */
527 if (uptr
) { /* valid unit? */
528 mot
= DTS_GETMOT (uptr
->STATE
); /* get motion */
529 if (mot
& DTS_DIR
) dtsb
= dtsb
| DTB_REV
; /* rev? set */
530 if ((mot
>= DTS_ACCF
) || (uptr
->STATE
& 0777700))
531 dtsb
= dtsb
| DTB_GO
; /* accel? go */
533 dat
= (dat
& ~DMASK
) | dtsb
;
535 if ((pulse
& 044) == 044) { /* MMSE */
536 if ((dtsa
^ dat
) & DTA_UNIT
) dt_deselect (dtsa
); /* new unit? */
537 dtsa
= (dtsa
& ~DTA_UNIT
) | (dat
& DTA_UNIT
);
538 dtsb
= dtsb
& ~(DTB_DTF
| DTB_BEF
| DTB_ERF
| DTB_ALLERR
);
540 else if ((pulse
& 044) == 004) { /* MMLC */
541 dtsa
= (dtsa
& ~DTA_RW
) | (dat
& DTA_RW
); /* load dtsa */
542 dtsb
= dtsb
& ~(DTB_DTF
| DTB_BEF
| DTB_ERF
| DTB_ALLERR
);
543 fnc
= DTA_GETFNC (dtsa
); /* get fnc */
544 if ((uptr
== NULL
) || /* invalid? */
545 ((uptr
->flags
) & UNIT_DIS
) || /* disabled? */
546 (fnc
>= FNC_WMRK
) || /* write mark? */
547 ((fnc
== FNC_WRIT
) && (uptr
->flags
& UNIT_WLK
)) ||
548 ((fnc
== FNC_WALL
) && (uptr
->flags
& UNIT_WLK
)))
549 dt_seterr (uptr
, DTB_SEL
); /* select err */
550 else dt_newsa (dtsa
);
559 void dt_deselect (int32 oldf
)
561 int32 old_unit
, old_mot
;
564 old_unit
= DTA_GETUNIT (oldf
); /* get unit no */
565 if (old_unit
< 0) return; /* invalid? */
566 uptr
= dt_dev
.units
+ old_unit
; /* get unit */
567 old_mot
= DTS_GETMOT (uptr
->STATE
);
568 if (old_mot
>= DTS_ATSF
) /* at speed? */
569 dt_newfnc (uptr
, DTS_STA (old_mot
, DTS_OFR
));
570 else if (old_mot
>= DTS_ACCF
) /* accelerating? */
571 DTS_SET2ND (DTS_ATSF
| (old_mot
& DTS_DIR
), DTS_OFR
);
575 /* Command register change
577 1. If change in motion, stop to start
578 - schedule acceleration
579 - set function as next state
580 2. If change in motion, start to stop
581 - if not already decelerating (could be reversing),
582 schedule deceleration
583 3. If change in direction,
584 - if not decelerating, schedule deceleration
585 - set accelerating (other dir) as next state
586 - set function as next next state
587 4. If not accelerating or at speed,
588 - schedule acceleration
589 - set function as next state
590 5. If not yet at speed,
591 - set function as next state
593 - set function as current state, schedule function
596 void dt_newsa (int32 newf
)
598 int32 new_unit
, prev_mot
, new_fnc
;
599 int32 prev_mving
, new_mving
, prev_dir
, new_dir
;
602 new_unit
= DTA_GETUNIT (newf
); /* new unit */
603 if (new_unit
< 0) return; /* invalid? */
604 uptr
= dt_dev
.units
+ new_unit
;
605 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* new unit attached? */
606 dt_seterr (uptr
, DTB_SEL
); /* no, error */
609 prev_mot
= DTS_GETMOT (uptr
->STATE
); /* previous motion */
610 prev_mving
= prev_mot
!= DTS_STOP
; /* previous moving? */
611 prev_dir
= prev_mot
& DTS_DIR
; /* previous dir? */
612 new_mving
= (newf
& DTA_STSTP
) != 0; /* new moving? */
613 new_dir
= (newf
& DTA_FWDRV
) != 0; /* new dir? */
614 new_fnc
= DTA_GETFNC (newf
); /* new function? */
616 if ((prev_mving
| new_mving
) == 0) return; /* stop to stop */
618 if (new_mving
& ~prev_mving
) { /* start? */
619 if (dt_setpos (uptr
)) return; /* update pos */
620 sim_cancel (uptr
); /* stop current */
621 sim_activate (uptr
, dt_dctime
- (dt_dctime
>> 2)); /* sched accel */
622 DTS_SETSTA (DTS_ACCF
| new_dir
, 0); /* state = accel */
623 DTS_SET2ND (DTS_ATSF
| new_dir
, new_fnc
); /* next = fnc */
627 if (prev_mving
& ~new_mving
) { /* stop? */
628 if ((prev_mot
& ~DTS_DIR
) != DTS_DECF
) { /* !already stopping? */
629 if (dt_setpos (uptr
)) return; /* update pos */
630 sim_cancel (uptr
); /* stop current */
631 sim_activate (uptr
, dt_dctime
); /* schedule decel */
633 DTS_SETSTA (DTS_DECF
| prev_dir
, 0); /* state = decel */
637 if (prev_dir
^ new_dir
) { /* dir chg? */
638 if ((prev_mot
& ~DTS_DIR
) != DTS_DECF
) { /* !already stopping? */
639 if (dt_setpos (uptr
)) return; /* update pos */
640 sim_cancel (uptr
); /* stop current */
641 sim_activate (uptr
, dt_dctime
); /* schedule decel */
643 DTS_SETSTA (DTS_DECF
| prev_dir
, 0); /* state = decel */
644 DTS_SET2ND (DTS_ACCF
| new_dir
, 0); /* next = accel */
645 DTS_SET3RD (DTS_ATSF
| new_dir
, new_fnc
); /* next next = fnc */
649 if (prev_mot
< DTS_ACCF
) { /* not accel/at speed? */
650 if (dt_setpos (uptr
)) return; /* update pos */
651 sim_cancel (uptr
); /* cancel cur */
652 sim_activate (uptr
, dt_dctime
- (dt_dctime
>> 2)); /* sched accel */
653 DTS_SETSTA (DTS_ACCF
| new_dir
, 0); /* state = accel */
654 DTS_SET2ND (DTS_ATSF
| new_dir
, new_fnc
); /* next = fnc */
658 if (prev_mot
< DTS_ATSF
) { /* not at speed? */
659 DTS_SET2ND (DTS_ATSF
| new_dir
, new_fnc
); /* next = fnc */
663 dt_newfnc (uptr
, DTS_STA (DTS_ATSF
| new_dir
, new_fnc
));/* state = fnc */
667 /* Schedule new DECtape function
669 This routine is only called if
670 - the selected unit is attached
671 - the selected unit is at speed (forward or backward)
674 - updates the selected unit's position
675 - updates the selected unit's state
676 - schedules the new operation
679 void dt_newfnc (UNIT
*uptr
, int32 newsta
)
681 int32 fnc
, dir
, blk
, unum
, newpos
;
687 oldpos
= uptr
->pos
; /* save old pos */
688 if (dt_setpos (uptr
)) return; /* update pos */
689 uptr
->STATE
= newsta
; /* update state */
690 fnc
= DTS_GETFNC (uptr
->STATE
); /* set variables */
691 dir
= DTS_GETMOT (uptr
->STATE
) & DTS_DIR
;
692 unum
= (int32
) (uptr
- dt_dev
.units
);
693 if (oldpos
== uptr
->pos
) /* bump pos */
694 uptr
->pos
= uptr
->pos
+ (dir
? -1: 1);
695 blk
= DT_LIN2BL (uptr
->pos
, uptr
);
697 if (dir
? DT_QREZ (uptr
): DT_QFEZ (uptr
)) { /* wrong ez? */
698 dt_seterr (uptr
, DTB_END
); /* set ez flag, stop */
701 sim_cancel (uptr
); /* cancel cur op */
702 dt_substate
= DTO_SOB
; /* substate = block start */
703 switch (fnc
) { /* case function */
705 case DTS_OFR
: /* off reel */
706 if (dir
) newpos
= -1000; /* rev? < start */
707 else newpos
= DTU_FWDEZ (uptr
) + DT_EZLIN
+ 1000; /* fwd? > end */
710 case FNC_MOVE
: /* move */
711 dt_schedez (uptr
, dir
); /* sched end zone */
712 if (DEBUG_PRI (dt_dev
, LOG_MS
)) fprintf (sim_deb
, ">>DT%d: moving %s\n",
713 unum
, (dir
? "backward": "forward"));
716 case FNC_SRCH
: /* search */
717 if (dir
) newpos
= DT_BLK2LN ((DT_QFEZ (uptr
)?
718 DTU_TSIZE (uptr
): blk
), uptr
) - DT_BLKLN
- DT_WSIZE
;
719 else newpos
= DT_BLK2LN ((DT_QREZ (uptr
)?
720 0: blk
+ 1), uptr
) + DT_BLKLN
+ (DT_WSIZE
- 1);
721 if (DEBUG_PRI (dt_dev
, LOG_MS
)) fprintf (sim_deb
, ">>DT%d: searching %s\n",
722 unum
, (dir
? "backward": "forward"));
725 case FNC_WRIT
: /* write */
726 case FNC_READ
: /* read */
727 #if defined (TC02) /* TC02/TC15 */
728 if (DT_QEZ (uptr
)) { /* in "ok" end zone? */
729 if (dir
) newpos
= DTU_FWDEZ (uptr
) - DT_HTLIN
- DT_WSIZE
;
730 else newpos
= DT_EZLIN
+ DT_HTLIN
+ (DT_WSIZE
- 1);
733 relpos
= DT_LIN2OF (uptr
->pos
, uptr
); /* cur pos in blk */
734 if ((relpos
>= DT_HTLIN
) && /* in data zone? */
735 (relpos
< (DTU_LPERB (uptr
) - DT_HTLIN
))) {
736 dt_seterr (uptr
, DTB_SEL
);
739 if (dir
) newpos
= DT_BLK2LN (((relpos
>= (DTU_LPERB (uptr
) - DT_HTLIN
))?
740 blk
+ 1: blk
), uptr
) - DT_HTLIN
- DT_WSIZE
;
741 else newpos
= DT_BLK2LN (((relpos
< DT_HTLIN
)?
742 blk
: blk
+ 1), uptr
) + DT_HTLIN
+ (DT_WSIZE
- 1);
746 case FNC_RALL
: /* read all */
747 case FNC_WALL
: /* write all */
748 if (DT_QEZ (uptr
)) { /* in "ok" end zone? */
749 if (dir
) newpos
= DTU_FWDEZ (uptr
) - DT_WSIZE
;
750 else newpos
= DT_EZLIN
+ (DT_WSIZE
- 1);
753 newpos
= ((uptr
->pos
) / DT_WSIZE
) * DT_WSIZE
;
754 if (!dir
) newpos
= newpos
+ (DT_WSIZE
- 1);
756 if (DEBUG_PRI (dt_dev
, LOG_RA
) ||
757 (DEBUG_PRI (dt_dev
, LOG_BL
) && (blk
== dt_logblk
)))
758 fprintf (sim_deb
, ">>DT%d: read all block %d %s%s\n",
759 unum
, blk
, (dir
? "backward": "forward"),
760 ((dtsa
& DTA_MODE
)? " continuous]": " "));
764 dt_seterr (uptr
, DTB_SEL
); /* bad state */
768 #if defined (TYPE550) /* Type 550 */
769 if ((fnc
== FNC_WRIT
) || (fnc
== FNC_WALL
)) { /* write function? */
770 dtsb
= dtsb
| DTB_DTF
; /* set data flag */
775 sim_activate (uptr
, ABS (newpos
- ((int32
) uptr
->pos
)) * dt_ltime
);
779 /* Update DECtape position
781 DECtape motion is modeled as a constant velocity, with linear
782 acceleration and deceleration. The motion equations are as follows:
784 t = time since operation started
785 tmax = time for operation (accel, decel only)
786 v = at speed velocity in lines (= 1/dt_ltime)
789 at speed dist = t * v
790 accel dist = (t^2 * v) / (2 * tmax)
791 decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)
793 This routine uses the relative (integer) time, rather than the absolute
794 (floating point) time, to allow save and restore of the start times.
797 t_bool
dt_setpos (UNIT
*uptr
)
799 uint32 new_time
, ut
, ulin
, udelt
;
800 int32 mot
= DTS_GETMOT (uptr
->STATE
);
803 new_time
= sim_grtime (); /* current time */
804 ut
= new_time
- uptr
->LASTT
; /* elapsed time */
805 if (ut
== 0) return FALSE
; /* no time gone? exit */
806 uptr
->LASTT
= new_time
; /* update last time */
807 switch (mot
& ~DTS_DIR
) { /* case on motion */
809 case DTS_STOP
: /* stop */
813 case DTS_DECF
: /* slowing */
814 ulin
= ut
/ (uint32
) dt_ltime
;
815 udelt
= dt_dctime
/ dt_ltime
;
816 delta
= ((ulin
* udelt
* 2) - (ulin
* ulin
)) / (2 * udelt
);
819 case DTS_ACCF
: /* accelerating */
820 ulin
= ut
/ (uint32
) dt_ltime
;
821 udelt
= (dt_dctime
- (dt_dctime
>> 2)) / dt_ltime
;
822 delta
= (ulin
* ulin
) / (2 * udelt
);
825 case DTS_ATSF
: /* at speed */
826 delta
= ut
/ (uint32
) dt_ltime
;
830 if (mot
& DTS_DIR
) uptr
->pos
= uptr
->pos
- delta
; /* update pos */
831 else uptr
->pos
= uptr
->pos
+ delta
;
832 if (((int32
) uptr
->pos
< 0) ||
833 ((int32
) uptr
->pos
> (DTU_FWDEZ (uptr
) + DT_EZLIN
))) {
834 detach_unit (uptr
); /* off reel? */
835 uptr
->STATE
= uptr
->pos
= 0;
836 unum
= (int32
) (uptr
- dt_dev
.units
);
837 if (unum
== DTA_GETUNIT (dtsa
)) /* if selected, */
838 dt_seterr (uptr
, DTB_SEL
); /* error */
846 Unit must be attached, detach cancels operation
849 t_stat
dt_svc (UNIT
*uptr
)
851 int32 mot
= DTS_GETMOT (uptr
->STATE
);
852 int32 dir
= mot
& DTS_DIR
;
853 int32 fnc
= DTS_GETFNC (uptr
->STATE
);
854 int32
*fbuf
= (int32
*) uptr
->filebuf
;
855 int32 unum
= uptr
- dt_dev
.units
;
856 int32 blk
, wrd
, ma
, relpos
;
861 Decelerating - if next state != stopped, must be accel reverse
862 Accelerating - next state must be @speed, schedule function
863 At speed - do functional processing
868 case DTS_DECF
: case DTS_DECR
: /* decelerating */
869 if (dt_setpos (uptr
)) /* upd pos; off reel? */
870 return IORETURN (dt_stopoffr
, STOP_DTOFF
);
871 uptr
->STATE
= DTS_NXTSTA (uptr
->STATE
); /* advance state */
872 if (uptr
->STATE
) /* not stopped? */
873 sim_activate (uptr
, dt_dctime
- (dt_dctime
>> 2)); /* reversing */
876 case DTS_ACCF
: case DTS_ACCR
: /* accelerating */
877 dt_newfnc (uptr
, DTS_NXTSTA (uptr
->STATE
)); /* adv state, sched */
880 case DTS_ATSF
: case DTS_ATSR
: /* at speed */
881 break; /* check function */
884 dt_seterr (uptr
, DTB_SEL
); /* state error */
890 Move - must be at end zone
891 Search - transfer block number, schedule next block
892 Off reel - detach unit (it must be deselected)
895 if (dt_setpos (uptr
)) /* upd pos; off reel? */
896 return IORETURN (dt_stopoffr
, STOP_DTOFF
);
897 if (DT_QEZ (uptr
)) { /* in end zone? */
898 dt_seterr (uptr
, DTB_END
); /* end zone error */
901 blk
= DT_LIN2BL (uptr
->pos
, uptr
); /* get block # */
903 switch (fnc
) { /* at speed, check fnc */
905 case FNC_MOVE
: /* move */
906 dt_seterr (uptr
, DTB_END
); /* end zone error */
909 case DTS_OFR
: /* off reel */
910 detach_unit (uptr
); /* must be deselected */
911 uptr
->STATE
= uptr
->pos
= 0; /* no visible action */
914 /* TC02/TC15 service */
917 #if defined (TC02) /* TC02/TC15 */
919 case FNC_SRCH
: /* search */
920 if (dtsb
& DTB_DTF
) { /* DTF set? */
921 dt_seterr (uptr
, DTB_TIM
); /* timing error */
924 sim_activate (uptr
, DTU_LPERB (uptr
) * dt_ltime
);/* sched next block */
925 M
[DT_WC
] = (M
[DT_WC
] + 1) & DMASK
; /* inc WC */
926 ma
= M
[DT_CA
] & AMASK
; /* get mem addr */
927 if (MEM_ADDR_OK (ma
)) M
[ma
] = blk
; /* store block # */
928 if (((dtsa
& DTA_MODE
) == 0) || (M
[DT_WC
] == 0))
929 dtsb
= dtsb
| DTB_DTF
; /* set DTF */
930 if (DEBUG_PRI (dt_dev
, LOG_MS
))
931 fprintf (sim_deb
, ">>DT%d: found block %d\n", unum
, blk
);
934 /* Read has four subcases
936 Start of block, not wc ovf - check that DTF is clear, otherwise normal
937 Normal - increment MA, WC, copy word from tape to memory
938 if read dir != write dir, bits must be scrambled
939 if wc overflow, next state is wc overflow
940 if end of block, possibly set DTF, next state is start of block
941 Wc ovf, not start of block -
942 if end of block, possibly set DTF, next state is start of block
943 Wc ovf, start of block - if end of block reached, timing error,
944 otherwise, continue to next word
947 case FNC_READ
: /* read */
948 wrd
= DT_LIN2WD (uptr
->pos
, uptr
); /* get word # */
949 switch (dt_substate
) { /* case on substate */
951 case DTO_SOB
: /* start of block */
952 if (dtsb
& DTB_DTF
) { /* DTF set? */
953 dt_seterr (uptr
, DTB_TIM
); /* timing error */
956 if (DEBUG_PRI (dt_dev
, LOG_RW
) ||
957 (DEBUG_PRI (dt_dev
, LOG_BL
) && (blk
== dt_logblk
)))
958 fprintf (sim_deb
, ">>DT%d: reading block %d %s%s\n",
959 unum
, blk
, (dir
? "backward": "forward"),
960 ((dtsa
& DTA_MODE
)? " continuous": " "));
961 dt_substate
= 0; /* fall through */
962 case 0: /* normal read */
963 M
[DT_WC
] = (M
[DT_WC
] + 1) & DMASK
; /* incr WC, CA */
964 M
[DT_CA
] = (M
[DT_CA
] + 1) & DMASK
;
965 ma
= M
[DT_CA
] & AMASK
; /* mem addr */
966 ba
= (blk
* DTU_BSIZE (uptr
)) + wrd
; /* buffer ptr */
967 dtdb
= fbuf
[ba
]; /* get tape word */
968 if (dir
) dtdb
= dt_comobv (dtdb
); /* rev? comp obv */
969 if (MEM_ADDR_OK (ma
)) M
[ma
] = dtdb
; /* mem addr legal? */
970 if (M
[DT_WC
] == 0) dt_substate
= DTO_WCO
; /* wc ovf? */
971 case DTO_WCO
: /* wc ovf, not sob */
972 if (wrd
!= (dir
? 0: DTU_BSIZE (uptr
) - 1)) /* not last? */
973 sim_activate (uptr
, DT_WSIZE
* dt_ltime
);
975 dt_substate
= dt_substate
| DTO_SOB
;
976 sim_activate (uptr
, ((2 * DT_HTLIN
) + DT_WSIZE
) * dt_ltime
);
977 if (((dtsa
& DTA_MODE
) == 0) || (M
[DT_WC
] == 0))
978 dtsb
= dtsb
| DTB_DTF
; /* set DTF */
982 case DTO_WCO
| DTO_SOB
: /* next block */
983 if (wrd
== (dir
? 0: DTU_BSIZE (uptr
))) /* end of block? */
984 dt_seterr (uptr
, DTB_TIM
); /* timing error */
985 else sim_activate (uptr
, DT_WSIZE
* dt_ltime
);
987 } /* end case subst */
990 /* Write has four subcases
992 Start of block, not wc ovf - check that DTF is clear, set block direction
993 Normal - increment MA, WC, copy word from memory to tape
994 if wc overflow, next state is wc overflow
995 if end of block, possibly set DTF, next state is start of block
996 Wc ovf, not start of block -
998 if end of block, possibly set DTF, next state is start of block
999 Wc ovf, start of block - schedule end zone
1002 case FNC_WRIT
: /* write */
1003 wrd
= DT_LIN2WD (uptr
->pos
, uptr
); /* get word # */
1004 switch (dt_substate
) { /* case on substate */
1006 case DTO_SOB
: /* start block */
1007 if (dtsb
& DTB_DTF
) { /* DTF set? */
1008 dt_seterr (uptr
, DTB_TIM
); /* timing error */
1011 if (DEBUG_PRI (dt_dev
, LOG_RW
) ||
1012 (DEBUG_PRI (dt_dev
, LOG_BL
) && (blk
== dt_logblk
)))
1013 fprintf (sim_deb
, ">>DT%d: writing block %d %s%s\n", unum
, blk
,
1014 (dir
? "backward": "forward"),
1015 ((dtsa
& DTA_MODE
)? " continuous": " "));
1016 dt_substate
= 0; /* fall through */
1017 case 0: /* normal write */
1018 M
[DT_WC
] = (M
[DT_WC
] + 1) & DMASK
; /* incr WC, CA */
1019 M
[DT_CA
] = (M
[DT_CA
] + 1) & DMASK
;
1020 case DTO_WCO
: /* wc ovflo */
1021 ma
= M
[DT_CA
] & AMASK
; /* mem addr */
1022 ba
= (blk
* DTU_BSIZE (uptr
)) + wrd
; /* buffer ptr */
1023 dtdb
= dt_substate
? 0: M
[ma
]; /* get word */
1024 if (dir
) dtdb
= dt_comobv (dtdb
); /* rev? comp obv */
1025 fbuf
[ba
] = dtdb
; /* write word */
1026 if (ba
>= uptr
->hwmark
) uptr
->hwmark
= ba
+ 1;
1027 if (M
[DT_WC
] == 0) dt_substate
= DTO_WCO
;
1028 if (wrd
!= (dir
? 0: DTU_BSIZE (uptr
) - 1)) /* not last? */
1029 sim_activate (uptr
, DT_WSIZE
* dt_ltime
);
1031 dt_substate
= dt_substate
| DTO_SOB
;
1032 sim_activate (uptr
, ((2 * DT_HTLIN
) + DT_WSIZE
) * dt_ltime
);
1033 if (((dtsa
& DTA_MODE
) == 0) || (M
[DT_WC
] == 0))
1034 dtsb
= dtsb
| DTB_DTF
; /* set DTF */
1038 case DTO_WCO
| DTO_SOB
: /* all done */
1039 dt_schedez (uptr
, dir
); /* sched end zone */
1041 } /* end case subst */
1044 /* Read all has two subcases
1046 Not word count overflow - increment MA, WC, copy word from tape to memory
1047 Word count overflow - schedule end zone
1050 case FNC_RALL
: /* read all */
1051 switch (dt_substate
) { /* case on substate */
1053 case 0: case DTO_SOB
: /* read in progress */
1054 if (dtsb
& DTB_DTF
) { /* DTF set? */
1055 dt_seterr (uptr
, DTB_TIM
); /* timing error */
1058 relpos
= DT_LIN2OF (uptr
->pos
, uptr
); /* cur pos in blk */
1059 M
[DT_WC
] = (M
[DT_WC
] + 1) & DMASK
; /* incr WC, CA */
1060 M
[DT_CA
] = (M
[DT_CA
] + 1) & DMASK
;
1061 ma
= M
[DT_CA
] & AMASK
; /* mem addr */
1062 if ((relpos
>= DT_HTLIN
) && /* in data zone? */
1063 (relpos
< (DTU_LPERB (uptr
) - DT_HTLIN
))) {
1064 wrd
= DT_LIN2WD (uptr
->pos
, uptr
);
1065 ba
= (blk
* DTU_BSIZE (uptr
)) + wrd
;
1066 dtdb
= fbuf
[ba
]; /* get tape word */
1068 else dtdb
= dt_gethdr (uptr
, blk
, relpos
); /* get hdr */
1069 if (dir
) dtdb
= dt_comobv (dtdb
); /* rev? comp obv */
1070 sim_activate (uptr
, DT_WSIZE
* dt_ltime
);
1071 if (MEM_ADDR_OK (ma
)) M
[ma
] = dtdb
; /* mem addr legal? */
1072 if (M
[DT_WC
] == 0) dt_substate
= DTO_WCO
;
1073 if (((dtsa
& DTA_MODE
) == 0) || (M
[DT_WC
] == 0))
1074 dtsb
= dtsb
| DTB_DTF
; /* set DTF */
1077 case DTO_WCO
: case DTO_WCO
| DTO_SOB
: /* all done */
1078 dt_schedez (uptr
, dir
); /* sched end zone */
1080 } /* end case substate */
1083 /* Write all has two subcases
1085 Not word count overflow - increment MA, WC, copy word from memory to tape
1086 Word count overflow - schedule end zone
1089 case FNC_WALL
: /* write all */
1090 switch (dt_substate
) { /* case on substate */
1092 case 0: case DTO_SOB
: /* read in progress */
1093 if (dtsb
& DTB_DTF
) { /* DTF set? */
1094 dt_seterr (uptr
, DTB_TIM
); /* timing error */
1097 relpos
= DT_LIN2OF (uptr
->pos
, uptr
); /* cur pos in blk */
1098 M
[DT_WC
] = (M
[DT_WC
] + 1) & DMASK
; /* incr WC, CA */
1099 M
[DT_CA
] = (M
[DT_CA
] + 1) & DMASK
;
1100 ma
= M
[DT_CA
] & AMASK
; /* mem addr */
1101 if ((relpos
>= DT_HTLIN
) && /* in data zone? */
1102 (relpos
< (DTU_LPERB (uptr
) - DT_HTLIN
))) {
1103 dtdb
= M
[ma
]; /* get mem word */
1104 if (dir
) dtdb
= dt_comobv (dtdb
);
1105 wrd
= DT_LIN2WD (uptr
->pos
, uptr
);
1106 ba
= (blk
* DTU_BSIZE (uptr
)) + wrd
;
1107 fbuf
[ba
] = dtdb
; /* write word */
1108 if (ba
>= uptr
->hwmark
) uptr
->hwmark
= ba
+ 1;
1111 sim_activate (uptr
, DT_WSIZE
* dt_ltime
);
1112 if (M
[DT_WC
] == 0) dt_substate
= DTO_WCO
;
1113 if (((dtsa
& DTA_MODE
) == 0) || (M
[DT_WC
] == 0))
1114 dtsb
= dtsb
| DTB_DTF
; /* set DTF */
1117 case DTO_WCO
: case DTO_WCO
| DTO_SOB
: /* all done */
1118 dt_schedez (uptr
, dir
); /* sched end zone */
1120 } /* end case substate */
1123 /* Type 550 service */
1126 #else /* Type 550 */
1127 case FNC_SRCH
: /* search */
1128 if (dtsb
& DTB_DTF
) { /* DTF set? */
1129 dt_seterr (uptr
, DTB_TIM
); /* timing error */
1132 sim_activate (uptr
, DTU_LPERB (uptr
) * dt_ltime
);/* sched next block */
1133 dtdb
= blk
; /* store block # */
1134 dtsb
= dtsb
| DTB_DTF
; /* set DTF */
1135 if (DEBUG_PRI (dt_dev
, LOG_MS
))
1136 fprintf (sim_deb
, ">>DT%d: search found block %d\n", unum
, blk
);
1139 /* Read and read all */
1141 case FNC_READ
: case FNC_RALL
:
1142 if (dtsb
& DTB_DTF
) { /* DTF set? */
1143 dt_seterr (uptr
, DTB_TIM
); /* timing error */
1146 sim_activate (uptr
, DT_WSIZE
* dt_ltime
); /* sched next word */
1147 relpos
= DT_LIN2OF (uptr
->pos
, uptr
); /* cur pos in blk */
1148 if ((relpos
>= DT_HTLIN
) && /* in data zone? */
1149 (relpos
< (DTU_LPERB (uptr
) - DT_HTLIN
))) {
1150 wrd
= DT_LIN2WD (uptr
->pos
, uptr
);
1151 ba
= (blk
* DTU_BSIZE (uptr
)) + wrd
;
1152 dtdb
= fbuf
[ba
]; /* get tape word */
1153 dtsb
= dtsb
| DTB_DTF
; /* set flag */
1156 ma
= (2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - DT_CSMWD
- 1;
1157 wrd
= relpos
/ DT_WSIZE
; /* hdr start = wd 0 */
1158 #if defined (OLD_TYPE550)
1159 if ((wrd
== 0) || /* skip 1st, last */
1160 (wrd
== ((2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - 1))) break;
1162 if ((fnc
== FNC_READ
) && /* read, skip if not */
1163 (wrd
!= DT_CSMWD
) && /* fwd, rev cksum */
1165 dtdb
= dt_gethdr (uptr
, blk
, relpos
);
1166 if (wrd
== (dir
? DT_CSMWD
: ma
)) /* at end csum? */
1167 dtsb
= dtsb
| DTB_BEF
; /* end block */
1168 else dtsb
= dtsb
| DTB_DTF
; /* else next word */
1170 if (dir
) dtdb
= dt_comobv (dtdb
);
1173 /* Write and write all */
1175 case FNC_WRIT
: case FNC_WALL
:
1176 if (dtsb
& DTB_DTF
) { /* DTF set? */
1177 dt_seterr (uptr
, DTB_TIM
); /* timing error */
1180 sim_activate (uptr
, DT_WSIZE
* dt_ltime
); /* sched next word */
1181 relpos
= DT_LIN2OF (uptr
->pos
, uptr
); /* cur pos in blk */
1182 if ((relpos
>= DT_HTLIN
) && /* in data zone? */
1183 (relpos
< (DTU_LPERB (uptr
) - DT_HTLIN
))) {
1184 wrd
= DT_LIN2WD (uptr
->pos
, uptr
);
1185 ba
= (blk
* DTU_BSIZE (uptr
)) + wrd
;
1186 if (dir
) fbuf
[ba
] = dt_comobv (dtdb
); /* get data word */
1187 else fbuf
[ba
] = dtdb
;
1188 if (ba
>= uptr
->hwmark
) uptr
->hwmark
= ba
+ 1;
1189 if (wrd
== (dir
? 0: DTU_BSIZE (uptr
) - 1))
1190 dtsb
= dtsb
| DTB_BEF
; /* end block */
1191 else dtsb
= dtsb
| DTB_DTF
; /* else next word */
1194 wrd
= relpos
/ DT_WSIZE
; /* hdr start = wd 0 */
1195 #if defined (OLD_TYPE550)
1196 if ((wrd
== 0) || /* skip 1st, last */
1197 (wrd
== ((2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - 1))) break;
1199 if ((fnc
== FNC_WRIT
) && /* wr, skip if !csm */
1200 (wrd
!= ((2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - DT_CSMWD
- 1)))
1202 dtsb
= dtsb
| DTB_DTF
; /* set flag */
1208 dt_seterr (uptr
, DTB_SEL
); /* impossible state */
1210 } /* end case function */
1212 DT_UPDINT
; /* update interrupts */
1216 /* Utility routines */
1218 /* Set error flag */
1220 void dt_seterr (UNIT
*uptr
, int32 e
)
1222 int32 mot
= DTS_GETMOT (uptr
->STATE
);
1224 dtsa
= dtsa
& ~DTA_STSTP
; /* clear go */
1225 dtsb
= dtsb
| DTB_ERF
| e
; /* set error flag */
1226 if (mot
>= DTS_ACCF
) { /* ~stopped or stopping? */
1227 sim_cancel (uptr
); /* cancel activity */
1228 if (dt_setpos (uptr
)) return; /* update position */
1229 sim_activate (uptr
, dt_dctime
); /* sched decel */
1230 DTS_SETSTA (DTS_DECF
| (mot
& DTS_DIR
), 0); /* state = decel */
1236 /* Schedule end zone */
1238 void dt_schedez (UNIT
*uptr
, int32 dir
)
1242 if (dir
) newpos
= DT_EZLIN
- DT_WSIZE
; /* rev? rev ez */
1243 else newpos
= DTU_FWDEZ (uptr
) + DT_WSIZE
; /* fwd? fwd ez */
1244 sim_activate (uptr
, ABS (newpos
- ((int32
) uptr
->pos
)) * dt_ltime
);
1248 /* Complement obverse routine */
1250 int32
dt_comobv (int32 dat
)
1252 dat
= dat
^ DMASK
; /* compl obverse */
1253 dat
= ((dat
>> 15) & 07) | ((dat
>> 9) & 070) |
1254 ((dat
>> 3) & 0700) | ((dat
& 0700) << 3) |
1255 ((dat
& 070) << 9) | ((dat
& 07) << 15);
1259 /* Checksum routine */
1261 int32
dt_csum (UNIT
*uptr
, int32 blk
)
1263 int32
*fbuf
= (int32
*) uptr
->filebuf
;
1264 int32 ba
= blk
* DTU_BSIZE (uptr
);
1267 #if defined (TC02) /* TC02/TC15 */
1268 csum
= 077; /* init csum */
1269 for (i
= 0; i
< DTU_BSIZE (uptr
); i
++) { /* loop thru buf */
1270 wrd
= fbuf
[ba
+ i
] ^ DMASK
; /* get ~word */
1271 csum
= csum
^ (wrd
>> 12) ^ (wrd
>> 6) ^ wrd
;
1273 return (csum
& 077);
1274 #else /* Type 550 */
1276 for (i
= 0; i
< DTU_BSIZE (uptr
); i
++) { /* loop thru buf */
1277 wrd
= fbuf
[ba
+ i
]; /* get word */
1278 csum
= csum
+ wrd
; /* 1's comp add */
1279 if (csum
> DMASK
) csum
= (csum
+ 1) & DMASK
;
1281 return (csum
^ DMASK
); /* 1's comp res */
1285 /* Get header word */
1287 int32
dt_gethdr (UNIT
*uptr
, int32 blk
, int32 relpos
)
1289 int32 wrd
= relpos
/ DT_WSIZE
;
1291 if (wrd
== DT_BLKWD
) return blk
; /* fwd blknum */
1292 #if defined (TC02) /* TC02/TC15 */
1293 if (wrd
== DT_CSMWD
) return 077; /* rev csum */
1294 if (wrd
== ((2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - DT_CSMWD
- 1)) /* fwd csum */
1295 return (dt_csum (uptr
, blk
) << 12);
1296 #else /* Type 550 */
1297 if (wrd
== DT_CSMWD
) return 0777777; /* rev csum */
1298 if (wrd
== ((2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - DT_CSMWD
- 1)) /* fwd csum */
1299 return (dt_csum (uptr
, blk
));
1301 if (wrd
== ((2 * DT_HTWRD
) + DTU_BSIZE (uptr
) - DT_BLKWD
- 1)) /* rev blkno */
1302 return dt_comobv (blk
);
1303 return 0; /* all others */
1308 t_stat
dt_reset (DEVICE
*dptr
)
1313 for (i
= 0; i
< DT_NUMDR
; i
++) { /* stop all drives */
1314 uptr
= dt_dev
.units
+ i
;
1315 if (sim_is_running
) { /* CAF? */
1316 prev_mot
= DTS_GETMOT (uptr
->STATE
); /* get motion */
1317 if ((prev_mot
& ~DTS_DIR
) > DTS_DECF
) { /* accel or spd? */
1318 if (dt_setpos (uptr
)) continue; /* update pos */
1320 sim_activate (uptr
, dt_dctime
); /* sched decel */
1321 DTS_SETSTA (DTS_DECF
| (prev_mot
& DTS_DIR
), 0);
1325 sim_cancel (uptr
); /* sim reset */
1327 uptr
->LASTT
= sim_grtime ();
1330 dtsa
= dtsb
= 0; /* clear status */
1331 DT_UPDINT
; /* reset interrupt */
1337 int32
dt_iors (void)
1340 return ((dtsb
& (DTB_ERF
| DTB_DTF
))? IOS_DTA
: 0);
1348 Determine 12b, 16b, or 18b/36b format
1350 If 12b, read 12b format and convert to 18b in buffer
1351 If 16b, read 16b format and convert to 18b in buffer
1352 If 18b/36b, read data into buffer
1355 t_stat
dt_attach (UNIT
*uptr
, char *cptr
)
1357 uint16 pdp8b
[D8_NBSIZE
];
1358 uint16 pdp11b
[D18_BSIZE
];
1359 uint32 ba
, sz
, k
, *fbuf
;
1360 int32 u
= uptr
- dt_dev
.units
;
1363 r
= attach_unit (uptr
, cptr
); /* attach */
1364 if (r
!= SCPE_OK
) return r
; /* error? */
1365 if ((sim_switches
& SIM_SW_REST
) == 0) { /* not from rest? */
1366 uptr
->flags
= uptr
->flags
& ~(UNIT_8FMT
| UNIT_11FMT
); /* default 18b */
1367 if (sim_switches
& SWMASK ('T')) /* att 12b? */
1368 uptr
->flags
= uptr
->flags
| UNIT_8FMT
;
1369 else if (sim_switches
& SWMASK ('S')) /* att 16b? */
1370 uptr
->flags
= uptr
->flags
| UNIT_11FMT
;
1371 else if (!(sim_switches
& SWMASK ('A')) && /* autosize? */
1372 (sz
= sim_fsize (uptr
->fileref
))) {
1373 if (sz
== D8_FILSIZ
)
1374 uptr
->flags
= uptr
->flags
| UNIT_8FMT
;
1375 else if (sz
== D11_FILSIZ
)
1376 uptr
->flags
= uptr
->flags
| UNIT_11FMT
;
1379 uptr
->capac
= DTU_CAPAC (uptr
); /* set capacity */
1380 uptr
->filebuf
= calloc (uptr
->capac
, sizeof (uint32
));
1381 if (uptr
->filebuf
== NULL
) { /* can't alloc? */
1385 fbuf
= (uint32
*) uptr
->filebuf
; /* file buffer */
1386 printf ("%s%d: ", sim_dname (&dt_dev
), u
);
1387 if (uptr
->flags
& UNIT_8FMT
) printf ("12b format");
1388 else if (uptr
->flags
& UNIT_11FMT
) printf ("16b format");
1389 else printf ("18b/36b format");
1390 printf (", buffering file in memory\n");
1391 if (uptr
->flags
& UNIT_8FMT
) { /* 12b? */
1392 for (ba
= 0; ba
< uptr
->capac
; ) { /* loop thru file */
1393 k
= fxread (pdp8b
, sizeof (uint16
), D8_NBSIZE
, uptr
->fileref
);
1395 for ( ; k
< D8_NBSIZE
; k
++) pdp8b
[k
] = 0;
1396 for (k
= 0; k
< D8_NBSIZE
; k
= k
+ 3) { /* loop thru blk */
1397 fbuf
[ba
] = ((uint32
) (pdp8b
[k
] & 07777) << 6) |
1398 ((uint32
) (pdp8b
[k
+ 1] >> 6) & 077);
1399 fbuf
[ba
+ 1] = ((uint32
) (pdp8b
[k
+ 1] & 077) << 12) |
1400 ((uint32
) pdp8b
[k
+ 2] & 07777);
1401 ba
= ba
+ 2; /* end blk loop */
1403 } /* end file loop */
1406 else if (uptr
->flags
& UNIT_11FMT
) { /* 16b? */
1407 for (ba
= 0; ba
< uptr
->capac
; ) { /* loop thru file */
1408 k
= fxread (pdp11b
, sizeof (uint16
), D18_BSIZE
, uptr
->fileref
);
1410 for ( ; k
< D18_BSIZE
; k
++) pdp11b
[k
] = 0;
1411 for (k
= 0; k
< D18_BSIZE
; k
++)
1412 fbuf
[ba
++] = pdp11b
[k
];
1416 else uptr
->hwmark
= fxread (uptr
->filebuf
, sizeof (uint32
),
1417 uptr
->capac
, uptr
->fileref
);
1418 uptr
->flags
= uptr
->flags
| UNIT_BUF
; /* set buf flag */
1419 uptr
->pos
= DT_EZLIN
; /* beyond leader */
1420 uptr
->LASTT
= sim_grtime (); /* last pos update */
1426 Cancel in progress operation
1427 If 12b, convert 18b buffer to 12b and write to file
1428 If 16b, convert 18b buffer to 16b and write to file
1429 If 18b/36b, write buffer to file
1433 t_stat
dt_detach (UNIT
* uptr
)
1435 uint16 pdp8b
[D8_NBSIZE
];
1436 uint16 pdp11b
[D18_BSIZE
];
1437 uint32 ba
, k
, *fbuf
;
1438 int32 u
= uptr
- dt_dev
.units
;
1440 if (!(uptr
->flags
& UNIT_ATT
)) return SCPE_OK
;
1441 if (sim_is_active (uptr
)) {
1443 if ((u
== DTA_GETUNIT (dtsa
)) && (dtsa
& DTA_STSTP
)) {
1444 dtsb
= dtsb
| DTB_ERF
| DTB_SEL
| DTB_DTF
;
1447 uptr
->STATE
= uptr
->pos
= 0;
1449 fbuf
= (uint32
*) uptr
->filebuf
; /* file buffer */
1450 if (uptr
->hwmark
&& ((uptr
->flags
& UNIT_RO
) == 0)) { /* any data? */
1451 printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev
), u
);
1452 rewind (uptr
->fileref
); /* start of file */
1453 if (uptr
->flags
& UNIT_8FMT
) { /* 12b? */
1454 for (ba
= 0; ba
< uptr
->hwmark
; ) { /* loop thru file */
1455 for (k
= 0; k
< D8_NBSIZE
; k
= k
+ 3) { /* loop blk */
1456 pdp8b
[k
] = (fbuf
[ba
] >> 6) & 07777;
1457 pdp8b
[k
+ 1] = ((fbuf
[ba
] & 077) << 6) |
1458 ((fbuf
[ba
+ 1] >> 12) & 077);
1459 pdp8b
[k
+ 2] = fbuf
[ba
+ 1] & 07777;
1461 } /* end loop blk */
1462 fxwrite (pdp8b
, sizeof (uint16
), D8_NBSIZE
, uptr
->fileref
);
1463 if (ferror (uptr
->fileref
)) break;
1464 } /* end loop file */
1466 else if (uptr
->flags
& UNIT_11FMT
) { /* 16b? */
1467 for (ba
= 0; ba
< uptr
->hwmark
; ) { /* loop thru file */
1468 for (k
= 0; k
< D18_BSIZE
; k
++) /* loop blk */
1469 pdp11b
[k
] = fbuf
[ba
++] & 0177777;
1470 fxwrite (pdp11b
, sizeof (uint16
), D18_BSIZE
, uptr
->fileref
);
1471 if (ferror (uptr
->fileref
)) break;
1472 } /* end loop file */
1474 else fxwrite (uptr
->filebuf
, sizeof (uint32
), /* write file */
1475 uptr
->hwmark
, uptr
->fileref
);
1476 if (ferror (uptr
->fileref
)) perror ("I/O error");
1477 } /* end if hwmark */
1478 free (uptr
->filebuf
); /* release buf */
1479 uptr
->flags
= uptr
->flags
& ~UNIT_BUF
; /* clear buf flag */
1480 uptr
->filebuf
= NULL
; /* clear buf ptr */
1481 uptr
->flags
= uptr
->flags
& ~(UNIT_8FMT
| UNIT_11FMT
); /* default fmt */
1482 uptr
->capac
= DT_CAPAC
; /* default size */
1483 return detach_unit (uptr
);