398d35b213d8c174e5ab1e1c39682399232e22b2
1 /* pdp8_dt.c: PDP-8 DECtape simulator
3 Copyright (c) 1993-2006, Robert M Supnik
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
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
51 PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words.
52 Three file formats are supported:
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]
58 When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format.
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:
64 reverse end zone 8192 reverse end zone codes ~ 10 feet
65 reverse buffer 200 interblock codes
69 forward buffer 200 interblock codes
70 forward end zone 8192 forward end zone codes ~ 10 feet
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).
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:
83 header word 1 block number (for forward reads)
85 header word 4 checksum (for reverse reads)
87 trailer word 4 checksum (for forward reads)
89 trailer word 1 block number (for reverse reads)
92 Write all writes only the data words and dumps the non-data words in the
96 #include "pdp8_defs.h"
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 */
111 /* System independent DECtape constants */
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 */
123 /* 16b, 18b, 36b DECtape constants */
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 */
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))
136 /* 12b DECtape constants */
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))
146 /* This controller */
148 #define DT_CAPAC D8_CAPAC /* default */
149 #define DT_WSIZE D8_WSIZE
151 /* Calculated constants, per unit */
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)
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))
167 /* Status register A */
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 */
174 #define DTA_V_MODE 6 /* mode */
175 #define DTA_V_FNC 3 /* function */
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))
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)
199 /* Status register B */
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 */
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 | \
220 #define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX))
224 #define DTS_V_MOT 3 /* motion */
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 */
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)
249 /* Operation substates */
251 #define DTO_WCO 1 /* wc overflow */
252 #define DTO_SOB 2 /* start of block */
256 #define LOG_MS 001 /* move, search */
257 #define LOG_RW 002 /* read, write */
258 #define LOG_BL 004 /* block # lblk */
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))
266 extern int32 int_req
;
267 extern UNIT cpu_unit
;
268 extern int32 sim_switches
;
269 extern FILE *sim_deb
;
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;
277 int32 dt_stopoffr
= 0;
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
;
298 /* DT data structures
300 dt_dev DT device descriptor
302 dt_reg DT register list
303 dt_mod DT modifier list
306 DIB dt_dib
= { DEV_DTA
, 2, { &dt76
, &dt77
} };
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
) }
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,
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
},
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
},
363 { "MOTION", LOG_MS
},
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,
380 int32
dt76 (int32 IR
, int32 AC
)
382 int32 pulse
= IR
& 07;
383 int32 old_dtsa
= dtsa
, fnc
;
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
);
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
);
409 int32
dt77 (int32 IR
, int32 AC
)
411 int32 pulse
= IR
& 07;
413 if ((pulse
& 01) && (dtsb
& (DTB_ERF
|DTB_DTF
))) /* DTSF */
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 */
425 void dt_deselect (int32 oldf
)
427 int32 old_unit
= DTA_GETUNIT (oldf
);
428 UNIT
*uptr
= dt_dev
.units
+ old_unit
;
429 int32 old_mot
= DTS_GETMOT (uptr
->STATE
);
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
);
438 /* Command register change
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
456 - set function as current state, schedule function
459 void dt_newsa (int32 newf
)
461 int32 new_unit
, prev_mot
, new_fnc
;
462 int32 prev_mving
, new_mving
, prev_dir
, new_dir
;
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 */
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? */
478 if ((prev_mving
| new_mving
) == 0) return; /* stop to stop */
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 */
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 */
495 DTS_SETSTA (DTS_DECF
| prev_dir
, 0); /* state = decel */
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 */
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 */
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 */
520 if (prev_mot
< DTS_ATSF
) { /* not at speed? */
521 DTS_SET2ND (DTS_ATSF
| new_dir
, new_fnc
); /* next = fnc */
525 dt_newfnc (uptr
, DTS_STA (DTS_ATSF
| new_dir
, new_fnc
));/* state = fnc */
529 /* Schedule new DECtape function
531 This routine is only called if
532 - the selected unit is attached
533 - the selected unit is at speed (forward or backward)
536 - updates the selected unit's position
537 - updates the selected unit's state
538 - schedules the new operation
541 void dt_newfnc (UNIT
*uptr
, int32 newsta
)
543 int32 fnc
, dir
, blk
, unum
, relpos
, newpos
;
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
);
556 if (dir
? DT_QREZ (uptr
): DT_QFEZ (uptr
)) { /* wrong ez? */
557 dt_seterr (uptr
, DTB_END
); /* set ez flag, stop */
560 sim_cancel (uptr
); /* cancel cur op */
561 dt_substate
= DTO_SOB
; /* substate = block start */
562 switch (fnc
) { /* case function */
564 case DTS_OFR
: /* off reel */
565 if (dir
) newpos
= -1000; /* rev? < start */
566 else newpos
= DTU_FWDEZ (uptr
) + DT_EZLIN
+ 1000; /* fwd? > end */
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"));
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"));
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);
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
);
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);
606 dt_seterr (uptr
, DTB_SEL
); /* bad state */
610 sim_activate (uptr
, ABS (newpos
- ((int32
) uptr
->pos
)) * dt_ltime
);
614 /* Update DECtape position
616 DECtape motion is modeled as a constant velocity, with linear
617 acceleration and deceleration. The motion equations are as follows:
619 t = time since operation started
620 tmax = time for operation (accel, decel only)
621 v = at speed velocity in lines (= 1/dt_ltime)
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)
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.
632 t_bool
dt_setpos (UNIT
*uptr
)
634 uint32 new_time
, ut
, ulin
, udelt
;
635 int32 mot
= DTS_GETMOT (uptr
->STATE
);
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 */
644 case DTS_STOP
: /* stop */
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
);
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
);
660 case DTS_ATSF
: /* at speed */
661 delta
= ut
/ (uint32
) dt_ltime
;
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 */
681 Unit must be attached, detach cancels operation
684 t_stat
dt_svc (UNIT
*uptr
)
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
;
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
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 */
711 case DTS_ACCF
: case DTS_ACCR
: /* accelerating */
712 dt_newfnc (uptr
, DTS_NXTSTA (uptr
->STATE
)); /* adv state, sched */
715 case DTS_ATSF
: case DTS_ATSR
: /* at speed */
716 break; /* check function */
719 dt_seterr (uptr
, DTB_SEL
); /* state error */
725 Move - must be at end zone
726 Search - transfer block number, schedule next block
727 Off reel - detach unit (it must be deselected)
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 */
736 blk
= DT_LIN2BL (uptr
->pos
, uptr
); /* get block # */
737 switch (fnc
) { /* at speed, check fnc */
739 case FNC_MOVE
: /* move */
740 dt_seterr (uptr
, DTB_END
); /* end zone error */
743 case FNC_SRCH
: /* search */
744 if (dtsb
& DTB_DTF
) { /* DTF set? */
745 dt_seterr (uptr
, DTB_TIM
); /* timing error */
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 */
756 case DTS_OFR
: /* off reel */
757 detach_unit (uptr
); /* must be deselected */
758 uptr
->STATE
= uptr
->pos
= 0; /* no visible action */
761 /* Read has four subcases
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
774 case FNC_READ
: /* read */
775 wrd
= DT_LIN2WD (uptr
->pos
, uptr
); /* get word # */
776 switch (dt_substate
) { /* case on substate */
778 case DTO_SOB
: /* start of block */
779 if (dtsb
& DTB_DTF
) { /* DTF set? */
780 dt_seterr (uptr
, DTB_TIM
); /* timing error */
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
);
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 */
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
);
818 /* Write has four subcases
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 -
826 if end of block, possibly set DTF, next state is start of block
827 Wc ovf, start of block - schedule end zone
830 case FNC_WRIT
: /* write */
831 wrd
= DT_LIN2WD (uptr
->pos
, uptr
); /* get word # */
832 switch (dt_substate
) { /* case on substate */
834 case DTO_SOB
: /* start block */
835 if (dtsb
& DTB_DTF
) { /* DTF set? */
836 dt_seterr (uptr
, DTB_TIM
); /* timing error */
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
);
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 */
866 case DTO_WCO
| DTO_SOB
: /* all done */
867 dt_schedez (uptr
, dir
); /* sched end zone */
873 /* Read all has two subcases
875 Not word count overflow - increment MA, WC, copy word from tape to memory
876 Word count overflow - schedule end zone
880 switch (dt_substate
) { /* case on substate */
882 case 0: case DTO_SOB
: /* read in progress */
883 if (dtsb
& DTB_DTF
) { /* DTF set? */
884 dt_seterr (uptr
, DTB_TIM
); /* timing error */
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 */
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 */
906 case DTO_WCO
: case DTO_WCO
| DTO_SOB
: /* all done */
907 dt_schedez (uptr
, dir
); /* sched end zone */
909 } /* end case substate */
913 /* Write all has two subcases
915 Not word count overflow - increment MA, WC, copy word from memory to tape
916 Word count overflow - schedule end zone
920 switch (dt_substate
) { /* case on substate */
922 case 0: case DTO_SOB
: /* read in progress */
923 if (dtsb
& DTB_DTF
) { /* DTF set? */
924 dt_seterr (uptr
, DTB_TIM
); /* timing error */
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;
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 */
947 case DTO_WCO
: case DTO_WCO
| DTO_SOB
: /* all done */
948 dt_schedez (uptr
, dir
); /* sched end zone */
950 } /* end case substate */
954 dt_seterr (uptr
, DTB_SEL
); /* impossible state */
958 DT_UPDINT
; /* update interrupts */
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
967 Word Word Content Word Word Content
968 (abs) (rel) (abs) (rel)
970 137 8 fwd csm'00 6 6 rev csm'00
974 141 12 00'lo rev blk 2 2 00'lo fwd blk
975 142 13 hi rev blk 1 1 hi fwd blk
979 2 2 hi fwd blk 141 12 hi rev blk
980 3 3 lo fwd blk'00 140 11 lo rev blk'00
984 7 7 rev csm 136 7 00'fwd csm
987 int32
dt_gethdr (UNIT
*uptr
, int32 blk
, int32 relpos
, int32 dir
)
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 */
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 */
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 */
1022 default: /* others */
1029 /* Utility routines */
1031 /* Set error flag */
1033 void dt_seterr (UNIT
*uptr
, int32 e
)
1035 int32 mot
= DTS_GETMOT (uptr
->STATE
);
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 */
1049 /* Schedule end zone */
1051 void dt_schedez (UNIT
*uptr
, int32 dir
)
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
);
1061 /* Complement obverse routine */
1063 int32
dt_comobv (int32 dat
)
1065 dat
= dat
^ 07777; /* compl obverse */
1066 dat
= ((dat
>> 9) & 07) | ((dat
>> 3) & 070) |
1067 ((dat
& 070) << 3) | ((dat
& 07) << 9);
1071 /* Checksum routine */
1073 int32
dt_csum (UNIT
*uptr
, int32 blk
)
1075 int16
*fbuf
= (int16
*) uptr
->filebuf
;
1076 int32 ba
= blk
* DTU_BSIZE (uptr
);
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
;
1084 return (csum
& 077);
1089 t_stat
dt_reset (DEVICE
*dptr
)
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 */
1101 sim_activate (uptr
, dt_dctime
); /* sched decel */
1102 DTS_SETSTA (DTS_DECF
| (prev_mot
& DTS_DIR
), 0);
1106 sim_cancel (uptr
); /* sim reset */
1108 uptr
->LASTT
= sim_grtime ();
1111 dtsa
= dtsb
= 0; /* clear status */
1112 DT_UPDINT
; /* reset interrupt */
1116 /* Bootstrap routine
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.
1123 #define BOOT_START 0200
1124 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
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 */
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 */
1148 t_stat
dt_boot (int32 unitno
, DEVICE
*dptr
)
1151 extern int32 saved_PC
;
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
;
1163 Determine 12b, 16b, or 18b/36b format
1165 If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
1166 If 12b, read data into buffer
1169 t_stat
dt_attach (UNIT
*uptr
, char *cptr
)
1171 uint32 pdp18b
[D18_NBSIZE
];
1172 uint16 pdp11b
[D18_NBSIZE
], *fbuf
;
1174 int32 u
= uptr
- dt_dev
.units
;
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
;
1194 uptr
->capac
= DTU_CAPAC (uptr
); /* set capacity */
1195 uptr
->filebuf
= calloc (uptr
->capac
, sizeof (uint16
));
1196 if (uptr
->filebuf
== NULL
) { /* can't alloc? */
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
];
1215 else k
= fxread (pdp18b
, sizeof (uint32
), D18_NBSIZE
, uptr
->fileref
);
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;
1224 } /* end blk loop */
1225 } /* end file loop */
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 */
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
1242 t_stat
dt_detach (UNIT
* uptr
)
1244 uint32 pdp18b
[D18_NBSIZE
];
1245 uint16 pdp11b
[D18_NBSIZE
], *fbuf
;
1247 int32 u
= uptr
- dt_dev
.units
;
1250 if (!(uptr
->flags
& UNIT_ATT
)) return SCPE_OK
; /* attached? */
1251 if (sim_is_active (uptr
)) {
1253 if ((u
== DTA_GETUNIT (dtsa
)) && (dtsa
& DTA_STSTP
)) {
1254 dtsb
= dtsb
| DTB_ERF
| DTB_SEL
| DTB_DTF
;
1257 uptr
->STATE
= uptr
->pos
= 0;
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));
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
);
1280 else fxwrite (pdp18b
, sizeof (uint32
),
1281 D18_NBSIZE
, uptr
->fileref
);
1282 } /* end loop buf */
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
);