First Commit of my working state
[simh.git] / PDP18B / pdp18b_dt.c
CommitLineData
196ba1fc
PH
1/* pdp18b_dt.c: 18b DECtape simulator\r
2\r
3 Copyright (c) 1993-2006, Robert M Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25\r
26 dt (PDP-4, PDP-7) Type 550/555 DECtape\r
27 (PDP-9) TC02/TU55 DECtape\r
28 (PDP-15) TC15/TU56 DECtape\r
29\r
30 23-Jun-06 RMS Fixed switch conflict in ATTACH\r
31 Revised Type 550 header based on DECTOG formatter\r
32 13-Jun-06 RMS Fixed checksum calculation bug in Type 550\r
33 16-Aug-05 RMS Fixed C++ declaration and cast problems\r
34 25-Jan-04 RMS Revised for device debug support\r
35 14-Jan-04 RMS Revised IO device call interface\r
36 Changed sim_fsize calling sequence, added STOP_OFFR\r
37 26-Oct-03 RMS Cleaned up buffer copy code\r
38 18-Oct-03 RMS Fixed reverse checksum in read all\r
39 Added DECtape off reel message\r
40 Simplified timing\r
41 25-Apr-03 RMS Revised for extended file support\r
42 14-Mar-03 RMS Fixed variable size interaction with save/restore\r
43 17-Oct-02 RMS Fixed bug in end of reel logic\r
44 05-Oct-02 RMS Added DIB, device number support\r
45 12-Sep-02 RMS Added 16b format support\r
46 13-Aug-02 RMS Corrected Type 550 unit select logic\r
47 25-Jul-02 RMS Added PDP-4 support\r
48 30-May-02 RMS Widened POS to 32b\r
49 10-Feb-02 RMS Added PDP-7 support\r
50 06-Jan-02 RMS Revised enable/disable support\r
51 29-Nov-01 RMS Added read only unit support\r
52 25-Nov-01 RMS Revised interrupt structure\r
53 Changed POS, STATT, LASTT, FLG to arrays\r
54 29-Aug-01 RMS Added casts to PDP-8 unpack routine\r
55 17-Jul-01 RMS Moved function prototype\r
56 11-May-01 RMS Fixed bug in reset\r
57 26-Apr-01 RMS Added device enable/disable support\r
58 15-Mar-01 RMS Added 129th word to PDP-8 format\r
59\r
60 18b DECtapes are represented in memory by fixed length buffer of 32b words.\r
61 Three file formats are supported:\r
62\r
63 18b/36b 256 words per block [256 x 18b]\r
64 16b 256 words per block [256 x 16b]\r
65 12b 129 words per block [129 x 12b]\r
66\r
67 When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format.\r
68\r
69 DECtape motion is measured in 3b lines. Time between lines is 33.33us.\r
70 Tape density is nominally 300 lines per inch. The format of a DECtape (as\r
71 taken from the PDP-7 formatter) is:\r
72\r
73 reverse end zone 7144 reverse end zone codes ~ 12 feet\r
74 reverse buffer 200 interblock codes\r
75 block 0\r
76 :\r
77 block n\r
78 forward buffer 200 interblock codes\r
79 forward end zone 7144 forward end zone codes ~ 12 feet\r
80\r
81 A block consists of five 18b header words, a tape-specific number of data\r
82 words, and five 18b trailer words. All systems except the PDP-8 use a\r
83 standard block length of 256 words; the PDP-8 uses a standard block length\r
84 of 86 words (x 18b = 129 words x 12b). PDP-4/7 DECtapes came in two\r
85 formats. The first 5 controllers used a 4 word header/trailer (missing\r
86 word 0/4). All later serial numbers used the standard header. The later,\r
87 standard header/trailer is simulated here.\r
88\r
89 Because a DECtape file only contains data, the simulator cannot support\r
90 write timing and mark track and can only do a limited implementation\r
91 of read all and write all. Read all assumes that the tape has been\r
92 conventionally written forward:\r
93\r
94 header word 0 0\r
95 header word 1 block number (for forward reads)\r
96 header words 2,3 0\r
97 header word 4 checksum (for reverse reads)\r
98 :\r
99 trailer word 4 checksum (for forward reads)\r
100 trailer words 3,2 0\r
101 trailer word 1 block number (for reverse reads)\r
102 trailer word 0 0\r
103\r
104 Write all writes only the data words and dumps the interblock words in the\r
105 bit bucket.\r
106\r
107 The Type 550 controller has a 4b unit select field, for units 1-8; the TC02\r
108 has a 3b unit select field, with unit 8 being represented as 0. The code\r
109 assumes that the GETUNIT macro returns a unit number in the range of 0-7,\r
110 with 8 represented as 0, and an invalid unit as -1.\r
111*/\r
112\r
113#include "pdp18b_defs.h"\r
114\r
115#define DT_NUMDR 8 /* #drives */\r
116#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */\r
117#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */\r
118#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */\r
119#define UNIT_WLK (1 << UNIT_V_WLK)\r
120#define UNIT_8FMT (1 << UNIT_V_8FMT)\r
121#define UNIT_11FMT (1 << UNIT_V_11FMT)\r
122#define STATE u3 /* unit state */\r
123#define LASTT u4 /* last time update */\r
124#define DT_WC 030 /* word count */\r
125#define DT_CA 031 /* current addr */\r
126#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */\r
127\r
128/* System independent DECtape constants */\r
129\r
130#define DT_LPERMC 6 /* lines per mark track */\r
131#define DT_BLKWD 1 /* blk no word in h/t */\r
132#define DT_CSMWD 4 /* checksum word in h/t */\r
133#define DT_HTWRD 5 /* header/trailer words */\r
134#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */\r
135#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */\r
136#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */\r
137#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */\r
138#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */\r
139\r
140/* 16b, 18b, 36b DECtape constants */\r
141\r
142#define D18_WSIZE 6 /* word size in lines */\r
143#define D18_BSIZE 256 /* block size in 18b */\r
144#define D18_TSIZE 578 /* tape size */\r
145#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)\r
146#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))\r
147#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */\r
148#define D11_FILSIZ (D18_CAPAC * sizeof (int16))\r
149\r
150/* 12b DECtape constants */\r
151\r
152#define D8_WSIZE 4 /* word size in lines */\r
153#define D8_BSIZE 86 /* block size in 18b */\r
154#define D8_TSIZE 1474 /* tape size */\r
155#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)\r
156#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))\r
157#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */\r
158\r
159#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE)\r
160#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16))\r
161\r
162/* This controller */\r
163\r
164#define DT_CAPAC D18_CAPAC /* default */\r
165#define DT_WSIZE D18_WSIZE\r
166\r
167/* Calculated constants, per unit */\r
168\r
169#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)\r
170#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)\r
171#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)\r
172#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)\r
173#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)\r
174\r
175#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))\r
176#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))\r
177#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE)\r
178#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN)\r
179#define DT_QREZ(u) (((u)->pos) < DT_EZLIN)\r
180#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u)))\r
181#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u))\r
182\r
183/* Status register A */\r
184\r
185#if defined (TC02) /* TC02/TC15 */\r
186#define DTA_V_UNIT 15 /* unit select */\r
187#define DTA_M_UNIT 07\r
188#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT)\r
189#define DTA_V_MOT 13 /* motion */\r
190#define DTA_M_MOT 03\r
191#define DTA_V_MODE 12 /* mode */\r
192#define DTA_V_FNC 9 /* function */\r
193#define DTA_M_FNC 07\r
194#define FNC_MOVE 00 /* move */\r
195#define FNC_SRCH 01 /* search */\r
196#define FNC_READ 02 /* read */\r
197#define FNC_RALL 03 /* read all */\r
198#define FNC_WRIT 04 /* write */\r
199#define FNC_WALL 05 /* write all */\r
200#define FNC_WMRK 06 /* write timing */\r
201#define DTA_V_ENB 8 /* int enable */\r
202#define DTA_V_CERF 7 /* clr error flag */\r
203#define DTA_V_CDTF 6 /* clr DECtape flag */\r
204#define DTA_FWDRV (1u << (DTA_V_MOT + 1))\r
205#define DTA_STSTP (1u << DTA_V_MOT)\r
206#define DTA_MODE (1u << DTA_V_MODE)\r
207#define DTA_ENB (1u << DTA_V_ENB)\r
208#define DTA_CERF (1u << DTA_V_CERF)\r
209#define DTA_CDTF (1u << DTA_V_CDTF)\r
210#define DTA_RW (0777700 & ~(DTA_CERF | DTA_CDTF))\r
211#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT)\r
212#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \\r
213 SET_INT (DTA); \\r
214 else CLR_INT (DTA);\r
215\r
216#else /* Type 550 */\r
217#define DTA_V_UNIT 12 /* unit select */\r
218#define DTA_M_UNIT 017\r
219#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT)\r
220#define DTA_V_MOT 4 /* motion */\r
221#define DTA_M_MOT 03\r
222#define DTA_V_FNC 0 /* function */\r
223#define DTA_M_FNC 07\r
224#define FNC_MOVE 00 /* move */\r
225#define FNC_SRCH 01 /* search */\r
226#define FNC_READ 02 /* read */\r
227#define FNC_WRIT 03 /* write */\r
228#define FNC_RALL 05 /* read all */\r
229#define FNC_WALL 06 /* write all */\r
230#define FNC_WMRK 07 /* write timing */\r
231#define DTA_STSTP (1u << (DTA_V_MOT + 1))\r
232#define DTA_FWDRV (1u << DTA_V_MOT)\r
233#define DTA_MODE 0 /* not implemented */\r
234#define DTA_RW 077\r
235#define DTA_GETUNIT(x) map_unit[(((x) >> DTA_V_UNIT) & DTA_M_UNIT)]\r
236#define DT_UPDINT if (dtsb & (DTB_DTF | DTB_BEF | DTB_ERF)) \\r
237 SET_INT (DTA); \\r
238 else CLR_INT (DTA);\r
239#endif\r
240\r
241#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT)\r
242#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC)\r
243\r
244/* Status register B */\r
245\r
246#if defined (TC02) /* TC02/TC15 */\r
247#define DTB_V_ERF 17 /* error flag */\r
248#define DTB_V_MRK 16 /* mark trk err */\r
249#define DTB_V_END 15 /* end zone err */\r
250#define DTB_V_SEL 14 /* select err */\r
251#define DTB_V_PAR 13 /* parity err */\r
252#define DTB_V_TIM 12 /* timing err */\r
253#define DTB_V_DTF 6 /* DECtape flag */\r
254#define DTB_ERF (1u << DTB_V_ERF)\r
255#define DTB_MRK (1u << DTB_V_MRK)\r
256#define DTB_END (1u << DTB_V_END)\r
257#define DTB_SEL (1u << DTB_V_SEL)\r
258#define DTB_PAR (1u << DTB_V_PAR)\r
259#define DTB_TIM (1u << DTB_V_TIM)\r
260#define DTB_DTF (1u << DTB_V_DTF)\r
261#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \\r
262 DTB_PAR | DTB_TIM)\r
263\r
264#else /* Type 550 */\r
265#define DTB_V_DTF 17 /* data flag */\r
266#define DTB_V_BEF 16 /* block end flag */\r
267#define DTB_V_ERF 15 /* error flag */\r
268#define DTB_V_END 14 /* end of tape */\r
269#define DTB_V_TIM 13 /* timing err */\r
270#define DTB_V_REV 12 /* reverse */\r
271#define DTB_V_GO 11 /* go */\r
272#define DTB_V_MRK 10 /* mark trk err */\r
273#define DTB_V_SEL 9 /* select err */\r
274#define DTB_DTF (1u << DTB_V_DTF)\r
275#define DTB_BEF (1u << DTB_V_BEF)\r
276#define DTB_ERF (1u << DTB_V_ERF)\r
277#define DTB_END (1u << DTB_V_END)\r
278#define DTB_TIM (1u << DTB_V_TIM)\r
279#define DTB_REV (1u << DTB_V_REV)\r
280#define DTB_GO (1u << DTB_V_GO)\r
281#define DTB_MRK (1u << DTB_V_MRK)\r
282#define DTB_SEL (1u << DTB_V_SEL)\r
283#define DTB_ALLERR (DTB_END | DTB_TIM | DTB_MRK | DTB_SEL)\r
284#endif\r
285\r
286/* DECtape state */\r
287\r
288#define DTS_V_MOT 3 /* motion */\r
289#define DTS_M_MOT 07\r
290#define DTS_STOP 0 /* stopped */\r
291#define DTS_DECF 2 /* decel, fwd */\r
292#define DTS_DECR 3 /* decel, rev */\r
293#define DTS_ACCF 4 /* accel, fwd */\r
294#define DTS_ACCR 5 /* accel, rev */\r
295#define DTS_ATSF 6 /* @speed, fwd */\r
296#define DTS_ATSR 7 /* @speed, rev */\r
297#define DTS_DIR 01 /* dir mask */\r
298#define DTS_V_FNC 0 /* function */\r
299#define DTS_M_FNC 07\r
300#define DTS_OFR 7 /* "off reel" */\r
301#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT)\r
302#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC)\r
303#define DTS_V_2ND 6 /* next state */\r
304#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */\r
305#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC))\r
306#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z)\r
307#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \\r
308 ((DTS_STA (y, z)) << DTS_V_2ND)\r
309#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \\r
310 ((DTS_STA (y, z)) << DTS_V_3RD)\r
311#define DTS_NXTSTA(x) (x >> DTS_V_2ND)\r
312\r
313/* Operation substates */\r
314\r
315#define DTO_WCO 1 /* wc overflow */\r
316#define DTO_SOB 2 /* start of block */\r
317\r
318/* Logging */\r
319\r
320#define LOG_MS 001 /* move, search */\r
321#define LOG_RW 002 /* read, write */\r
322#define LOG_RA 004 /* read all */\r
323#define LOG_BL 010 /* block # lblk */\r
324\r
325#define ABS(x) (((x) < 0)? (-(x)): (x))\r
326\r
327extern int32 M[];\r
328extern int32 int_hwre[API_HLVL+1];\r
329extern UNIT cpu_unit;\r
330extern int32 sim_switches;\r
331extern int32 sim_is_running;\r
332extern FILE *sim_deb;\r
333\r
334int32 dtsa = 0; /* status A */\r
335int32 dtsb = 0; /* status B */\r
336int32 dtdb = 0; /* data buffer */\r
337int32 dt_ltime = 12; /* interline time */\r
338int32 dt_dctime = 40000; /* decel time */\r
339int32 dt_substate = 0;\r
340int32 dt_logblk = 0;\r
341int32 dt_stopoffr = 0; /* stop on off reel */\r
342static const int32 map_unit[16] = { /* Type 550 unit map */\r
343 -1, 1, 2, 3, 4, 5, 6, 7,\r
344 0, -1, -1, -1, -1, -1, -1, -1\r
345 };\r
346\r
347DEVICE dt_dev;\r
348int32 dt75 (int32 dev, int32 pulse, int32 dat);\r
349int32 dt76 (int32 dev, int32 pulse, int32 dat);\r
350int32 dt_iors (void);\r
351t_stat dt_svc (UNIT *uptr);\r
352t_stat dt_reset (DEVICE *dptr);\r
353t_stat dt_attach (UNIT *uptr, char *cptr);\r
354t_stat dt_detach (UNIT *uptr);\r
355void dt_deselect (int32 oldf);\r
356void dt_newsa (int32 newf);\r
357void dt_newfnc (UNIT *uptr, int32 newsta);\r
358t_bool dt_setpos (UNIT *uptr);\r
359void dt_schedez (UNIT *uptr, int32 dir);\r
360void dt_seterr (UNIT *uptr, int32 e);\r
361int32 dt_comobv (int32 val);\r
362int32 dt_csum (UNIT *uptr, int32 blk);\r
363int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos);\r
364\r
365/* DT data structures\r
366\r
367 dt_dev DT device descriptor\r
368 dt_unit DT unit list\r
369 dt_reg DT register list\r
370 dt_mod DT modifier list\r
371*/\r
372\r
373DIB dt_dib = { DEV_DTA, 2, &dt_iors, { &dt75, &dt76 } };\r
374\r
375UNIT dt_unit[] = {\r
376 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
377 UNIT_ROABLE, DT_CAPAC) },\r
378 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
379 UNIT_ROABLE, DT_CAPAC) },\r
380 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
381 UNIT_ROABLE, DT_CAPAC) },\r
382 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
383 UNIT_ROABLE, DT_CAPAC) },\r
384 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
385 UNIT_ROABLE, DT_CAPAC) },\r
386 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
387 UNIT_ROABLE, DT_CAPAC) },\r
388 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
389 UNIT_ROABLE, DT_CAPAC) },\r
390 { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r
391 UNIT_ROABLE, DT_CAPAC) }\r
392 };\r
393\r
394REG dt_reg[] = {\r
395 { ORDATA (DTSA, dtsa, 18) },\r
396 { ORDATA (DTSB, dtsb, 18) },\r
397 { ORDATA (DTDB, dtdb, 18) },\r
398 { FLDATA (INT, int_hwre[API_DTA], INT_V_DTA) },\r
399#if defined (DTA_V_ENB)\r
400 { FLDATA (ENB, dtsa, DTA_V_ENB) },\r
401#endif\r
402 { FLDATA (DTF, dtsb, DTB_V_DTF) },\r
403#if defined (DTB_V_BEF)\r
404 { FLDATA (BEF, dtsb, DTB_V_BEF) },\r
405#endif\r
406 { FLDATA (ERF, dtsb, DTB_V_ERF) },\r
407#if defined (TC02) /* TC02/TC15 */\r
408 { ORDATA (WC, M[DT_WC], 18) },\r
409 { ORDATA (CA, M[DT_CA], 18) },\r
410#endif\r
411 { DRDATA (LTIME, dt_ltime, 31), REG_NZ },\r
412 { DRDATA (DCTIME, dt_dctime, 31), REG_NZ },\r
413 { ORDATA (SUBSTATE, dt_substate, 2) },\r
414 { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN },\r
415 { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0,\r
416 DT_NUMDR, PV_LEFT | REG_RO) },\r
417 { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0,\r
418 DT_NUMDR, REG_RO) },\r
419 { URDATA (LASTT, dt_unit[0].LASTT, 10, T_ADDR_W, 0,\r
420 DT_NUMDR, REG_HRO) },\r
421 { ORDATA (DEVNO, dt_dib.dev, 6), REG_HRO },\r
422 { FLDATA (STOP_OFFR, dt_stopoffr, 0) },\r
423 { NULL }\r
424 };\r
425\r
426MTAB dt_mod[] = {\r
427 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r
428 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, \r
429 { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },\r
430 { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },\r
431 { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },\r
432 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno },\r
433 { 0 }\r
434 };\r
435\r
436DEBTAB dt_deb[] = {\r
437 { "MOTION", LOG_MS },\r
438 { "DATA", LOG_RW },\r
439 { "READALL", LOG_RA },\r
440 { "BLOCK", LOG_BL },\r
441 { NULL, 0 }\r
442 };\r
443\r
444DEVICE dt_dev = {\r
445 "DT", dt_unit, dt_reg, dt_mod,\r
446 DT_NUMDR, 8, 24, 1, 8, 18,\r
447 NULL, NULL, &dt_reset,\r
448 NULL, &dt_attach, &dt_detach,\r
449 &dt_dib, DEV_DISABLE | DEV_DEBUG, 0,\r
450 dt_deb, NULL, NULL\r
451 };\r
452\r
453/* IOT routines */\r
454\r
455#if defined (TC02) /* TC02/TC15 */\r
456int32 dt75 (int32 dev, int32 pulse, int32 dat)\r
457{\r
458int32 old_dtsa = dtsa, fnc;\r
459UNIT *uptr;\r
460\r
461if (((pulse & 060) == 040) && (pulse & 05)) { /* select */\r
462 if (pulse & 01) dtsa = 0; /* DTCA */\r
463 if (pulse & 02) dat = dtsa; /* DTRA!... */\r
464 if (pulse & 04) { /* DTXA */\r
465 if ((dat & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR;\r
466 if ((dat & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF;\r
467 dtsa = dtsa ^ (dat & DTA_RW);\r
468 }\r
469 if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa);\r
470 uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */\r
471 fnc = DTA_GETFNC (dtsa); /* get fnc */\r
472 if (((uptr->flags) & UNIT_DIS) || /* disabled? */\r
473 (fnc >= FNC_WMRK) || /* write mark? */\r
474 ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)) ||\r
475 ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)))\r
476 dt_seterr (uptr, DTB_SEL); /* select err */\r
477 else dt_newsa (dtsa); /* new func */\r
478 DT_UPDINT;\r
479 return dat;\r
480 }\r
481if ((pulse & 067) == 042) return dtsa; /* DTRA */\r
482if ((pulse & 067) == 061) /* DTEF */\r
483 return ((dtsb & DTB_ERF)? IOT_SKP + dat: dat);\r
484if ((pulse & 067) == 062) return dtsb; /* DTRB */\r
485if ((pulse & 067) == 063) /* DTEF!DTRB */\r
486 return ((dtsb & DTB_ERF)? IOT_SKP + dtsb: dtsb);\r
487return dat;\r
488}\r
489\r
490int32 dt76 (int32 dev, int32 pulse, int32 dat)\r
491{\r
492if ((pulse & 01) && (dtsb & DTB_DTF)) /* DTDF */\r
493 return IOT_SKP + dat;\r
494return dat;\r
495}\r
496\r
497#else /* Type 550 */\r
498int32 dt75 (int32 dev, int32 pulse, int32 dat)\r
499{\r
500if (((pulse & 041) == 001) && (dtsb & DTB_DTF)) /* MMDF */\r
501 dat = dat | IOT_SKP;\r
502else if (((pulse & 041) == 041) && (dtsb & DTB_ERF)) /* MMEF */\r
503 dat = dat | IOT_SKP;\r
504if (pulse & 002) { /* MMRD */\r
505 dat = (dat & ~DMASK) | dtdb;\r
506 dtsb = dtsb & ~(DTB_DTF | DTB_BEF);\r
507 }\r
508if (pulse & 004) { /* MMWR */\r
509 dtdb = dat & DMASK;\r
510 dtsb = dtsb & ~(DTB_DTF | DTB_BEF);\r
511 }\r
512DT_UPDINT;\r
513return dat;\r
514}\r
515\r
516int32 dt76 (int32 dev, int32 pulse, int32 dat)\r
517{\r
518int32 fnc, mot, unum;\r
519UNIT *uptr = NULL;\r
520\r
521unum = DTA_GETUNIT (dtsa); /* get unit no */\r
522if (unum >= 0) uptr = dt_dev.units + unum; /* get unit */\r
523if ((pulse & 001) && (dtsb & DTB_BEF)) /* MMBF */\r
524 dat = dat | IOT_SKP;\r
525if (pulse & 002) { /* MMRS */\r
526 dtsb = dtsb & ~(DTB_REV | DTB_GO); /* clr rev, go */\r
527 if (uptr) { /* valid unit? */\r
528 mot = DTS_GETMOT (uptr->STATE); /* get motion */\r
529 if (mot & DTS_DIR) dtsb = dtsb | DTB_REV; /* rev? set */\r
530 if ((mot >= DTS_ACCF) || (uptr->STATE & 0777700))\r
531 dtsb = dtsb | DTB_GO; /* accel? go */\r
532 }\r
533 dat = (dat & ~DMASK) | dtsb;\r
534 }\r
535if ((pulse & 044) == 044) { /* MMSE */\r
536 if ((dtsa ^ dat) & DTA_UNIT) dt_deselect (dtsa); /* new unit? */\r
537 dtsa = (dtsa & ~DTA_UNIT) | (dat & DTA_UNIT);\r
538 dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR);\r
539 }\r
540else if ((pulse & 044) == 004) { /* MMLC */\r
541 dtsa = (dtsa & ~DTA_RW) | (dat & DTA_RW); /* load dtsa */\r
542 dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR);\r
543 fnc = DTA_GETFNC (dtsa); /* get fnc */\r
544 if ((uptr == NULL) || /* invalid? */\r
545 ((uptr->flags) & UNIT_DIS) || /* disabled? */\r
546 (fnc >= FNC_WMRK) || /* write mark? */\r
547 ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WLK)) ||\r
548 ((fnc == FNC_WALL) && (uptr->flags & UNIT_WLK)))\r
549 dt_seterr (uptr, DTB_SEL); /* select err */\r
550 else dt_newsa (dtsa);\r
551 }\r
552DT_UPDINT;\r
553return dat;\r
554}\r
555#endif\r
556\r
557/* Unit deselect */\r
558\r
559void dt_deselect (int32 oldf)\r
560{\r
561int32 old_unit, old_mot;\r
562UNIT *uptr;\r
563\r
564old_unit = DTA_GETUNIT (oldf); /* get unit no */\r
565if (old_unit < 0) return; /* invalid? */\r
566uptr = dt_dev.units + old_unit; /* get unit */\r
567old_mot = DTS_GETMOT (uptr->STATE);\r
568if (old_mot >= DTS_ATSF) /* at speed? */\r
569 dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR));\r
570else if (old_mot >= DTS_ACCF) /* accelerating? */\r
571 DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR);\r
572return;\r
573}\r
574\r
575/* Command register change\r
576\r
577 1. If change in motion, stop to start\r
578 - schedule acceleration\r
579 - set function as next state\r
580 2. If change in motion, start to stop\r
581 - if not already decelerating (could be reversing),\r
582 schedule deceleration\r
583 3. If change in direction,\r
584 - if not decelerating, schedule deceleration\r
585 - set accelerating (other dir) as next state\r
586 - set function as next next state\r
587 4. If not accelerating or at speed,\r
588 - schedule acceleration\r
589 - set function as next state\r
590 5. If not yet at speed,\r
591 - set function as next state\r
592 6. If at speed,\r
593 - set function as current state, schedule function\r
594*/\r
595\r
596void dt_newsa (int32 newf)\r
597{\r
598int32 new_unit, prev_mot, new_fnc;\r
599int32 prev_mving, new_mving, prev_dir, new_dir;\r
600UNIT *uptr;\r
601\r
602new_unit = DTA_GETUNIT (newf); /* new unit */\r
603if (new_unit < 0) return; /* invalid? */\r
604uptr = dt_dev.units + new_unit;\r
605if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */\r
606 dt_seterr (uptr, DTB_SEL); /* no, error */\r
607 return;\r
608 }\r
609prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */\r
610prev_mving = prev_mot != DTS_STOP; /* previous moving? */\r
611prev_dir = prev_mot & DTS_DIR; /* previous dir? */\r
612new_mving = (newf & DTA_STSTP) != 0; /* new moving? */\r
613new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */\r
614new_fnc = DTA_GETFNC (newf); /* new function? */\r
615\r
616if ((prev_mving | new_mving) == 0) return; /* stop to stop */\r
617\r
618if (new_mving & ~prev_mving) { /* start? */\r
619 if (dt_setpos (uptr)) return; /* update pos */\r
620 sim_cancel (uptr); /* stop current */\r
621 sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */\r
622 DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */\r
623 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */\r
624 return;\r
625 }\r
626\r
627if (prev_mving & ~new_mving) { /* stop? */\r
628 if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */\r
629 if (dt_setpos (uptr)) return; /* update pos */\r
630 sim_cancel (uptr); /* stop current */\r
631 sim_activate (uptr, dt_dctime); /* schedule decel */\r
632 }\r
633 DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */\r
634 return;\r
635 }\r
636\r
637if (prev_dir ^ new_dir) { /* dir chg? */\r
638 if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */\r
639 if (dt_setpos (uptr)) return; /* update pos */\r
640 sim_cancel (uptr); /* stop current */\r
641 sim_activate (uptr, dt_dctime); /* schedule decel */\r
642 }\r
643 DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */\r
644 DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */\r
645 DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */\r
646 return;\r
647 }\r
648\r
649if (prev_mot < DTS_ACCF) { /* not accel/at speed? */\r
650 if (dt_setpos (uptr)) return; /* update pos */\r
651 sim_cancel (uptr); /* cancel cur */\r
652 sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */\r
653 DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */\r
654 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */\r
655 return;\r
656 }\r
657\r
658if (prev_mot < DTS_ATSF) { /* not at speed? */\r
659 DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */\r
660 return;\r
661 }\r
662\r
663dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */\r
664return; \r
665}\r
666\r
667/* Schedule new DECtape function\r
668\r
669 This routine is only called if\r
670 - the selected unit is attached\r
671 - the selected unit is at speed (forward or backward)\r
672\r
673 This routine\r
674 - updates the selected unit's position\r
675 - updates the selected unit's state\r
676 - schedules the new operation\r
677*/\r
678\r
679void dt_newfnc (UNIT *uptr, int32 newsta)\r
680{\r
681int32 fnc, dir, blk, unum, newpos;\r
682#if defined (TC02)\r
683int32 relpos;\r
684#endif\r
685uint32 oldpos;\r
686\r
687oldpos = uptr->pos; /* save old pos */\r
688if (dt_setpos (uptr)) return; /* update pos */\r
689uptr->STATE = newsta; /* update state */\r
690fnc = DTS_GETFNC (uptr->STATE); /* set variables */\r
691dir = DTS_GETMOT (uptr->STATE) & DTS_DIR;\r
692unum = (int32) (uptr - dt_dev.units);\r
693if (oldpos == uptr->pos) /* bump pos */\r
694 uptr->pos = uptr->pos + (dir? -1: 1);\r
695blk = DT_LIN2BL (uptr->pos, uptr);\r
696\r
697if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */\r
698 dt_seterr (uptr, DTB_END); /* set ez flag, stop */\r
699 return;\r
700 }\r
701sim_cancel (uptr); /* cancel cur op */\r
702dt_substate = DTO_SOB; /* substate = block start */\r
703switch (fnc) { /* case function */\r
704\r
705 case DTS_OFR: /* off reel */\r
706 if (dir) newpos = -1000; /* rev? < start */\r
707 else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */\r
708 break;\r
709\r
710 case FNC_MOVE: /* move */\r
711 dt_schedez (uptr, dir); /* sched end zone */\r
712 if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n",\r
713 unum, (dir? "backward": "forward"));\r
714 return; /* done */\r
715\r
716 case FNC_SRCH: /* search */\r
717 if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)?\r
718 DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE;\r
719 else newpos = DT_BLK2LN ((DT_QREZ (uptr)?\r
720 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1);\r
721 if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n",\r
722 unum, (dir? "backward": "forward"));\r
723 break;\r
724\r
725 case FNC_WRIT: /* write */\r
726 case FNC_READ: /* read */\r
727#if defined (TC02) /* TC02/TC15 */\r
728 if (DT_QEZ (uptr)) { /* in "ok" end zone? */\r
729 if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE;\r
730 else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1);\r
731 break;\r
732 }\r
733 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r
734 if ((relpos >= DT_HTLIN) && /* in data zone? */\r
735 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r
736 dt_seterr (uptr, DTB_SEL);\r
737 return;\r
738 }\r
739 if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))?\r
740 blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE;\r
741 else newpos = DT_BLK2LN (((relpos < DT_HTLIN)?\r
742 blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1);\r
743 break;\r
744#endif\r
745\r
746 case FNC_RALL: /* read all */\r
747 case FNC_WALL: /* write all */\r
748 if (DT_QEZ (uptr)) { /* in "ok" end zone? */\r
749 if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE;\r
750 else newpos = DT_EZLIN + (DT_WSIZE - 1);\r
751 }\r
752 else {\r
753 newpos = ((uptr->pos) / DT_WSIZE) * DT_WSIZE;\r
754 if (!dir) newpos = newpos + (DT_WSIZE - 1);\r
755 }\r
756 if (DEBUG_PRI (dt_dev, LOG_RA) ||\r
757 (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))\r
758 fprintf (sim_deb, ">>DT%d: read all block %d %s%s\n",\r
759 unum, blk, (dir? "backward": "forward"),\r
760 ((dtsa & DTA_MODE)? " continuous]": " "));\r
761 break;\r
762\r
763 default:\r
764 dt_seterr (uptr, DTB_SEL); /* bad state */\r
765 return;\r
766 }\r
767\r
768#if defined (TYPE550) /* Type 550 */\r
769if ((fnc == FNC_WRIT) || (fnc == FNC_WALL)) { /* write function? */\r
770 dtsb = dtsb | DTB_DTF; /* set data flag */\r
771 DT_UPDINT;\r
772 }\r
773#endif\r
774\r
775sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);\r
776return;\r
777}\r
778\r
779/* Update DECtape position\r
780\r
781 DECtape motion is modeled as a constant velocity, with linear\r
782 acceleration and deceleration. The motion equations are as follows:\r
783\r
784 t = time since operation started\r
785 tmax = time for operation (accel, decel only)\r
786 v = at speed velocity in lines (= 1/dt_ltime)\r
787\r
788 Then:\r
789 at speed dist = t * v\r
790 accel dist = (t^2 * v) / (2 * tmax)\r
791 decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)\r
792\r
793 This routine uses the relative (integer) time, rather than the absolute\r
794 (floating point) time, to allow save and restore of the start times.\r
795*/\r
796\r
797t_bool dt_setpos (UNIT *uptr)\r
798{\r
799uint32 new_time, ut, ulin, udelt;\r
800int32 mot = DTS_GETMOT (uptr->STATE);\r
801int32 unum, delta;\r
802\r
803new_time = sim_grtime (); /* current time */\r
804ut = new_time - uptr->LASTT; /* elapsed time */\r
805if (ut == 0) return FALSE; /* no time gone? exit */\r
806uptr->LASTT = new_time; /* update last time */\r
807switch (mot & ~DTS_DIR) { /* case on motion */\r
808\r
809 case DTS_STOP: /* stop */\r
810 delta = 0;\r
811 break;\r
812\r
813 case DTS_DECF: /* slowing */\r
814 ulin = ut / (uint32) dt_ltime;\r
815 udelt = dt_dctime / dt_ltime;\r
816 delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);\r
817 break;\r
818\r
819 case DTS_ACCF: /* accelerating */\r
820 ulin = ut / (uint32) dt_ltime;\r
821 udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime;\r
822 delta = (ulin * ulin) / (2 * udelt);\r
823 break;\r
824\r
825 case DTS_ATSF: /* at speed */\r
826 delta = ut / (uint32) dt_ltime;\r
827 break;\r
828 }\r
829\r
830if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */\r
831else uptr->pos = uptr->pos + delta;\r
832if (((int32) uptr->pos < 0) ||\r
833 ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {\r
834 detach_unit (uptr); /* off reel? */\r
835 uptr->STATE = uptr->pos = 0;\r
836 unum = (int32) (uptr - dt_dev.units);\r
837 if (unum == DTA_GETUNIT (dtsa)) /* if selected, */\r
838 dt_seterr (uptr, DTB_SEL); /* error */\r
839 return TRUE;\r
840 }\r
841return FALSE;\r
842}\r
843\r
844/* Unit service\r
845\r
846 Unit must be attached, detach cancels operation\r
847*/\r
848\r
849t_stat dt_svc (UNIT *uptr)\r
850{\r
851int32 mot = DTS_GETMOT (uptr->STATE);\r
852int32 dir = mot & DTS_DIR;\r
853int32 fnc = DTS_GETFNC (uptr->STATE);\r
854int32 *fbuf = (int32 *) uptr->filebuf;\r
855int32 unum = uptr - dt_dev.units;\r
856int32 blk, wrd, ma, relpos;\r
857uint32 ba;\r
858\r
859/* Motion cases\r
860\r
861 Decelerating - if next state != stopped, must be accel reverse\r
862 Accelerating - next state must be @speed, schedule function\r
863 At speed - do functional processing\r
864*/\r
865\r
866switch (mot) {\r
867\r
868 case DTS_DECF: case DTS_DECR: /* decelerating */\r
869 if (dt_setpos (uptr)) /* upd pos; off reel? */\r
870 return IORETURN (dt_stopoffr, STOP_DTOFF);\r
871 uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */\r
872 if (uptr->STATE) /* not stopped? */\r
873 sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */\r
874 return SCPE_OK;\r
875\r
876 case DTS_ACCF: case DTS_ACCR: /* accelerating */\r
877 dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */\r
878 return SCPE_OK;\r
879\r
880 case DTS_ATSF: case DTS_ATSR: /* at speed */\r
881 break; /* check function */\r
882\r
883 default: /* other */\r
884 dt_seterr (uptr, DTB_SEL); /* state error */\r
885 return SCPE_OK;\r
886 }\r
887\r
888/* Functional cases\r
889\r
890 Move - must be at end zone\r
891 Search - transfer block number, schedule next block\r
892 Off reel - detach unit (it must be deselected)\r
893*/\r
894\r
895if (dt_setpos (uptr)) /* upd pos; off reel? */\r
896 return IORETURN (dt_stopoffr, STOP_DTOFF);\r
897if (DT_QEZ (uptr)) { /* in end zone? */\r
898 dt_seterr (uptr, DTB_END); /* end zone error */\r
899 return SCPE_OK;\r
900 }\r
901blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */\r
902\r
903switch (fnc) { /* at speed, check fnc */\r
904\r
905 case FNC_MOVE: /* move */\r
906 dt_seterr (uptr, DTB_END); /* end zone error */\r
907 return SCPE_OK;\r
908\r
909 case DTS_OFR: /* off reel */\r
910 detach_unit (uptr); /* must be deselected */\r
911 uptr->STATE = uptr->pos = 0; /* no visible action */\r
912 break;\r
913\r
914/* TC02/TC15 service */\r
915/* Search */\r
916\r
917#if defined (TC02) /* TC02/TC15 */\r
918\r
919 case FNC_SRCH: /* search */\r
920 if (dtsb & DTB_DTF) { /* DTF set? */\r
921 dt_seterr (uptr, DTB_TIM); /* timing error */\r
922 return SCPE_OK;\r
923 }\r
924 sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */\r
925 M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* inc WC */\r
926 ma = M[DT_CA] & AMASK; /* get mem addr */\r
927 if (MEM_ADDR_OK (ma)) M[ma] = blk; /* store block # */\r
928 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))\r
929 dtsb = dtsb | DTB_DTF; /* set DTF */\r
930 if (DEBUG_PRI (dt_dev, LOG_MS))\r
931 fprintf (sim_deb, ">>DT%d: found block %d\n", unum, blk);\r
932 break;\r
933\r
934/* Read has four subcases\r
935\r
936 Start of block, not wc ovf - check that DTF is clear, otherwise normal\r
937 Normal - increment MA, WC, copy word from tape to memory\r
938 if read dir != write dir, bits must be scrambled\r
939 if wc overflow, next state is wc overflow\r
940 if end of block, possibly set DTF, next state is start of block\r
941 Wc ovf, not start of block - \r
942 if end of block, possibly set DTF, next state is start of block\r
943 Wc ovf, start of block - if end of block reached, timing error,\r
944 otherwise, continue to next word\r
945*/\r
946\r
947 case FNC_READ: /* read */\r
948 wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */\r
949 switch (dt_substate) { /* case on substate */\r
950\r
951 case DTO_SOB: /* start of block */\r
952 if (dtsb & DTB_DTF) { /* DTF set? */\r
953 dt_seterr (uptr, DTB_TIM); /* timing error */\r
954 return SCPE_OK;\r
955 }\r
956 if (DEBUG_PRI (dt_dev, LOG_RW) ||\r
957 (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))\r
958 fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n",\r
959 unum, blk, (dir? "backward": "forward"),\r
960 ((dtsa & DTA_MODE)? " continuous": " "));\r
961 dt_substate = 0; /* fall through */\r
962 case 0: /* normal read */\r
963 M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */\r
964 M[DT_CA] = (M[DT_CA] + 1) & DMASK;\r
965 ma = M[DT_CA] & AMASK; /* mem addr */\r
966 ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */\r
967 dtdb = fbuf[ba]; /* get tape word */\r
968 if (dir) dtdb = dt_comobv (dtdb); /* rev? comp obv */\r
969 if (MEM_ADDR_OK (ma)) M[ma] = dtdb; /* mem addr legal? */\r
970 if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */\r
971 case DTO_WCO: /* wc ovf, not sob */\r
972 if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */\r
973 sim_activate (uptr, DT_WSIZE * dt_ltime);\r
974 else {\r
975 dt_substate = dt_substate | DTO_SOB;\r
976 sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);\r
977 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))\r
978 dtsb = dtsb | DTB_DTF; /* set DTF */\r
979 }\r
980 break; \r
981\r
982 case DTO_WCO | DTO_SOB: /* next block */ \r
983 if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */\r
984 dt_seterr (uptr, DTB_TIM); /* timing error */\r
985 else sim_activate (uptr, DT_WSIZE * dt_ltime);\r
986 break;\r
987 } /* end case subst */\r
988 break;\r
989\r
990/* Write has four subcases\r
991\r
992 Start of block, not wc ovf - check that DTF is clear, set block direction\r
993 Normal - increment MA, WC, copy word from memory to tape\r
994 if wc overflow, next state is wc overflow\r
995 if end of block, possibly set DTF, next state is start of block\r
996 Wc ovf, not start of block -\r
997 copy 0 to tape\r
998 if end of block, possibly set DTF, next state is start of block\r
999 Wc ovf, start of block - schedule end zone\r
1000*/\r
1001\r
1002 case FNC_WRIT: /* write */\r
1003 wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */\r
1004 switch (dt_substate) { /* case on substate */\r
1005\r
1006 case DTO_SOB: /* start block */\r
1007 if (dtsb & DTB_DTF) { /* DTF set? */\r
1008 dt_seterr (uptr, DTB_TIM); /* timing error */\r
1009 return SCPE_OK;\r
1010 }\r
1011 if (DEBUG_PRI (dt_dev, LOG_RW) ||\r
1012 (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))\r
1013 fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk,\r
1014 (dir? "backward": "forward"),\r
1015 ((dtsa & DTA_MODE)? " continuous": " "));\r
1016 dt_substate = 0; /* fall through */\r
1017 case 0: /* normal write */\r
1018 M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */\r
1019 M[DT_CA] = (M[DT_CA] + 1) & DMASK;\r
1020 case DTO_WCO: /* wc ovflo */\r
1021 ma = M[DT_CA] & AMASK; /* mem addr */\r
1022 ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */\r
1023 dtdb = dt_substate? 0: M[ma]; /* get word */\r
1024 if (dir) dtdb = dt_comobv (dtdb); /* rev? comp obv */\r
1025 fbuf[ba] = dtdb; /* write word */\r
1026 if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;\r
1027 if (M[DT_WC] == 0) dt_substate = DTO_WCO;\r
1028 if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */\r
1029 sim_activate (uptr, DT_WSIZE * dt_ltime);\r
1030 else {\r
1031 dt_substate = dt_substate | DTO_SOB;\r
1032 sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);\r
1033 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))\r
1034 dtsb = dtsb | DTB_DTF; /* set DTF */\r
1035 }\r
1036 break; \r
1037\r
1038 case DTO_WCO | DTO_SOB: /* all done */\r
1039 dt_schedez (uptr, dir); /* sched end zone */\r
1040 break;\r
1041 } /* end case subst */\r
1042 break;\r
1043\r
1044/* Read all has two subcases\r
1045\r
1046 Not word count overflow - increment MA, WC, copy word from tape to memory\r
1047 Word count overflow - schedule end zone\r
1048*/\r
1049\r
1050 case FNC_RALL: /* read all */\r
1051 switch (dt_substate) { /* case on substate */\r
1052\r
1053 case 0: case DTO_SOB: /* read in progress */\r
1054 if (dtsb & DTB_DTF) { /* DTF set? */\r
1055 dt_seterr (uptr, DTB_TIM); /* timing error */\r
1056 return SCPE_OK;\r
1057 }\r
1058 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r
1059 M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */\r
1060 M[DT_CA] = (M[DT_CA] + 1) & DMASK;\r
1061 ma = M[DT_CA] & AMASK; /* mem addr */\r
1062 if ((relpos >= DT_HTLIN) && /* in data zone? */\r
1063 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r
1064 wrd = DT_LIN2WD (uptr->pos, uptr);\r
1065 ba = (blk * DTU_BSIZE (uptr)) + wrd;\r
1066 dtdb = fbuf[ba]; /* get tape word */\r
1067 }\r
1068 else dtdb = dt_gethdr (uptr, blk, relpos); /* get hdr */\r
1069 if (dir) dtdb = dt_comobv (dtdb); /* rev? comp obv */\r
1070 sim_activate (uptr, DT_WSIZE * dt_ltime);\r
1071 if (MEM_ADDR_OK (ma)) M[ma] = dtdb; /* mem addr legal? */\r
1072 if (M[DT_WC] == 0) dt_substate = DTO_WCO;\r
1073 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))\r
1074 dtsb = dtsb | DTB_DTF; /* set DTF */\r
1075 break;\r
1076\r
1077 case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */\r
1078 dt_schedez (uptr, dir); /* sched end zone */\r
1079 break;\r
1080 } /* end case substate */\r
1081 break;\r
1082\r
1083/* Write all has two subcases\r
1084\r
1085 Not word count overflow - increment MA, WC, copy word from memory to tape\r
1086 Word count overflow - schedule end zone\r
1087*/\r
1088\r
1089 case FNC_WALL: /* write all */\r
1090 switch (dt_substate) { /* case on substate */\r
1091\r
1092 case 0: case DTO_SOB: /* read in progress */\r
1093 if (dtsb & DTB_DTF) { /* DTF set? */\r
1094 dt_seterr (uptr, DTB_TIM); /* timing error */\r
1095 return SCPE_OK;\r
1096 }\r
1097 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r
1098 M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */\r
1099 M[DT_CA] = (M[DT_CA] + 1) & DMASK;\r
1100 ma = M[DT_CA] & AMASK; /* mem addr */\r
1101 if ((relpos >= DT_HTLIN) && /* in data zone? */\r
1102 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r
1103 dtdb = M[ma]; /* get mem word */\r
1104 if (dir) dtdb = dt_comobv (dtdb);\r
1105 wrd = DT_LIN2WD (uptr->pos, uptr);\r
1106 ba = (blk * DTU_BSIZE (uptr)) + wrd;\r
1107 fbuf[ba] = dtdb; /* write word */\r
1108 if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;\r
1109 }\r
1110/* /* ignore hdr */\r
1111 sim_activate (uptr, DT_WSIZE * dt_ltime);\r
1112 if (M[DT_WC] == 0) dt_substate = DTO_WCO;\r
1113 if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0))\r
1114 dtsb = dtsb | DTB_DTF; /* set DTF */\r
1115 break;\r
1116\r
1117 case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */\r
1118 dt_schedez (uptr, dir); /* sched end zone */\r
1119 break;\r
1120 } /* end case substate */\r
1121 break;\r
1122\r
1123/* Type 550 service */\r
1124/* Search */\r
1125\r
1126#else /* Type 550 */\r
1127 case FNC_SRCH: /* search */\r
1128 if (dtsb & DTB_DTF) { /* DTF set? */\r
1129 dt_seterr (uptr, DTB_TIM); /* timing error */\r
1130 return SCPE_OK;\r
1131 }\r
1132 sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */\r
1133 dtdb = blk; /* store block # */\r
1134 dtsb = dtsb | DTB_DTF; /* set DTF */\r
1135 if (DEBUG_PRI (dt_dev, LOG_MS))\r
1136 fprintf (sim_deb, ">>DT%d: search found block %d\n", unum, blk);\r
1137 break;\r
1138\r
1139/* Read and read all */\r
1140\r
1141 case FNC_READ: case FNC_RALL:\r
1142 if (dtsb & DTB_DTF) { /* DTF set? */\r
1143 dt_seterr (uptr, DTB_TIM); /* timing error */\r
1144 return SCPE_OK;\r
1145 }\r
1146 sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */\r
1147 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r
1148 if ((relpos >= DT_HTLIN) && /* in data zone? */\r
1149 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r
1150 wrd = DT_LIN2WD (uptr->pos, uptr);\r
1151 ba = (blk * DTU_BSIZE (uptr)) + wrd;\r
1152 dtdb = fbuf[ba]; /* get tape word */\r
1153 dtsb = dtsb | DTB_DTF; /* set flag */\r
1154 }\r
1155 else {\r
1156 ma = (2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1;\r
1157 wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */\r
1158#if defined (OLD_TYPE550)\r
1159 if ((wrd == 0) || /* skip 1st, last */\r
1160 (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break;\r
1161#endif\r
1162 if ((fnc == FNC_READ) && /* read, skip if not */\r
1163 (wrd != DT_CSMWD) && /* fwd, rev cksum */\r
1164 (wrd != ma)) break;\r
1165 dtdb = dt_gethdr (uptr, blk, relpos);\r
1166 if (wrd == (dir? DT_CSMWD: ma)) /* at end csum? */\r
1167 dtsb = dtsb | DTB_BEF; /* end block */\r
1168 else dtsb = dtsb | DTB_DTF; /* else next word */\r
1169 }\r
1170 if (dir) dtdb = dt_comobv (dtdb);\r
1171 break;\r
1172\r
1173/* Write and write all */\r
1174\r
1175 case FNC_WRIT: case FNC_WALL:\r
1176 if (dtsb & DTB_DTF) { /* DTF set? */\r
1177 dt_seterr (uptr, DTB_TIM); /* timing error */\r
1178 return SCPE_OK;\r
1179 }\r
1180 sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */\r
1181 relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r
1182 if ((relpos >= DT_HTLIN) && /* in data zone? */\r
1183 (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r
1184 wrd = DT_LIN2WD (uptr->pos, uptr);\r
1185 ba = (blk * DTU_BSIZE (uptr)) + wrd;\r
1186 if (dir) fbuf[ba] = dt_comobv (dtdb); /* get data word */\r
1187 else fbuf[ba] = dtdb;\r
1188 if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;\r
1189 if (wrd == (dir? 0: DTU_BSIZE (uptr) - 1))\r
1190 dtsb = dtsb | DTB_BEF; /* end block */\r
1191 else dtsb = dtsb | DTB_DTF; /* else next word */\r
1192 }\r
1193 else {\r
1194 wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */\r
1195#if defined (OLD_TYPE550)\r
1196 if ((wrd == 0) || /* skip 1st, last */\r
1197 (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break;\r
1198#endif\r
1199 if ((fnc == FNC_WRIT) && /* wr, skip if !csm */\r
1200 (wrd != ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)))\r
1201 break;\r
1202 dtsb = dtsb | DTB_DTF; /* set flag */\r
1203 }\r
1204 break;\r
1205#endif\r
1206\r
1207 default:\r
1208 dt_seterr (uptr, DTB_SEL); /* impossible state */\r
1209 break;\r
1210 } /* end case function */\r
1211\r
1212DT_UPDINT; /* update interrupts */\r
1213return SCPE_OK;\r
1214}\r
1215\r
1216/* Utility routines */\r
1217\r
1218/* Set error flag */\r
1219\r
1220void dt_seterr (UNIT *uptr, int32 e)\r
1221{\r
1222int32 mot = DTS_GETMOT (uptr->STATE);\r
1223\r
1224dtsa = dtsa & ~DTA_STSTP; /* clear go */\r
1225dtsb = dtsb | DTB_ERF | e; /* set error flag */\r
1226if (mot >= DTS_ACCF) { /* ~stopped or stopping? */\r
1227 sim_cancel (uptr); /* cancel activity */\r
1228 if (dt_setpos (uptr)) return; /* update position */\r
1229 sim_activate (uptr, dt_dctime); /* sched decel */\r
1230 DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */\r
1231 }\r
1232DT_UPDINT;\r
1233return;\r
1234}\r
1235\r
1236/* Schedule end zone */\r
1237\r
1238void dt_schedez (UNIT *uptr, int32 dir)\r
1239{\r
1240int32 newpos;\r
1241\r
1242if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */\r
1243else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */\r
1244sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);\r
1245return;\r
1246}\r
1247\r
1248/* Complement obverse routine */\r
1249\r
1250int32 dt_comobv (int32 dat)\r
1251{\r
1252dat = dat ^ DMASK; /* compl obverse */\r
1253dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) |\r
1254 ((dat >> 3) & 0700) | ((dat & 0700) << 3) |\r
1255 ((dat & 070) << 9) | ((dat & 07) << 15);\r
1256return dat;\r
1257}\r
1258\r
1259/* Checksum routine */\r
1260\r
1261int32 dt_csum (UNIT *uptr, int32 blk)\r
1262{\r
1263int32 *fbuf = (int32 *) uptr->filebuf;\r
1264int32 ba = blk * DTU_BSIZE (uptr);\r
1265int32 i, csum, wrd;\r
1266\r
1267#if defined (TC02) /* TC02/TC15 */\r
1268csum = 077; /* init csum */\r
1269for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */\r
1270 wrd = fbuf[ba + i] ^ DMASK; /* get ~word */\r
1271 csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd;\r
1272 }\r
1273return (csum & 077);\r
1274#else /* Type 550 */\r
1275csum = 0777777;\r
1276for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */\r
1277 wrd = fbuf[ba + i]; /* get word */\r
1278 csum = csum + wrd; /* 1's comp add */\r
1279 if (csum > DMASK) csum = (csum + 1) & DMASK;\r
1280 }\r
1281return (csum ^ DMASK); /* 1's comp res */\r
1282#endif\r
1283}\r
1284\r
1285/* Get header word */\r
1286\r
1287int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos)\r
1288{\r
1289int32 wrd = relpos / DT_WSIZE;\r
1290\r
1291if (wrd == DT_BLKWD) return blk; /* fwd blknum */\r
1292#if defined (TC02) /* TC02/TC15 */\r
1293if (wrd == DT_CSMWD) return 077; /* rev csum */\r
1294if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */\r
1295 return (dt_csum (uptr, blk) << 12);\r
1296#else /* Type 550 */\r
1297if (wrd == DT_CSMWD) return 0777777; /* rev csum */\r
1298if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */\r
1299 return (dt_csum (uptr, blk));\r
1300#endif\r
1301if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */\r
1302 return dt_comobv (blk);\r
1303return 0; /* all others */\r
1304} \r
1305\r
1306/* Reset routine */\r
1307\r
1308t_stat dt_reset (DEVICE *dptr)\r
1309{\r
1310int32 i, prev_mot;\r
1311UNIT *uptr;\r
1312\r
1313for (i = 0; i < DT_NUMDR; i++) { /* stop all drives */\r
1314 uptr = dt_dev.units + i;\r
1315 if (sim_is_running) { /* CAF? */\r
1316 prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */\r
1317 if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */\r
1318 if (dt_setpos (uptr)) continue; /* update pos */\r
1319 sim_cancel (uptr);\r
1320 sim_activate (uptr, dt_dctime); /* sched decel */\r
1321 DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0);\r
1322 }\r
1323 }\r
1324 else {\r
1325 sim_cancel (uptr); /* sim reset */\r
1326 uptr->STATE = 0; \r
1327 uptr->LASTT = sim_grtime ();\r
1328 }\r
1329 }\r
1330dtsa = dtsb = 0; /* clear status */\r
1331DT_UPDINT; /* reset interrupt */\r
1332return SCPE_OK;\r
1333}\r
1334\r
1335/* IORS routine */\r
1336\r
1337int32 dt_iors (void)\r
1338{\r
1339#if defined IOS_DTA\r
1340return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0);\r
1341#else\r
1342return 0;\r
1343#endif\r
1344}\r
1345\r
1346/* Attach routine\r
1347\r
1348 Determine 12b, 16b, or 18b/36b format\r
1349 Allocate buffer\r
1350 If 12b, read 12b format and convert to 18b in buffer\r
1351 If 16b, read 16b format and convert to 18b in buffer\r
1352 If 18b/36b, read data into buffer\r
1353*/\r
1354\r
1355t_stat dt_attach (UNIT *uptr, char *cptr)\r
1356{\r
1357uint16 pdp8b[D8_NBSIZE];\r
1358uint16 pdp11b[D18_BSIZE];\r
1359uint32 ba, sz, k, *fbuf;\r
1360int32 u = uptr - dt_dev.units;\r
1361t_stat r;\r
1362\r
1363r = attach_unit (uptr, cptr); /* attach */\r
1364if (r != SCPE_OK) return r; /* error? */\r
1365if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */\r
1366 uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */\r
1367 if (sim_switches & SWMASK ('T')) /* att 12b? */\r
1368 uptr->flags = uptr->flags | UNIT_8FMT;\r
1369 else if (sim_switches & SWMASK ('S')) /* att 16b? */\r
1370 uptr->flags = uptr->flags | UNIT_11FMT;\r
1371 else if (!(sim_switches & SWMASK ('A')) && /* autosize? */\r
1372 (sz = sim_fsize (uptr->fileref))) {\r
1373 if (sz == D8_FILSIZ)\r
1374 uptr->flags = uptr->flags | UNIT_8FMT;\r
1375 else if (sz == D11_FILSIZ)\r
1376 uptr->flags = uptr->flags | UNIT_11FMT;\r
1377 }\r
1378 }\r
1379uptr->capac = DTU_CAPAC (uptr); /* set capacity */\r
1380uptr->filebuf = calloc (uptr->capac, sizeof (uint32));\r
1381if (uptr->filebuf == NULL) { /* can't alloc? */\r
1382 detach_unit (uptr);\r
1383 return SCPE_MEM;\r
1384 }\r
1385fbuf = (uint32 *) uptr->filebuf; /* file buffer */\r
1386printf ("%s%d: ", sim_dname (&dt_dev), u);\r
1387if (uptr->flags & UNIT_8FMT) printf ("12b format");\r
1388else if (uptr->flags & UNIT_11FMT) printf ("16b format");\r
1389else printf ("18b/36b format");\r
1390printf (", buffering file in memory\n");\r
1391if (uptr->flags & UNIT_8FMT) { /* 12b? */\r
1392 for (ba = 0; ba < uptr->capac; ) { /* loop thru file */\r
1393 k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref);\r
1394 if (k == 0) break;\r
1395 for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0;\r
1396 for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */\r
1397 fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) |\r
1398 ((uint32) (pdp8b[k + 1] >> 6) & 077);\r
1399 fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) |\r
1400 ((uint32) pdp8b[k + 2] & 07777);\r
1401 ba = ba + 2; /* end blk loop */\r
1402 }\r
1403 } /* end file loop */\r
1404 uptr->hwmark = ba;\r
1405 } /* end if */\r
1406else if (uptr->flags & UNIT_11FMT) { /* 16b? */\r
1407 for (ba = 0; ba < uptr->capac; ) { /* loop thru file */\r
1408 k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref);\r
1409 if (k == 0) break;\r
1410 for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0;\r
1411 for (k = 0; k < D18_BSIZE; k++)\r
1412 fbuf[ba++] = pdp11b[k];\r
1413 }\r
1414 uptr->hwmark = ba;\r
1415 } /* end elif */\r
1416else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32),\r
1417 uptr->capac, uptr->fileref);\r
1418uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */\r
1419uptr->pos = DT_EZLIN; /* beyond leader */\r
1420uptr->LASTT = sim_grtime (); /* last pos update */\r
1421return SCPE_OK;\r
1422}\r
1423\r
1424/* Detach routine\r
1425\r
1426 Cancel in progress operation\r
1427 If 12b, convert 18b buffer to 12b and write to file\r
1428 If 16b, convert 18b buffer to 16b and write to file\r
1429 If 18b/36b, write buffer to file\r
1430 Deallocate buffer\r
1431*/\r
1432\r
1433t_stat dt_detach (UNIT* uptr)\r
1434{\r
1435uint16 pdp8b[D8_NBSIZE];\r
1436uint16 pdp11b[D18_BSIZE];\r
1437uint32 ba, k, *fbuf;\r
1438int32 u = uptr - dt_dev.units;\r
1439\r
1440if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r
1441if (sim_is_active (uptr)) {\r
1442 sim_cancel (uptr);\r
1443 if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) {\r
1444 dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF;\r
1445 DT_UPDINT;\r
1446 }\r
1447 uptr->STATE = uptr->pos = 0;\r
1448 }\r
1449fbuf = (uint32 *) uptr->filebuf; /* file buffer */\r
1450if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */\r
1451 printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u);\r
1452 rewind (uptr->fileref); /* start of file */\r
1453 if (uptr->flags & UNIT_8FMT) { /* 12b? */\r
1454 for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */\r
1455 for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */\r
1456 pdp8b[k] = (fbuf[ba] >> 6) & 07777;\r
1457 pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) |\r
1458 ((fbuf[ba + 1] >> 12) & 077);\r
1459 pdp8b[k + 2] = fbuf[ba + 1] & 07777;\r
1460 ba = ba + 2;\r
1461 } /* end loop blk */\r
1462 fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref);\r
1463 if (ferror (uptr->fileref)) break;\r
1464 } /* end loop file */\r
1465 } /* end if 12b */\r
1466 else if (uptr->flags & UNIT_11FMT) { /* 16b? */\r
1467 for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */\r
1468 for (k = 0; k < D18_BSIZE; k++) /* loop blk */\r
1469 pdp11b[k] = fbuf[ba++] & 0177777;\r
1470 fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref);\r
1471 if (ferror (uptr->fileref)) break;\r
1472 } /* end loop file */\r
1473 } /* end if 16b */\r
1474 else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */\r
1475 uptr->hwmark, uptr->fileref);\r
1476 if (ferror (uptr->fileref)) perror ("I/O error");\r
1477 } /* end if hwmark */\r
1478free (uptr->filebuf); /* release buf */\r
1479uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */\r
1480uptr->filebuf = NULL; /* clear buf ptr */\r
1481uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */\r
1482uptr->capac = DT_CAPAC; /* default size */\r
1483return detach_unit (uptr);\r
1484}\r