First Commit of my working state
[simh.git] / PDP18B / pdp18b_dt.c
1 /* pdp18b_dt.c: 18b DECtape 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 dt (PDP-4, PDP-7) Type 550/555 DECtape
27 (PDP-9) TC02/TU55 DECtape
28 (PDP-15) TC15/TU56 DECtape
29
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
40 Simplified timing
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
59
60 18b DECtapes are represented in memory by fixed length buffer of 32b words.
61 Three file formats are supported:
62
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]
66
67 When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format.
68
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:
72
73 reverse end zone 7144 reverse end zone codes ~ 12 feet
74 reverse buffer 200 interblock codes
75 block 0
76 :
77 block n
78 forward buffer 200 interblock codes
79 forward end zone 7144 forward end zone codes ~ 12 feet
80
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.
88
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:
93
94 header word 0 0
95 header word 1 block number (for forward reads)
96 header words 2,3 0
97 header word 4 checksum (for reverse reads)
98 :
99 trailer word 4 checksum (for forward reads)
100 trailer words 3,2 0
101 trailer word 1 block number (for reverse reads)
102 trailer word 0 0
103
104 Write all writes only the data words and dumps the interblock words in the
105 bit bucket.
106
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.
111 */
112
113 #include "pdp18b_defs.h"
114
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 */
127
128 /* System independent DECtape constants */
129
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 */
139
140 /* 16b, 18b, 36b DECtape constants */
141
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))
149
150 /* 12b DECtape constants */
151
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 */
158
159 #define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE)
160 #define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16))
161
162 /* This controller */
163
164 #define DT_CAPAC D18_CAPAC /* default */
165 #define DT_WSIZE D18_WSIZE
166
167 /* Calculated constants, per unit */
168
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)
174
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))
182
183 /* Status register A */
184
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 */
190 #define DTA_M_MOT 03
191 #define DTA_V_MODE 12 /* mode */
192 #define DTA_V_FNC 9 /* function */
193 #define DTA_M_FNC 07
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))) \
213 SET_INT (DTA); \
214 else CLR_INT (DTA);
215
216 #else /* Type 550 */
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 */
221 #define DTA_M_MOT 03
222 #define DTA_V_FNC 0 /* function */
223 #define DTA_M_FNC 07
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 */
234 #define DTA_RW 077
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)) \
237 SET_INT (DTA); \
238 else CLR_INT (DTA);
239 #endif
240
241 #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT)
242 #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC)
243
244 /* Status register B */
245
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 | \
262 DTB_PAR | DTB_TIM)
263
264 #else /* Type 550 */
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)
284 #endif
285
286 /* DECtape state */
287
288 #define DTS_V_MOT 3 /* motion */
289 #define DTS_M_MOT 07
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 */
299 #define DTS_M_FNC 07
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)
312
313 /* Operation substates */
314
315 #define DTO_WCO 1 /* wc overflow */
316 #define DTO_SOB 2 /* start of block */
317
318 /* Logging */
319
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 */
324
325 #define ABS(x) (((x) < 0)? (-(x)): (x))
326
327 extern int32 M[];
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;
333
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;
340 int32 dt_logblk = 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
345 };
346
347 DEVICE dt_dev;
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);
364
365 /* DT data structures
366
367 dt_dev DT device descriptor
368 dt_unit DT unit list
369 dt_reg DT register list
370 dt_mod DT modifier list
371 */
372
373 DIB dt_dib = { DEV_DTA, 2, &dt_iors, { &dt75, &dt76 } };
374
375 UNIT dt_unit[] = {
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) }
392 };
393
394 REG dt_reg[] = {
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) },
401 #endif
402 { FLDATA (DTF, dtsb, DTB_V_DTF) },
403 #if defined (DTB_V_BEF)
404 { FLDATA (BEF, dtsb, DTB_V_BEF) },
405 #endif
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) },
410 #endif
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,
418 DT_NUMDR, REG_RO) },
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) },
423 { NULL }
424 };
425
426 MTAB dt_mod[] = {
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 },
433 { 0 }
434 };
435
436 DEBTAB dt_deb[] = {
437 { "MOTION", LOG_MS },
438 { "DATA", LOG_RW },
439 { "READALL", LOG_RA },
440 { "BLOCK", LOG_BL },
441 { NULL, 0 }
442 };
443
444 DEVICE dt_dev = {
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,
450 dt_deb, NULL, NULL
451 };
452
453 /* IOT routines */
454
455 #if defined (TC02) /* TC02/TC15 */
456 int32 dt75 (int32 dev, int32 pulse, int32 dat)
457 {
458 int32 old_dtsa = dtsa, fnc;
459 UNIT *uptr;
460
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);
468 }
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 */
478 DT_UPDINT;
479 return dat;
480 }
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);
487 return dat;
488 }
489
490 int32 dt76 (int32 dev, int32 pulse, int32 dat)
491 {
492 if ((pulse & 01) && (dtsb & DTB_DTF)) /* DTDF */
493 return IOT_SKP + dat;
494 return dat;
495 }
496
497 #else /* Type 550 */
498 int32 dt75 (int32 dev, int32 pulse, int32 dat)
499 {
500 if (((pulse & 041) == 001) && (dtsb & DTB_DTF)) /* MMDF */
501 dat = dat | IOT_SKP;
502 else if (((pulse & 041) == 041) && (dtsb & DTB_ERF)) /* MMEF */
503 dat = dat | IOT_SKP;
504 if (pulse & 002) { /* MMRD */
505 dat = (dat & ~DMASK) | dtdb;
506 dtsb = dtsb & ~(DTB_DTF | DTB_BEF);
507 }
508 if (pulse & 004) { /* MMWR */
509 dtdb = dat & DMASK;
510 dtsb = dtsb & ~(DTB_DTF | DTB_BEF);
511 }
512 DT_UPDINT;
513 return dat;
514 }
515
516 int32 dt76 (int32 dev, int32 pulse, int32 dat)
517 {
518 int32 fnc, mot, unum;
519 UNIT *uptr = NULL;
520
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 */
524 dat = dat | IOT_SKP;
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 */
532 }
533 dat = (dat & ~DMASK) | dtsb;
534 }
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);
539 }
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);
551 }
552 DT_UPDINT;
553 return dat;
554 }
555 #endif
556
557 /* Unit deselect */
558
559 void dt_deselect (int32 oldf)
560 {
561 int32 old_unit, old_mot;
562 UNIT *uptr;
563
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);
572 return;
573 }
574
575 /* Command register change
576
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
592 6. If at speed,
593 - set function as current state, schedule function
594 */
595
596 void dt_newsa (int32 newf)
597 {
598 int32 new_unit, prev_mot, new_fnc;
599 int32 prev_mving, new_mving, prev_dir, new_dir;
600 UNIT *uptr;
601
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 */
607 return;
608 }
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? */
615
616 if ((prev_mving | new_mving) == 0) return; /* stop to stop */
617
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 */
624 return;
625 }
626
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 */
632 }
633 DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */
634 return;
635 }
636
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 */
642 }
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 */
646 return;
647 }
648
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 */
655 return;
656 }
657
658 if (prev_mot < DTS_ATSF) { /* not at speed? */
659 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */
660 return;
661 }
662
663 dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */
664 return;
665 }
666
667 /* Schedule new DECtape function
668
669 This routine is only called if
670 - the selected unit is attached
671 - the selected unit is at speed (forward or backward)
672
673 This routine
674 - updates the selected unit's position
675 - updates the selected unit's state
676 - schedules the new operation
677 */
678
679 void dt_newfnc (UNIT *uptr, int32 newsta)
680 {
681 int32 fnc, dir, blk, unum, newpos;
682 #if defined (TC02)
683 int32 relpos;
684 #endif
685 uint32 oldpos;
686
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);
696
697 if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */
698 dt_seterr (uptr, DTB_END); /* set ez flag, stop */
699 return;
700 }
701 sim_cancel (uptr); /* cancel cur op */
702 dt_substate = DTO_SOB; /* substate = block start */
703 switch (fnc) { /* case function */
704
705 case DTS_OFR: /* off reel */
706 if (dir) newpos = -1000; /* rev? < start */
707 else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */
708 break;
709
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"));
714 return; /* done */
715
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"));
723 break;
724
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);
731 break;
732 }
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);
737 return;
738 }
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);
743 break;
744 #endif
745
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);
751 }
752 else {
753 newpos = ((uptr->pos) / DT_WSIZE) * DT_WSIZE;
754 if (!dir) newpos = newpos + (DT_WSIZE - 1);
755 }
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]": " "));
761 break;
762
763 default:
764 dt_seterr (uptr, DTB_SEL); /* bad state */
765 return;
766 }
767
768 #if defined (TYPE550) /* Type 550 */
769 if ((fnc == FNC_WRIT) || (fnc == FNC_WALL)) { /* write function? */
770 dtsb = dtsb | DTB_DTF; /* set data flag */
771 DT_UPDINT;
772 }
773 #endif
774
775 sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
776 return;
777 }
778
779 /* Update DECtape position
780
781 DECtape motion is modeled as a constant velocity, with linear
782 acceleration and deceleration. The motion equations are as follows:
783
784 t = time since operation started
785 tmax = time for operation (accel, decel only)
786 v = at speed velocity in lines (= 1/dt_ltime)
787
788 Then:
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)
792
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.
795 */
796
797 t_bool dt_setpos (UNIT *uptr)
798 {
799 uint32 new_time, ut, ulin, udelt;
800 int32 mot = DTS_GETMOT (uptr->STATE);
801 int32 unum, delta;
802
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 */
808
809 case DTS_STOP: /* stop */
810 delta = 0;
811 break;
812
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);
817 break;
818
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);
823 break;
824
825 case DTS_ATSF: /* at speed */
826 delta = ut / (uint32) dt_ltime;
827 break;
828 }
829
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 */
839 return TRUE;
840 }
841 return FALSE;
842 }
843
844 /* Unit service
845
846 Unit must be attached, detach cancels operation
847 */
848
849 t_stat dt_svc (UNIT *uptr)
850 {
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;
857 uint32 ba;
858
859 /* Motion cases
860
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
864 */
865
866 switch (mot) {
867
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 */
874 return SCPE_OK;
875
876 case DTS_ACCF: case DTS_ACCR: /* accelerating */
877 dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */
878 return SCPE_OK;
879
880 case DTS_ATSF: case DTS_ATSR: /* at speed */
881 break; /* check function */
882
883 default: /* other */
884 dt_seterr (uptr, DTB_SEL); /* state error */
885 return SCPE_OK;
886 }
887
888 /* Functional cases
889
890 Move - must be at end zone
891 Search - transfer block number, schedule next block
892 Off reel - detach unit (it must be deselected)
893 */
894
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 */
899 return SCPE_OK;
900 }
901 blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */
902
903 switch (fnc) { /* at speed, check fnc */
904
905 case FNC_MOVE: /* move */
906 dt_seterr (uptr, DTB_END); /* end zone error */
907 return SCPE_OK;
908
909 case DTS_OFR: /* off reel */
910 detach_unit (uptr); /* must be deselected */
911 uptr->STATE = uptr->pos = 0; /* no visible action */
912 break;
913
914 /* TC02/TC15 service */
915 /* Search */
916
917 #if defined (TC02) /* TC02/TC15 */
918
919 case FNC_SRCH: /* search */
920 if (dtsb & DTB_DTF) { /* DTF set? */
921 dt_seterr (uptr, DTB_TIM); /* timing error */
922 return SCPE_OK;
923 }
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);
932 break;
933
934 /* Read has four subcases
935
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
945 */
946
947 case FNC_READ: /* read */
948 wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */
949 switch (dt_substate) { /* case on substate */
950
951 case DTO_SOB: /* start of block */
952 if (dtsb & DTB_DTF) { /* DTF set? */
953 dt_seterr (uptr, DTB_TIM); /* timing error */
954 return SCPE_OK;
955 }
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);
974 else {
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 */
979 }
980 break;
981
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);
986 break;
987 } /* end case subst */
988 break;
989
990 /* Write has four subcases
991
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 -
997 copy 0 to tape
998 if end of block, possibly set DTF, next state is start of block
999 Wc ovf, start of block - schedule end zone
1000 */
1001
1002 case FNC_WRIT: /* write */
1003 wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */
1004 switch (dt_substate) { /* case on substate */
1005
1006 case DTO_SOB: /* start block */
1007 if (dtsb & DTB_DTF) { /* DTF set? */
1008 dt_seterr (uptr, DTB_TIM); /* timing error */
1009 return SCPE_OK;
1010 }
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);
1030 else {
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 */
1035 }
1036 break;
1037
1038 case DTO_WCO | DTO_SOB: /* all done */
1039 dt_schedez (uptr, dir); /* sched end zone */
1040 break;
1041 } /* end case subst */
1042 break;
1043
1044 /* Read all has two subcases
1045
1046 Not word count overflow - increment MA, WC, copy word from tape to memory
1047 Word count overflow - schedule end zone
1048 */
1049
1050 case FNC_RALL: /* read all */
1051 switch (dt_substate) { /* case on substate */
1052
1053 case 0: case DTO_SOB: /* read in progress */
1054 if (dtsb & DTB_DTF) { /* DTF set? */
1055 dt_seterr (uptr, DTB_TIM); /* timing error */
1056 return SCPE_OK;
1057 }
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 */
1067 }
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 */
1075 break;
1076
1077 case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */
1078 dt_schedez (uptr, dir); /* sched end zone */
1079 break;
1080 } /* end case substate */
1081 break;
1082
1083 /* Write all has two subcases
1084
1085 Not word count overflow - increment MA, WC, copy word from memory to tape
1086 Word count overflow - schedule end zone
1087 */
1088
1089 case FNC_WALL: /* write all */
1090 switch (dt_substate) { /* case on substate */
1091
1092 case 0: case DTO_SOB: /* read in progress */
1093 if (dtsb & DTB_DTF) { /* DTF set? */
1094 dt_seterr (uptr, DTB_TIM); /* timing error */
1095 return SCPE_OK;
1096 }
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;
1109 }
1110 /* /* ignore hdr */
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 */
1115 break;
1116
1117 case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */
1118 dt_schedez (uptr, dir); /* sched end zone */
1119 break;
1120 } /* end case substate */
1121 break;
1122
1123 /* Type 550 service */
1124 /* Search */
1125
1126 #else /* Type 550 */
1127 case FNC_SRCH: /* search */
1128 if (dtsb & DTB_DTF) { /* DTF set? */
1129 dt_seterr (uptr, DTB_TIM); /* timing error */
1130 return SCPE_OK;
1131 }
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);
1137 break;
1138
1139 /* Read and read all */
1140
1141 case FNC_READ: case FNC_RALL:
1142 if (dtsb & DTB_DTF) { /* DTF set? */
1143 dt_seterr (uptr, DTB_TIM); /* timing error */
1144 return SCPE_OK;
1145 }
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 */
1154 }
1155 else {
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;
1161 #endif
1162 if ((fnc == FNC_READ) && /* read, skip if not */
1163 (wrd != DT_CSMWD) && /* fwd, rev cksum */
1164 (wrd != ma)) break;
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 */
1169 }
1170 if (dir) dtdb = dt_comobv (dtdb);
1171 break;
1172
1173 /* Write and write all */
1174
1175 case FNC_WRIT: case FNC_WALL:
1176 if (dtsb & DTB_DTF) { /* DTF set? */
1177 dt_seterr (uptr, DTB_TIM); /* timing error */
1178 return SCPE_OK;
1179 }
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 */
1192 }
1193 else {
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;
1198 #endif
1199 if ((fnc == FNC_WRIT) && /* wr, skip if !csm */
1200 (wrd != ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)))
1201 break;
1202 dtsb = dtsb | DTB_DTF; /* set flag */
1203 }
1204 break;
1205 #endif
1206
1207 default:
1208 dt_seterr (uptr, DTB_SEL); /* impossible state */
1209 break;
1210 } /* end case function */
1211
1212 DT_UPDINT; /* update interrupts */
1213 return SCPE_OK;
1214 }
1215
1216 /* Utility routines */
1217
1218 /* Set error flag */
1219
1220 void dt_seterr (UNIT *uptr, int32 e)
1221 {
1222 int32 mot = DTS_GETMOT (uptr->STATE);
1223
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 */
1231 }
1232 DT_UPDINT;
1233 return;
1234 }
1235
1236 /* Schedule end zone */
1237
1238 void dt_schedez (UNIT *uptr, int32 dir)
1239 {
1240 int32 newpos;
1241
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);
1245 return;
1246 }
1247
1248 /* Complement obverse routine */
1249
1250 int32 dt_comobv (int32 dat)
1251 {
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);
1256 return dat;
1257 }
1258
1259 /* Checksum routine */
1260
1261 int32 dt_csum (UNIT *uptr, int32 blk)
1262 {
1263 int32 *fbuf = (int32 *) uptr->filebuf;
1264 int32 ba = blk * DTU_BSIZE (uptr);
1265 int32 i, csum, wrd;
1266
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;
1272 }
1273 return (csum & 077);
1274 #else /* Type 550 */
1275 csum = 0777777;
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;
1280 }
1281 return (csum ^ DMASK); /* 1's comp res */
1282 #endif
1283 }
1284
1285 /* Get header word */
1286
1287 int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos)
1288 {
1289 int32 wrd = relpos / DT_WSIZE;
1290
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));
1300 #endif
1301 if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */
1302 return dt_comobv (blk);
1303 return 0; /* all others */
1304 }
1305
1306 /* Reset routine */
1307
1308 t_stat dt_reset (DEVICE *dptr)
1309 {
1310 int32 i, prev_mot;
1311 UNIT *uptr;
1312
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 */
1319 sim_cancel (uptr);
1320 sim_activate (uptr, dt_dctime); /* sched decel */
1321 DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0);
1322 }
1323 }
1324 else {
1325 sim_cancel (uptr); /* sim reset */
1326 uptr->STATE = 0;
1327 uptr->LASTT = sim_grtime ();
1328 }
1329 }
1330 dtsa = dtsb = 0; /* clear status */
1331 DT_UPDINT; /* reset interrupt */
1332 return SCPE_OK;
1333 }
1334
1335 /* IORS routine */
1336
1337 int32 dt_iors (void)
1338 {
1339 #if defined IOS_DTA
1340 return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0);
1341 #else
1342 return 0;
1343 #endif
1344 }
1345
1346 /* Attach routine
1347
1348 Determine 12b, 16b, or 18b/36b format
1349 Allocate buffer
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
1353 */
1354
1355 t_stat dt_attach (UNIT *uptr, char *cptr)
1356 {
1357 uint16 pdp8b[D8_NBSIZE];
1358 uint16 pdp11b[D18_BSIZE];
1359 uint32 ba, sz, k, *fbuf;
1360 int32 u = uptr - dt_dev.units;
1361 t_stat r;
1362
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;
1377 }
1378 }
1379 uptr->capac = DTU_CAPAC (uptr); /* set capacity */
1380 uptr->filebuf = calloc (uptr->capac, sizeof (uint32));
1381 if (uptr->filebuf == NULL) { /* can't alloc? */
1382 detach_unit (uptr);
1383 return SCPE_MEM;
1384 }
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);
1394 if (k == 0) break;
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 */
1402 }
1403 } /* end file loop */
1404 uptr->hwmark = ba;
1405 } /* end if */
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);
1409 if (k == 0) break;
1410 for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0;
1411 for (k = 0; k < D18_BSIZE; k++)
1412 fbuf[ba++] = pdp11b[k];
1413 }
1414 uptr->hwmark = ba;
1415 } /* end elif */
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 */
1421 return SCPE_OK;
1422 }
1423
1424 /* Detach routine
1425
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
1430 Deallocate buffer
1431 */
1432
1433 t_stat dt_detach (UNIT* uptr)
1434 {
1435 uint16 pdp8b[D8_NBSIZE];
1436 uint16 pdp11b[D18_BSIZE];
1437 uint32 ba, k, *fbuf;
1438 int32 u = uptr - dt_dev.units;
1439
1440 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;
1441 if (sim_is_active (uptr)) {
1442 sim_cancel (uptr);
1443 if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) {
1444 dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF;
1445 DT_UPDINT;
1446 }
1447 uptr->STATE = uptr->pos = 0;
1448 }
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;
1460 ba = ba + 2;
1461 } /* end loop blk */
1462 fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref);
1463 if (ferror (uptr->fileref)) break;
1464 } /* end loop file */
1465 } /* end if 12b */
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 */
1473 } /* end if 16b */
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);
1484 }