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