First Commit of my working state
[simh.git] / PDP8 / pdp8_dt.c
1 /* pdp8_dt.c: PDP-8 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 TC08/TU56 DECtape
27
28 23-Jun-06 RMS Fixed switch conflict in ATTACH
29 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman)
30 16-Aug-05 RMS Fixed C++ declaration and cast problems
31 25-Jan-04 RMS Revised for device debug support
32 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR
33 18-Oct-03 RMS Fixed bugs in read all, tightened timing
34 25-Apr-03 RMS Revised for extended file support
35 14-Mar-03 RMS Fixed sizing interaction with save/restore
36 17-Oct-02 RMS Fixed bug in end of reel logic
37 04-Oct-02 RMS Added DIB, device number support
38 12-Sep-02 RMS Added support for 16b format
39 30-May-02 RMS Widened POS to 32b
40 06-Jan-02 RMS Changed enable/disable support
41 30-Nov-01 RMS Added read only unit, extended SET/SHOW support
42 24-Nov-01 RMS Changed POS, STATT, LASTT, FLG to arrays
43 29-Aug-01 RMS Added casts to PDP-18b packup routine
44 17-Jul-01 RMS Moved function prototype
45 11-May-01 RMS Fixed bug in reset
46 25-Apr-01 RMS Added device enable/disable support
47 18-Apr-01 RMS Changed to rewind tape before boot
48 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor
49 15-Mar-01 RMS Added 129th word to PDP-8 format
50
51 PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words.
52 Three file formats are supported:
53
54 18b/36b 256 words per block [256 x 18b]
55 16b 256 words per block [256 x 16b]
56 12b 129 words per block [129 x 12b]
57
58 When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format.
59
60 DECtape motion is measured in 3b lines. Time between lines is 33.33us.
61 Tape density is nominally 300 lines per inch. The format of a DECtape (as
62 taken from the TD8E formatter) is:
63
64 reverse end zone 8192 reverse end zone codes ~ 10 feet
65 reverse buffer 200 interblock codes
66 block 0
67 :
68 block n
69 forward buffer 200 interblock codes
70 forward end zone 8192 forward end zone codes ~ 10 feet
71
72 A block consists of five 18b header words, a tape-specific number of data
73 words, and five 18b trailer words. All systems except the PDP-8 use a
74 standard block length of 256 words; the PDP-8 uses a standard block length
75 of 86 words (x 18b = 129 words x 12b).
76
77 Because a DECtape file only contains data, the simulator cannot support
78 write timing and mark track and can only do a limited implementation
79 of read all and write all. Read all assumes that the tape has been
80 conventionally written forward:
81
82 header word 0 0
83 header word 1 block number (for forward reads)
84 header words 2,3 0
85 header word 4 checksum (for reverse reads)
86 :
87 trailer word 4 checksum (for forward reads)
88 trailer words 3,2 0
89 trailer word 1 block number (for reverse reads)
90 trailer word 0 0
91
92 Write all writes only the data words and dumps the non-data words in the
93 bit bucket.
94 */
95
96 #include "pdp8_defs.h"
97
98 #define DT_NUMDR 8 /* #drives */
99 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
100 #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */
101 #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */
102 #define UNIT_WLK (1 << UNIT_V_WLK)
103 #define UNIT_8FMT (1 << UNIT_V_8FMT)
104 #define UNIT_11FMT (1 << UNIT_V_11FMT)
105 #define STATE u3 /* unit state */
106 #define LASTT u4 /* last time update */
107 #define DT_WC 07754 /* word count */
108 #define DT_CA 07755 /* current addr */
109 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
110
111 /* System independent DECtape constants */
112
113 #define DT_LPERMC 6 /* lines per mark track */
114 #define DT_BLKWD 1 /* blk no word in h/t */
115 #define DT_CSMWD 4 /* checksum word in h/t */
116 #define DT_HTWRD 5 /* header/trailer words */
117 #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */
118 #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */
119 #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */
120 #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */
121 #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */
122
123 /* 16b, 18b, 36b DECtape constants */
124
125 #define D18_WSIZE 6 /* word size in lines */
126 #define D18_BSIZE 384 /* block size in 12b */
127 #define D18_TSIZE 578 /* tape size */
128 #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
129 #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))
130 #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */
131
132 #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)
133 #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32))
134 #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16))
135
136 /* 12b DECtape constants */
137
138 #define D8_WSIZE 4 /* word size in lines */
139 #define D8_BSIZE 129 /* block size in 12b */
140 #define D8_TSIZE 1474 /* tape size */
141 #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
142 #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))
143 #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */
144 #define D8_FILSIZ (D8_CAPAC * sizeof (int16))
145
146 /* This controller */
147
148 #define DT_CAPAC D8_CAPAC /* default */
149 #define DT_WSIZE D8_WSIZE
150
151 /* Calculated constants, per unit */
152
153 #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
154 #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
155 #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
156 #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
157 #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)
158
159 #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))
160 #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))
161 #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE)
162 #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN)
163 #define DT_QREZ(u) (((u)->pos) < DT_EZLIN)
164 #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u)))
165 #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u))
166
167 /* Status register A */
168
169 #define DTA_V_UNIT 9 /* unit select */
170 #define DTA_M_UNIT 07
171 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT)
172 #define DTA_V_MOT 7 /* motion */
173 #define DTA_M_MOT 03
174 #define DTA_V_MODE 6 /* mode */
175 #define DTA_V_FNC 3 /* function */
176 #define DTA_M_FNC 07
177 #define FNC_MOVE 00 /* move */
178 #define FNC_SRCH 01 /* search */
179 #define FNC_READ 02 /* read */
180 #define FNC_RALL 03 /* read all */
181 #define FNC_WRIT 04 /* write */
182 #define FNC_WALL 05 /* write all */
183 #define FNC_WMRK 06 /* write timing */
184 #define DTA_V_ENB 2 /* int enable */
185 #define DTA_V_CERF 1 /* clr error flag */
186 #define DTA_V_CDTF 0 /* clr DECtape flag */
187 #define DTA_FWDRV (1u << (DTA_V_MOT + 1))
188 #define DTA_STSTP (1u << DTA_V_MOT)
189 #define DTA_MODE (1u << DTA_V_MODE)
190 #define DTA_ENB (1u << DTA_V_ENB)
191 #define DTA_CERF (1u << DTA_V_CERF)
192 #define DTA_CDTF (1u << DTA_V_CDTF)
193 #define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF))
194
195 #define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT)
196 #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT)
197 #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC)
198
199 /* Status register B */
200
201 #define DTB_V_ERF 11 /* error flag */
202 #define DTB_V_MRK 10 /* mark trk err */
203 #define DTB_V_END 9 /* end zone err */
204 #define DTB_V_SEL 8 /* select err */
205 #define DTB_V_PAR 7 /* parity err */
206 #define DTB_V_TIM 6 /* timing err */
207 #define DTB_V_MEX 3 /* memory extension */
208 #define DTB_M_MEX 07
209 #define DTB_MEX (DTB_M_MEX << DTB_V_MEX)
210 #define DTB_V_DTF 0 /* DECtape flag */
211 #define DTB_ERF (1u << DTB_V_ERF)
212 #define DTB_MRK (1u << DTB_V_MRK)
213 #define DTB_END (1u << DTB_V_END)
214 #define DTB_SEL (1u << DTB_V_SEL)
215 #define DTB_PAR (1u << DTB_V_PAR)
216 #define DTB_TIM (1u << DTB_V_TIM)
217 #define DTB_DTF (1u << DTB_V_DTF)
218 #define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \
219 DTB_PAR | DTB_TIM)
220 #define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX))
221
222 /* DECtape state */
223
224 #define DTS_V_MOT 3 /* motion */
225 #define DTS_M_MOT 07
226 #define DTS_STOP 0 /* stopped */
227 #define DTS_DECF 2 /* decel, fwd */
228 #define DTS_DECR 3 /* decel, rev */
229 #define DTS_ACCF 4 /* accel, fwd */
230 #define DTS_ACCR 5 /* accel, rev */
231 #define DTS_ATSF 6 /* @speed, fwd */
232 #define DTS_ATSR 7 /* @speed, rev */
233 #define DTS_DIR 01 /* dir mask */
234 #define DTS_V_FNC 0 /* function */
235 #define DTS_M_FNC 07
236 #define DTS_OFR 7 /* "off reel" */
237 #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT)
238 #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC)
239 #define DTS_V_2ND 6 /* next state */
240 #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */
241 #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC))
242 #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z)
243 #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \
244 ((DTS_STA (y, z)) << DTS_V_2ND)
245 #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \
246 ((DTS_STA (y, z)) << DTS_V_3RD)
247 #define DTS_NXTSTA(x) (x >> DTS_V_2ND)
248
249 /* Operation substates */
250
251 #define DTO_WCO 1 /* wc overflow */
252 #define DTO_SOB 2 /* start of block */
253
254 /* Logging */
255
256 #define LOG_MS 001 /* move, search */
257 #define LOG_RW 002 /* read, write */
258 #define LOG_BL 004 /* block # lblk */
259
260 #define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \
261 int_req = int_req | INT_DTA; \
262 else int_req = int_req & ~INT_DTA;
263 #define ABS(x) (((x) < 0)? (-(x)): (x))
264
265 extern uint16 M[];
266 extern int32 int_req;
267 extern UNIT cpu_unit;
268 extern int32 sim_switches;
269 extern FILE *sim_deb;
270
271 int32 dtsa = 0; /* status A */
272 int32 dtsb = 0; /* status B */
273 int32 dt_ltime = 12; /* interline time */
274 int32 dt_dctime = 40000; /* decel time */
275 int32 dt_substate = 0;
276 int32 dt_logblk = 0;
277 int32 dt_stopoffr = 0;
278
279 DEVICE dt_dev;
280 int32 dt76 (int32 IR, int32 AC);
281 int32 dt77 (int32 IR, int32 AC);
282 t_stat dt_svc (UNIT *uptr);
283 t_stat dt_reset (DEVICE *dptr);
284 t_stat dt_attach (UNIT *uptr, char *cptr);
285 t_stat dt_detach (UNIT *uptr);
286 t_stat dt_boot (int32 unitno, DEVICE *dptr);
287 void dt_deselect (int32 oldf);
288 void dt_newsa (int32 newf);
289 void dt_newfnc (UNIT *uptr, int32 newsta);
290 t_bool dt_setpos (UNIT *uptr);
291 void dt_schedez (UNIT *uptr, int32 dir);
292 void dt_seterr (UNIT *uptr, int32 e);
293 int32 dt_comobv (int32 val);
294 int32 dt_csum (UNIT *uptr, int32 blk);
295 int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir);
296 extern int32 sim_is_running;
297
298 /* DT data structures
299
300 dt_dev DT device descriptor
301 dt_unit DT unit list
302 dt_reg DT register list
303 dt_mod DT modifier list
304 */
305
306 DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } };
307
308 UNIT dt_unit[] = {
309 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
310 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
311 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
312 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
313 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
314 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
315 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
316 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
317 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
318 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
319 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
320 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
321 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
322 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
323 { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
324 UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }
325 };
326
327 REG dt_reg[] = {
328 { ORDATA (DTSA, dtsa, 12) },
329 { ORDATA (DTSB, dtsb, 12) },
330 { FLDATA (INT, int_req, INT_V_DTA) },
331 { FLDATA (ENB, dtsa, DTA_V_ENB) },
332 { FLDATA (DTF, dtsb, DTB_V_DTF) },
333 { FLDATA (ERF, dtsb, DTB_V_ERF) },
334 { ORDATA (WC, M[DT_WC], 12), REG_FIT },
335 { ORDATA (CA, M[DT_CA], 12), REG_FIT },
336 { DRDATA (LTIME, dt_ltime, 24), REG_NZ | PV_LEFT },
337 { DRDATA (DCTIME, dt_dctime, 24), REG_NZ | PV_LEFT },
338 { ORDATA (SUBSTATE, dt_substate, 2) },
339 { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN },
340 { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0,
341 DT_NUMDR, PV_LEFT | REG_RO) },
342 { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0,
343 DT_NUMDR, REG_RO) },
344 { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0,
345 DT_NUMDR, REG_HRO) },
346 { FLDATA (STOP_OFFR, dt_stopoffr, 0) },
347 { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO },
348 { NULL }
349 };
350
351 MTAB dt_mod[] = {
352 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
353 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
354 { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },
355 { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },
356 { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },
357 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
358 &set_dev, &show_dev, NULL },
359 { 0 }
360 };
361
362 DEBTAB dt_deb[] = {
363 { "MOTION", LOG_MS },
364 { "DATA", LOG_RW },
365 { "BLOCK", LOG_BL },
366 { NULL, 0 }
367 };
368
369 DEVICE dt_dev = {
370 "DT", dt_unit, dt_reg, dt_mod,
371 DT_NUMDR, 8, 24, 1, 8, 12,
372 NULL, NULL, &dt_reset,
373 &dt_boot, &dt_attach, &dt_detach,
374 &dt_dib, DEV_DISABLE | DEV_DEBUG, 0,
375 dt_deb, NULL, NULL
376 };
377
378 /* IOT routines */
379
380 int32 dt76 (int32 IR, int32 AC)
381 {
382 int32 pulse = IR & 07;
383 int32 old_dtsa = dtsa, fnc;
384 UNIT *uptr;
385
386 if (pulse & 01) AC = AC | dtsa; /* DTRA */
387 if (pulse & 06) { /* select */
388 if (pulse & 02) dtsa = 0; /* DTCA */
389 if (pulse & 04) { /* DTXA */
390 if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR;
391 if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF;
392 dtsa = dtsa ^ (AC & DTA_RW);
393 AC = 0; /* clr AC */
394 }
395 if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa);
396 uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */
397 fnc = DTA_GETFNC (dtsa); /* get fnc */
398 if (((uptr->flags) & UNIT_DIS) || /* disabled? */
399 (fnc >= FNC_WMRK) || /* write mark? */
400 ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) ||
401 ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)))
402 dt_seterr (uptr, DTB_SEL); /* select err */
403 else dt_newsa (dtsa);
404 DT_UPDINT;
405 }
406 return AC;
407 }
408
409 int32 dt77 (int32 IR, int32 AC)
410 {
411 int32 pulse = IR & 07;
412
413 if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */
414 AC = IOT_SKP | AC;
415 if (pulse & 02) AC = AC | dtsb; /* DTRB */
416 if (pulse & 04) { /* DTLB */
417 dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX);
418 AC = AC & ~07777; /* clear AC */
419 }
420 return AC;
421 }
422
423 /* Unit deselect */
424
425 void dt_deselect (int32 oldf)
426 {
427 int32 old_unit = DTA_GETUNIT (oldf);
428 UNIT *uptr = dt_dev.units + old_unit;
429 int32 old_mot = DTS_GETMOT (uptr->STATE);
430
431 if (old_mot >= DTS_ATSF) /* at speed? */
432 dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR));
433 else if (old_mot >= DTS_ACCF) /* accelerating? */
434 DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR);
435 return;
436 }
437
438 /* Command register change
439
440 1. If change in motion, stop to start
441 - schedule acceleration
442 - set function as next state
443 2. If change in motion, start to stop
444 - if not already decelerating (could be reversing),
445 schedule deceleration
446 3. If change in direction,
447 - if not decelerating, schedule deceleration
448 - set accelerating (other dir) as next state
449 - set function as next next state
450 4. If not accelerating or at speed,
451 - schedule acceleration
452 - set function as next state
453 5. If not yet at speed,
454 - set function as next state
455 6. If at speed,
456 - set function as current state, schedule function
457 */
458
459 void dt_newsa (int32 newf)
460 {
461 int32 new_unit, prev_mot, new_fnc;
462 int32 prev_mving, new_mving, prev_dir, new_dir;
463 UNIT *uptr;
464
465 new_unit = DTA_GETUNIT (newf); /* new, old units */
466 uptr = dt_dev.units + new_unit;
467 if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */
468 dt_seterr (uptr, DTB_SEL); /* no, error */
469 return;
470 }
471 prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */
472 prev_mving = prev_mot != DTS_STOP; /* previous moving? */
473 prev_dir = prev_mot & DTS_DIR; /* previous dir? */
474 new_mving = (newf & DTA_STSTP) != 0; /* new moving? */
475 new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */
476 new_fnc = DTA_GETFNC (newf); /* new function? */
477
478 if ((prev_mving | new_mving) == 0) return; /* stop to stop */
479
480 if (new_mving & ~prev_mving) { /* start? */
481 if (dt_setpos (uptr)) return; /* update pos */
482 sim_cancel (uptr); /* stop current */
483 sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */
484 DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */
485 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */
486 return;
487 }
488
489 if (prev_mving & ~new_mving) { /* stop? */
490 if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */
491 if (dt_setpos (uptr)) return; /* update pos */
492 sim_cancel (uptr); /* stop current */
493 sim_activate (uptr, dt_dctime); /* schedule decel */
494 }
495 DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */
496 return;
497 }
498
499 if (prev_dir ^ new_dir) { /* dir chg? */
500 if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */
501 if (dt_setpos (uptr)) return; /* update pos */
502 sim_cancel (uptr); /* stop current */
503 sim_activate (uptr, dt_dctime); /* schedule decel */
504 }
505 DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */
506 DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */
507 DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */
508 return;
509 }
510
511 if (prev_mot < DTS_ACCF) { /* not accel/at speed? */
512 if (dt_setpos (uptr)) return; /* update pos */
513 sim_cancel (uptr); /* cancel cur */
514 sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */
515 DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */
516 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */
517 return;
518 }
519
520 if (prev_mot < DTS_ATSF) { /* not at speed? */
521 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */
522 return;
523 }
524
525 dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */
526 return;
527 }
528
529 /* Schedule new DECtape function
530
531 This routine is only called if
532 - the selected unit is attached
533 - the selected unit is at speed (forward or backward)
534
535 This routine
536 - updates the selected unit's position
537 - updates the selected unit's state
538 - schedules the new operation
539 */
540
541 void dt_newfnc (UNIT *uptr, int32 newsta)
542 {
543 int32 fnc, dir, blk, unum, relpos, newpos;
544 uint32 oldpos;
545
546 oldpos = uptr->pos; /* save old pos */
547 if (dt_setpos (uptr)) return; /* update pos */
548 uptr->STATE = newsta; /* update state */
549 fnc = DTS_GETFNC (uptr->STATE); /* set variables */
550 dir = DTS_GETMOT (uptr->STATE) & DTS_DIR;
551 unum = (int32) (uptr - dt_dev.units);
552 if (oldpos == uptr->pos) /* bump pos */
553 uptr->pos = uptr->pos + (dir? -1: 1);
554 blk = DT_LIN2BL (uptr->pos, uptr);
555
556 if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */
557 dt_seterr (uptr, DTB_END); /* set ez flag, stop */
558 return;
559 }
560 sim_cancel (uptr); /* cancel cur op */
561 dt_substate = DTO_SOB; /* substate = block start */
562 switch (fnc) { /* case function */
563
564 case DTS_OFR: /* off reel */
565 if (dir) newpos = -1000; /* rev? < start */
566 else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */
567 break;
568
569 case FNC_MOVE: /* move */
570 dt_schedez (uptr, dir); /* sched end zone */
571 if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n",
572 unum, (dir? "backward": "forward"));
573 return; /* done */
574
575 case FNC_SRCH: /* search */
576 if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)?
577 DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE;
578 else newpos = DT_BLK2LN ((DT_QREZ (uptr)?
579 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1);
580 if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s]\n",
581 unum, (dir? "backward": "forward"));
582 break;
583
584 case FNC_WRIT: /* write */
585 case FNC_READ: /* read */
586 case FNC_RALL: /* read all */
587 case FNC_WALL: /* write all */
588 if (DT_QEZ (uptr)) { /* in "ok" end zone? */
589 if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE;
590 else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1);
591 break;
592 }
593 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */
594 if ((relpos >= DT_HTLIN) && /* in data zone? */
595 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
596 dt_seterr (uptr, DTB_SEL);
597 return;
598 }
599 if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))?
600 blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE;
601 else newpos = DT_BLK2LN (((relpos < DT_HTLIN)?
602 blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1);
603 break;
604
605 default:
606 dt_seterr (uptr, DTB_SEL); /* bad state */
607 return;
608 }
609
610 sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
611 return;
612 }
613
614 /* Update DECtape position
615
616 DECtape motion is modeled as a constant velocity, with linear
617 acceleration and deceleration. The motion equations are as follows:
618
619 t = time since operation started
620 tmax = time for operation (accel, decel only)
621 v = at speed velocity in lines (= 1/dt_ltime)
622
623 Then:
624 at speed dist = t * v
625 accel dist = (t^2 * v) / (2 * tmax)
626 decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)
627
628 This routine uses the relative (integer) time, rather than the absolute
629 (floating point) time, to allow save and restore of the start times.
630 */
631
632 t_bool dt_setpos (UNIT *uptr)
633 {
634 uint32 new_time, ut, ulin, udelt;
635 int32 mot = DTS_GETMOT (uptr->STATE);
636 int32 unum, delta;
637
638 new_time = sim_grtime (); /* current time */
639 ut = new_time - uptr->LASTT; /* elapsed time */
640 if (ut == 0) return FALSE; /* no time gone? exit */
641 uptr->LASTT = new_time; /* update last time */
642 switch (mot & ~DTS_DIR) { /* case on motion */
643
644 case DTS_STOP: /* stop */
645 delta = 0;
646 break;
647
648 case DTS_DECF: /* slowing */
649 ulin = ut / (uint32) dt_ltime;
650 udelt = dt_dctime / dt_ltime;
651 delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
652 break;
653
654 case DTS_ACCF: /* accelerating */
655 ulin = ut / (uint32) dt_ltime;
656 udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime;
657 delta = (ulin * ulin) / (2 * udelt);
658 break;
659
660 case DTS_ATSF: /* at speed */
661 delta = ut / (uint32) dt_ltime;
662 break;
663 }
664
665 if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */
666 else uptr->pos = uptr->pos + delta;
667 if (((int32) uptr->pos < 0) ||
668 ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {
669 detach_unit (uptr); /* off reel? */
670 uptr->STATE = uptr->pos = 0;
671 unum = (int32) (uptr - dt_dev.units);
672 if (unum == DTA_GETUNIT (dtsa)) /* if selected, */
673 dt_seterr (uptr, DTB_SEL); /* error */
674 return TRUE;
675 }
676 return FALSE;
677 }
678
679 /* Unit service
680
681 Unit must be attached, detach cancels operation
682 */
683
684 t_stat dt_svc (UNIT *uptr)
685 {
686 int32 mot = DTS_GETMOT (uptr->STATE);
687 int32 dir = mot & DTS_DIR;
688 int32 fnc = DTS_GETFNC (uptr->STATE);
689 int16 *fbuf = (int16 *) uptr->filebuf;
690 int32 unum = uptr - dt_dev.units;
691 int32 blk, wrd, ma, relpos, dat;
692 uint32 ba;
693
694 /* Motion cases
695
696 Decelerating - if next state != stopped, must be accel reverse
697 Accelerating - next state must be @speed, schedule function
698 At speed - do functional processing
699 */
700
701 switch (mot) {
702
703 case DTS_DECF: case DTS_DECR: /* decelerating */
704 if (dt_setpos (uptr)) /* upd pos; off reel? */
705 return IORETURN (dt_stopoffr, STOP_DTOFF);
706 uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */
707 if (uptr->STATE) /* not stopped? */
708 sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */
709 return SCPE_OK;
710
711 case DTS_ACCF: case DTS_ACCR: /* accelerating */
712 dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */
713 return SCPE_OK;
714
715 case DTS_ATSF: case DTS_ATSR: /* at speed */
716 break; /* check function */
717
718 default: /* other */
719 dt_seterr (uptr, DTB_SEL); /* state error */
720 return SCPE_OK;
721 }
722
723 /* Functional cases
724
725 Move - must be at end zone
726 Search - transfer block number, schedule next block
727 Off reel - detach unit (it must be deselected)
728 */
729
730 if (dt_setpos (uptr)) /* upd pos; off reel? */
731 return IORETURN (dt_stopoffr, STOP_DTOFF);
732 if (DT_QEZ (uptr)) { /* in end zone? */
733 dt_seterr (uptr, DTB_END); /* end zone error */
734 return SCPE_OK;
735 }
736 blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */
737 switch (fnc) { /* at speed, check fnc */
738
739 case FNC_MOVE: /* move */
740 dt_seterr (uptr, DTB_END); /* end zone error */
741 return SCPE_OK;
742
743 case FNC_SRCH: /* search */
744 if (dtsb & DTB_DTF) { /* DTF set? */
745 dt_seterr (uptr, DTB_TIM); /* timing error */
746 return SCPE_OK;
747 }
748 sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */
749 M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */
750 ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */
751 if (MEM_ADDR_OK (ma)) M[ma] = blk & 07777; /* store block # */
752 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
753 dtsb = dtsb | DTB_DTF; /* set DTF */
754 break;
755
756 case DTS_OFR: /* off reel */
757 detach_unit (uptr); /* must be deselected */
758 uptr->STATE = uptr->pos = 0; /* no visible action */
759 break;
760
761 /* Read has four subcases
762
763 Start of block, not wc ovf - check that DTF is clear, otherwise normal
764 Normal - increment MA, WC, copy word from tape to memory
765 if read dir != write dir, bits must be scrambled
766 if wc overflow, next state is wc overflow
767 if end of block, possibly set DTF, next state is start of block
768 Wc ovf, not start of block -
769 if end of block, possibly set DTF, next state is start of block
770 Wc ovf, start of block - if end of block reached, timing error,
771 otherwise, continue to next word
772 */
773
774 case FNC_READ: /* read */
775 wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */
776 switch (dt_substate) { /* case on substate */
777
778 case DTO_SOB: /* start of block */
779 if (dtsb & DTB_DTF) { /* DTF set? */
780 dt_seterr (uptr, DTB_TIM); /* timing error */
781 return SCPE_OK;
782 }
783 if (DEBUG_PRI (dt_dev, LOG_RW) ||
784 (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))
785 fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n",
786 unum, blk, (dir? "backward": "forward"),
787 ((dtsa & DTA_MODE)? " continuous": " "));
788 dt_substate = 0; /* fall through */
789 case 0: /* normal read */
790 M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */
791 M[DT_CA] = (M[DT_CA] + 1) & 07777;
792 ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */
793 ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */
794 dat = fbuf[ba]; /* get tape word */
795 if (dir) dat = dt_comobv (dat); /* rev? comp obv */
796 if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */
797 if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */
798 case DTO_WCO: /* wc ovf, not sob */
799 if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */
800 sim_activate (uptr, DT_WSIZE * dt_ltime);
801 else {
802 dt_substate = dt_substate | DTO_SOB;
803 sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);
804 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
805 dtsb = dtsb | DTB_DTF; /* set DTF */
806 }
807 break;
808
809 case DTO_WCO | DTO_SOB: /* next block */
810 if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */
811 dt_seterr (uptr, DTB_TIM); /* timing error */
812 else sim_activate (uptr, DT_WSIZE * dt_ltime);
813 break;
814 }
815
816 break;
817
818 /* Write has four subcases
819
820 Start of block, not wc ovf - check that DTF is clear, set block direction
821 Normal - increment MA, WC, copy word from memory to tape
822 if wc overflow, next state is wc overflow
823 if end of block, possibly set DTF, next state is start of block
824 Wc ovf, not start of block -
825 copy 0 to tape
826 if end of block, possibly set DTF, next state is start of block
827 Wc ovf, start of block - schedule end zone
828 */
829
830 case FNC_WRIT: /* write */
831 wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */
832 switch (dt_substate) { /* case on substate */
833
834 case DTO_SOB: /* start block */
835 if (dtsb & DTB_DTF) { /* DTF set? */
836 dt_seterr (uptr, DTB_TIM); /* timing error */
837 return SCPE_OK;
838 }
839 if (DEBUG_PRI (dt_dev, LOG_RW) ||
840 (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))
841 fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk,
842 (dir? "backward": "forward"),
843 ((dtsa & DTA_MODE)? " continuous": " "));
844 dt_substate = 0; /* fall through */
845 case 0: /* normal write */
846 M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */
847 M[DT_CA] = (M[DT_CA] + 1) & 07777;
848 case DTO_WCO: /* wc ovflo */
849 ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */
850 ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */
851 dat = dt_substate? 0: M[ma]; /* get word */
852 if (dir) dat = dt_comobv (dat); /* rev? comp obv */
853 fbuf[ba] = dat; /* write word */
854 if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;
855 if (M[DT_WC] == 0) dt_substate = DTO_WCO;
856 if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */
857 sim_activate (uptr, DT_WSIZE * dt_ltime);
858 else {
859 dt_substate = dt_substate | DTO_SOB;
860 sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);
861 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
862 dtsb = dtsb | DTB_DTF; /* set DTF */
863 }
864 break;
865
866 case DTO_WCO | DTO_SOB: /* all done */
867 dt_schedez (uptr, dir); /* sched end zone */
868 break;
869 }
870
871 break;
872
873 /* Read all has two subcases
874
875 Not word count overflow - increment MA, WC, copy word from tape to memory
876 Word count overflow - schedule end zone
877 */
878
879 case FNC_RALL:
880 switch (dt_substate) { /* case on substate */
881
882 case 0: case DTO_SOB: /* read in progress */
883 if (dtsb & DTB_DTF) { /* DTF set? */
884 dt_seterr (uptr, DTB_TIM); /* timing error */
885 return SCPE_OK;
886 }
887 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */
888 M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */
889 M[DT_CA] = (M[DT_CA] + 1) & 07777;
890 ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */
891 if ((relpos >= DT_HTLIN) && /* in data zone? */
892 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
893 wrd = DT_LIN2WD (uptr->pos, uptr);
894 ba = (blk * DTU_BSIZE (uptr)) + wrd;
895 dat = fbuf[ba]; /* get tape word */
896 if (dir) dat = dt_comobv (dat); /* rev? comp obv */
897 }
898 else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */
899 sim_activate (uptr, DT_WSIZE * dt_ltime);
900 if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */
901 if (M[DT_WC] == 0) dt_substate = DTO_WCO;
902 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
903 dtsb = dtsb | DTB_DTF; /* set DTF */
904 break;
905
906 case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */
907 dt_schedez (uptr, dir); /* sched end zone */
908 break;
909 } /* end case substate */
910
911 break;
912
913 /* Write all has two subcases
914
915 Not word count overflow - increment MA, WC, copy word from memory to tape
916 Word count overflow - schedule end zone
917 */
918
919 case FNC_WALL:
920 switch (dt_substate) { /* case on substate */
921
922 case 0: case DTO_SOB: /* read in progress */
923 if (dtsb & DTB_DTF) { /* DTF set? */
924 dt_seterr (uptr, DTB_TIM); /* timing error */
925 return SCPE_OK;
926 }
927 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */
928 M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */
929 M[DT_CA] = (M[DT_CA] + 1) & 07777;
930 ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */
931 if ((relpos >= DT_HTLIN) && /* in data zone? */
932 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
933 dat = M[ma]; /* get mem word */
934 if (dir) dat = dt_comobv (dat);
935 wrd = DT_LIN2WD (uptr->pos, uptr);
936 ba = (blk * DTU_BSIZE (uptr)) + wrd;
937 fbuf[ba] = dat; /* write word */
938 if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;
939 }
940 /* /* ignore hdr */
941 sim_activate (uptr, DT_WSIZE * dt_ltime);
942 if (M[DT_WC] == 0) dt_substate = DTO_WCO;
943 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))
944 dtsb = dtsb | DTB_DTF; /* set DTF */
945 break;
946
947 case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */
948 dt_schedez (uptr, dir); /* sched end zone */
949 break;
950 } /* end case substate */
951 break;
952
953 default:
954 dt_seterr (uptr, DTB_SEL); /* impossible state */
955 break;
956 }
957
958 DT_UPDINT; /* update interrupts */
959 return SCPE_OK;
960 }
961
962 /* Reading the header is complicated, because 18b words are being parsed
963 out 12b at a time. The sequence of word numbers is directionally
964 sensitive
965
966 Forward Reverse
967 Word Word Content Word Word Content
968 (abs) (rel) (abs) (rel)
969
970 137 8 fwd csm'00 6 6 rev csm'00
971 138 9 0000 5 5 0000
972 139 10 0000 4 4 0000
973 140 11 0000 3 3 0000
974 141 12 00'lo rev blk 2 2 00'lo fwd blk
975 142 13 hi rev blk 1 1 hi fwd blk
976 143 14 0000 0 0 0000
977 0 0 0000 143 14 0000
978 1 1 0000 142 13 0000
979 2 2 hi fwd blk 141 12 hi rev blk
980 3 3 lo fwd blk'00 140 11 lo rev blk'00
981 4 4 0000 139 10 0000
982 5 5 0000 138 9 0000
983 6 6 0000 137 8 0000
984 7 7 rev csm 136 7 00'fwd csm
985 */
986
987 int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir)
988 {
989 if (relpos >= DT_HTLIN) relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr));
990 if (dir) { /* reverse */
991 switch (relpos / DT_WSIZE) {
992 case 6: /* rev csm */
993 return 077;
994 case 2: /* lo fwd blk */
995 return dt_comobv ((blk & 077) << 6);
996 case 1: /* hi fwd blk */
997 return dt_comobv (blk >> 6);
998 case 12: /* hi rev blk */
999 return (blk >> 6) & 07777;
1000 case 11: /* lo rev blk */
1001 return ((blk & 077) << 6);
1002 case 7: /* fwd csum */
1003 return (dt_comobv (dt_csum (uptr, blk)) << 6);
1004 default: /* others */
1005 return 07777;
1006 }
1007 }
1008 else { /* forward */
1009 switch (relpos / DT_WSIZE) {
1010 case 8: /* fwd csum */
1011 return (dt_csum (uptr, blk) << 6);
1012 case 12: /* lo rev blk */
1013 return dt_comobv ((blk & 077) << 6);
1014 case 13: /* hi rev blk */
1015 return dt_comobv (blk >> 6);
1016 case 2: /* hi fwd blk */
1017 return ((blk >> 6) & 07777);
1018 case 3: /* lo fwd blk */
1019 return ((blk & 077) << 6);
1020 case 7: /* rev csum */
1021 return 077;
1022 default: /* others */
1023 break;
1024 }
1025 }
1026 return 0;
1027 }
1028
1029 /* Utility routines */
1030
1031 /* Set error flag */
1032
1033 void dt_seterr (UNIT *uptr, int32 e)
1034 {
1035 int32 mot = DTS_GETMOT (uptr->STATE);
1036
1037 dtsa = dtsa & ~DTA_STSTP; /* clear go */
1038 dtsb = dtsb | DTB_ERF | e; /* set error flag */
1039 if (mot >= DTS_ACCF) { /* ~stopped or stopping? */
1040 sim_cancel (uptr); /* cancel activity */
1041 if (dt_setpos (uptr)) return; /* update position */
1042 sim_activate (uptr, dt_dctime); /* sched decel */
1043 DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */
1044 }
1045 DT_UPDINT;
1046 return;
1047 }
1048
1049 /* Schedule end zone */
1050
1051 void dt_schedez (UNIT *uptr, int32 dir)
1052 {
1053 int32 newpos;
1054
1055 if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */
1056 else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */
1057 sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
1058 return;
1059 }
1060
1061 /* Complement obverse routine */
1062
1063 int32 dt_comobv (int32 dat)
1064 {
1065 dat = dat ^ 07777; /* compl obverse */
1066 dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) |
1067 ((dat & 070) << 3) | ((dat & 07) << 9);
1068 return dat;
1069 }
1070
1071 /* Checksum routine */
1072
1073 int32 dt_csum (UNIT *uptr, int32 blk)
1074 {
1075 int16 *fbuf = (int16 *) uptr->filebuf;
1076 int32 ba = blk * DTU_BSIZE (uptr);
1077 int32 i, csum, wrd;
1078
1079 csum = 077; /* init csum */
1080 for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */
1081 wrd = fbuf[ba + i] ^ 07777; /* get ~word */
1082 csum = csum ^ (wrd >> 6) ^ wrd;
1083 }
1084 return (csum & 077);
1085 }
1086
1087 /* Reset routine */
1088
1089 t_stat dt_reset (DEVICE *dptr)
1090 {
1091 int32 i, prev_mot;
1092 UNIT *uptr;
1093
1094 for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */
1095 uptr = dt_dev.units + i;
1096 if (sim_is_running) { /* CAF? */
1097 prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */
1098 if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */
1099 if (dt_setpos (uptr)) continue; /* update pos */
1100 sim_cancel (uptr);
1101 sim_activate (uptr, dt_dctime); /* sched decel */
1102 DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0);
1103 }
1104 }
1105 else {
1106 sim_cancel (uptr); /* sim reset */
1107 uptr->STATE = 0;
1108 uptr->LASTT = sim_grtime ();
1109 }
1110 }
1111 dtsa = dtsb = 0; /* clear status */
1112 DT_UPDINT; /* reset interrupt */
1113 return SCPE_OK;
1114 }
1115
1116 /* Bootstrap routine
1117
1118 This is actually the 4K disk monitor bootstrap, which also
1119 works with OS/8. The reverse is not true - the OS/8 bootstrap
1120 doesn't work with the disk monitor.
1121 */
1122
1123 #define BOOT_START 0200
1124 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
1125
1126 static const uint16 boot_rom[] = {
1127 07600, /* 200, CLA CLL */
1128 01216, /* TAD MVB ; move back */
1129 04210, /* JMS DO ; action */
1130 01217, /* TAD K7577 ; addr */
1131 03620, /* DCA I CA */
1132 01222, /* TAD RDF ; read fwd */
1133 04210, /* JMS DO ; action */
1134 05600, /* JMP I 200 ; enter boot */
1135 00000, /* DO, 0 */
1136 06766, /* DTCA!DTXA ; start tape */
1137 03621, /* DCA I WC ; clear wc */
1138 06771, /* DTSF ; wait */
1139 05213, /* JMP .-1 */
1140 05610, /* JMP I DO */
1141 00600, /* MVB, 0600 */
1142 07577, /* K7577, 7757 */
1143 07755, /* CA, 7755 */
1144 07754, /* WC, 7754 */
1145 00220 /* RF, 0220 */
1146 };
1147
1148 t_stat dt_boot (int32 unitno, DEVICE *dptr)
1149 {
1150 int32 i;
1151 extern int32 saved_PC;
1152
1153 if (unitno) return SCPE_ARG; /* only unit 0 */
1154 if (dt_dib.dev != DEV_DTA) return STOP_NOTSTD; /* only std devno */
1155 dt_unit[unitno].pos = DT_EZLIN;
1156 for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
1157 saved_PC = BOOT_START;
1158 return SCPE_OK;
1159 }
1160
1161 /* Attach routine
1162
1163 Determine 12b, 16b, or 18b/36b format
1164 Allocate buffer
1165 If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
1166 If 12b, read data into buffer
1167 */
1168
1169 t_stat dt_attach (UNIT *uptr, char *cptr)
1170 {
1171 uint32 pdp18b[D18_NBSIZE];
1172 uint16 pdp11b[D18_NBSIZE], *fbuf;
1173 int32 i, k;
1174 int32 u = uptr - dt_dev.units;
1175 t_stat r;
1176 uint32 ba, sz;
1177
1178 r = attach_unit (uptr, cptr); /* attach */
1179 if (r != SCPE_OK) return r; /* fail? */
1180 if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */
1181 uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
1182 if (sim_switches & SWMASK ('F')) /* att 18b? */
1183 uptr->flags = uptr->flags & ~UNIT_8FMT;
1184 else if (sim_switches & SWMASK ('S')) /* att 16b? */
1185 uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
1186 else if (!(sim_switches & SWMASK ('A')) && /* autosize? */
1187 (sz = sim_fsize (uptr->fileref))) {
1188 if (sz == D11_FILSIZ)
1189 uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
1190 else if (sz > D8_FILSIZ)
1191 uptr->flags = uptr->flags & ~UNIT_8FMT;
1192 }
1193 }
1194 uptr->capac = DTU_CAPAC (uptr); /* set capacity */
1195 uptr->filebuf = calloc (uptr->capac, sizeof (uint16));
1196 if (uptr->filebuf == NULL) { /* can't alloc? */
1197 detach_unit (uptr);
1198 return SCPE_MEM;
1199 }
1200 fbuf = (uint16 *) uptr->filebuf; /* file buffer */
1201 printf ("%s%d: ", sim_dname (&dt_dev), u);
1202 if (uptr->flags & UNIT_8FMT) printf ("12b format");
1203 else if (uptr->flags & UNIT_11FMT) printf ("16b format");
1204 else printf ("18b/36b format");
1205 printf (", buffering file in memory\n");
1206 if (uptr->flags & UNIT_8FMT) /* 12b? */
1207 uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16),
1208 uptr->capac, uptr->fileref);
1209 else { /* 16b/18b */
1210 for (ba = 0; ba < uptr->capac; ) { /* loop thru file */
1211 if (uptr->flags & UNIT_11FMT) {
1212 k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref);
1213 for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i];
1214 }
1215 else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref);
1216 if (k == 0) break;
1217 for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0;
1218 for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */
1219 fbuf[ba] = (pdp18b[k] >> 6) & 07777;
1220 fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |
1221 ((pdp18b[k + 1] >> 12) & 077);
1222 fbuf[ba + 2] = pdp18b[k + 1] & 07777;
1223 ba = ba + 3;
1224 } /* end blk loop */
1225 } /* end file loop */
1226 uptr->hwmark = ba;
1227 } /* end else */
1228 uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */
1229 uptr->pos = DT_EZLIN; /* beyond leader */
1230 uptr->LASTT = sim_grtime (); /* last pos update */
1231 return SCPE_OK;
1232 }
1233
1234 /* Detach routine
1235
1236 Cancel in progress operation
1237 If 12b, write buffer to file
1238 If 16b or 18b, convert 12b buffer to 16b or 18b and write to file
1239 Deallocate buffer
1240 */
1241
1242 t_stat dt_detach (UNIT* uptr)
1243 {
1244 uint32 pdp18b[D18_NBSIZE];
1245 uint16 pdp11b[D18_NBSIZE], *fbuf;
1246 int32 i, k;
1247 int32 u = uptr - dt_dev.units;
1248 uint32 ba;
1249
1250 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */
1251 if (sim_is_active (uptr)) {
1252 sim_cancel (uptr);
1253 if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) {
1254 dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF;
1255 DT_UPDINT;
1256 }
1257 uptr->STATE = uptr->pos = 0;
1258 }
1259 fbuf = (uint16 *) uptr->filebuf; /* file buffer */
1260 if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */
1261 printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u);
1262 rewind (uptr->fileref); /* start of file */
1263 if (uptr->flags & UNIT_8FMT) /* PDP8? */
1264 fxwrite (uptr->filebuf, sizeof (uint16), /* write file */
1265 uptr->hwmark, uptr->fileref);
1266 else { /* 16b/18b */
1267 for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */
1268 for (k = 0; k < D18_NBSIZE; k = k + 2) {
1269 pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |
1270 ((uint32) (fbuf[ba + 1] >> 6) & 077);
1271 pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |
1272 ((uint32) (fbuf[ba + 2] & 07777));
1273 ba = ba + 3;
1274 } /* end loop blk */
1275 if (uptr->flags & UNIT_11FMT) { /* 16b? */
1276 for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i];
1277 fxwrite (pdp11b, sizeof (uint16),
1278 D18_NBSIZE, uptr->fileref);
1279 }
1280 else fxwrite (pdp18b, sizeof (uint32),
1281 D18_NBSIZE, uptr->fileref);
1282 } /* end loop buf */
1283 } /* end else */
1284 if (ferror (uptr->fileref)) perror ("I/O error");
1285 } /* end if hwmark */
1286 free (uptr->filebuf); /* release buf */
1287 uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */
1288 uptr->filebuf = NULL; /* clear buf ptr */
1289 uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */
1290 uptr->capac = DT_CAPAC; /* default size */
1291 return detach_unit (uptr);
1292 }