Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) 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 | This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones'\r | |
27 | PDP8 simulator but tracks the hardware implementation more closely.\r | |
28 | \r | |
29 | td TD8E/TU56 DECtape\r | |
30 | \r | |
31 | 23-Jun-06 RMS Fixed switch conflict in ATTACH\r | |
32 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r | |
33 | 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR\r | |
34 | \r | |
35 | PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words.\r | |
36 | Three file formats are supported:\r | |
37 | \r | |
38 | 18b/36b 256 words per block [256 x 18b]\r | |
39 | 16b 256 words per block [256 x 16b]\r | |
40 | 12b 129 words per block [129 x 12b]\r | |
41 | \r | |
42 | When a 16b or 18/36b DECtape file is read in, it is converted to 12b format.\r | |
43 | \r | |
44 | DECtape motion is measured in 3b lines. Time between lines is 33.33us.\r | |
45 | Tape density is nominally 300 lines per inch. The format of a DECtape (as\r | |
46 | taken from the TD8E formatter) is:\r | |
47 | \r | |
48 | reverse end zone 8192 reverse end zone codes ~ 10 feet\r | |
49 | reverse buffer 200 interblock codes\r | |
50 | block 0\r | |
51 | :\r | |
52 | block n\r | |
53 | forward buffer 200 interblock codes\r | |
54 | forward end zone 8192 forward end zone codes ~ 10 feet\r | |
55 | \r | |
56 | A block consists of five 18b header words, a tape-specific number of data\r | |
57 | words, and five 18b trailer words. All systems except the PDP-8 use a\r | |
58 | standard block length of 256 words; the PDP-8 uses a standard block length\r | |
59 | of 86 words (x 18b = 129 words x 12b).\r | |
60 | \r | |
61 | Because a DECtape file only contains data, the simulator cannot support\r | |
62 | write timing and mark track and can only do a limited implementation\r | |
63 | of non-data words. Read assumes that the tape has been conventionally\r | |
64 | written forward:\r | |
65 | \r | |
66 | header word 0 0\r | |
67 | header word 1 block number (for forward reads)\r | |
68 | header words 2,3 0\r | |
69 | header word 4 checksum (for reverse reads)\r | |
70 | :\r | |
71 | trailer word 4 checksum (for forward reads)\r | |
72 | trailer words 3,2 0\r | |
73 | trailer word 1 block number (for reverse reads)\r | |
74 | trailer word 0 0\r | |
75 | \r | |
76 | Write modifies only the data words and dumps the non-data words in the\r | |
77 | bit bucket.\r | |
78 | */\r | |
79 | \r | |
80 | #include "pdp8_defs.h"\r | |
81 | \r | |
82 | #define DT_NUMDR 2 /* #drives */\r | |
83 | #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */\r | |
84 | #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */\r | |
85 | #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */\r | |
86 | #define UNIT_WLK (1 << UNIT_V_WLK)\r | |
87 | #define UNIT_8FMT (1 << UNIT_V_8FMT)\r | |
88 | #define UNIT_11FMT (1 << UNIT_V_11FMT)\r | |
89 | #define STATE u3 /* unit state */\r | |
90 | #define LASTT u4 /* last time update */\r | |
91 | #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */\r | |
92 | \r | |
93 | /* System independent DECtape constants */\r | |
94 | \r | |
95 | #define DT_LPERMC 6 /* lines per mark track */\r | |
96 | #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */\r | |
97 | #define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */\r | |
98 | #define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */\r | |
99 | \r | |
100 | /* 16b, 18b, 36b DECtape constants */\r | |
101 | \r | |
102 | #define D18_WSIZE 6 /* word sizein lines */\r | |
103 | #define D18_BSIZE 384 /* block size in 12b */\r | |
104 | #define D18_TSIZE 578 /* tape size */\r | |
105 | #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)\r | |
106 | #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))\r | |
107 | #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */\r | |
108 | \r | |
109 | #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)\r | |
110 | #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32))\r | |
111 | #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16))\r | |
112 | \r | |
113 | /* 12b DECtape constants */\r | |
114 | \r | |
115 | #define D8_WSIZE 4 /* word size in lines */\r | |
116 | #define D8_BSIZE 129 /* block size in 12b */\r | |
117 | #define D8_TSIZE 1474 /* tape size */\r | |
118 | #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)\r | |
119 | #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))\r | |
120 | #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */\r | |
121 | #define D8_FILSIZ (D8_CAPAC * sizeof (int16))\r | |
122 | \r | |
123 | /* This controller */\r | |
124 | \r | |
125 | #define DT_CAPAC D8_CAPAC /* default */\r | |
126 | #define DT_WSIZE D8_WSIZE\r | |
127 | \r | |
128 | /* Calculated constants, per unit */\r | |
129 | \r | |
130 | #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)\r | |
131 | #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)\r | |
132 | #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)\r | |
133 | #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)\r | |
134 | #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)\r | |
135 | \r | |
136 | #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))\r | |
137 | #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))\r | |
138 | \r | |
139 | /* Command register */\r | |
140 | \r | |
141 | #define TDC_UNIT 04000 /* unit select */\r | |
142 | #define TDC_FWDRV 02000 /* fwd/rev */\r | |
143 | #define TDC_STPGO 01000 /* stop/go */\r | |
144 | #define TDC_RW 00400 /* read/write */\r | |
145 | #define TDC_MASK 07400 /* implemented */\r | |
146 | #define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0)\r | |
147 | \r | |
148 | /* Status register */\r | |
149 | \r | |
150 | #define TDS_WLO 00200 /* write lock */\r | |
151 | #define TDS_TME 00100 /* timing/sel err */\r | |
152 | \r | |
153 | /* Mark track register and codes */\r | |
154 | \r | |
155 | #define MTK_MASK 077\r | |
156 | #define MTK_REV_END 055 /* rev end zone */\r | |
157 | #define MTK_INTER 025 /* interblock */\r | |
158 | #define MTK_FWD_BLK 026 /* fwd block */\r | |
159 | #define MTK_REV_GRD 032 /* reverse guard */\r | |
160 | #define MTK_FWD_PRE 010 /* lock, etc */\r | |
161 | #define MTK_DATA 070 /* data */\r | |
162 | #define MTK_REV_PRE 073 /* lock, etc */\r | |
163 | #define MTK_FWD_GRD 051 /* fwd guard */\r | |
164 | #define MTK_REV_BLK 045 /* rev block */\r | |
165 | #define MTK_FWD_END 022 /* fwd end zone */\r | |
166 | \r | |
167 | /* DECtape state */\r | |
168 | \r | |
169 | #define STA_STOP 0 /* stopped */\r | |
170 | #define STA_DEC 2 /* decelerating */\r | |
171 | #define STA_ACC 4 /* accelerating */\r | |
172 | #define STA_UTS 6 /* up to speed */\r | |
173 | #define STA_DIR 1 /* fwd/rev */\r | |
174 | \r | |
175 | #define ABS(x) (((x) < 0)? (-(x)): (x))\r | |
176 | #define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1)\r | |
177 | \r | |
178 | /* State and declarations */\r | |
179 | \r | |
180 | int32 td_cmd = 0; /* command */\r | |
181 | int32 td_dat = 0; /* data */\r | |
182 | int32 td_mtk = 0; /* mark track */\r | |
183 | int32 td_slf = 0; /* single line flag */\r | |
184 | int32 td_qlf = 0; /* quad line flag */\r | |
185 | int32 td_tme = 0; /* timing error flag */\r | |
186 | int32 td_csum = 0; /* save check sum */\r | |
187 | int32 td_qlctr = 0; /* quad line ctr */\r | |
188 | int32 td_ltime = 20; /* interline time */\r | |
189 | int32 td_dctime = 40000; /* decel time */\r | |
190 | int32 td_stopoffr = 0;\r | |
191 | static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */\r | |
192 | \r | |
193 | DEVICE td_dev;\r | |
194 | int32 td77 (int32 IR, int32 AC);\r | |
195 | t_stat td_svc (UNIT *uptr);\r | |
196 | t_stat td_reset (DEVICE *dptr);\r | |
197 | t_stat td_attach (UNIT *uptr, char *cptr);\r | |
198 | t_stat td_detach (UNIT *uptr);\r | |
199 | t_stat td_boot (int32 unitno, DEVICE *dptr);\r | |
200 | t_bool td_newsa (int32 newf);\r | |
201 | t_bool td_setpos (UNIT *uptr);\r | |
202 | int32 td_header (UNIT *uptr, int32 blk, int32 line);\r | |
203 | int32 td_trailer (UNIT *uptr, int32 blk, int32 line);\r | |
204 | int32 td_read (UNIT *uptr, int32 blk, int32 line);\r | |
205 | void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb);\r | |
206 | int32 td_set_mtk (int32 code, int32 u, int32 k);\r | |
207 | t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
208 | \r | |
209 | extern uint16 M[];\r | |
210 | extern int32 sim_switches;\r | |
211 | extern int32 sim_is_running;\r | |
212 | \r | |
213 | /* TD data structures\r | |
214 | \r | |
215 | td_dev DT device descriptor\r | |
216 | td_unit DT unit list\r | |
217 | td_reg DT register list\r | |
218 | td_mod DT modifier list\r | |
219 | */\r | |
220 | \r | |
221 | DIB td_dib = { DEV_TD8E, 1, { &td77 } };\r | |
222 | \r | |
223 | UNIT td_unit[] = {\r | |
224 | { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+\r | |
225 | UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },\r | |
226 | { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+\r | |
227 | UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }\r | |
228 | };\r | |
229 | \r | |
230 | REG td_reg[] = {\r | |
231 | { GRDATA (TDCMD, td_cmd, 8, 4, 8) },\r | |
232 | { ORDATA (TDDAT, td_dat, 12) },\r | |
233 | { ORDATA (TDMTK, td_mtk, 6) },\r | |
234 | { FLDATA (TDSLF, td_slf, 0) },\r | |
235 | { FLDATA (TDQLF, td_qlf, 0) },\r | |
236 | { FLDATA (TDTME, td_tme, 0) },\r | |
237 | { ORDATA (TDQL, td_qlctr, 2) },\r | |
238 | { ORDATA (TDCSUM, td_csum, 6), REG_RO },\r | |
239 | { DRDATA (LTIME, td_ltime, 31), REG_NZ | PV_LEFT },\r | |
240 | { DRDATA (DCTIME, td_dctime, 31), REG_NZ | PV_LEFT },\r | |
241 | { URDATA (POS, td_unit[0].pos, 10, T_ADDR_W, 0,\r | |
242 | DT_NUMDR, PV_LEFT | REG_RO) },\r | |
243 | { URDATA (STATT, td_unit[0].STATE, 8, 18, 0,\r | |
244 | DT_NUMDR, REG_RO) },\r | |
245 | { URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0,\r | |
246 | DT_NUMDR, REG_HRO) },\r | |
247 | { FLDATA (STOP_OFFR, td_stopoffr, 0) },\r | |
248 | { ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO },\r | |
249 | { NULL }\r | |
250 | };\r | |
251 | \r | |
252 | MTAB td_mod[] = {\r | |
253 | { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r | |
254 | { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, \r | |
255 | { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },\r | |
256 | { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },\r | |
257 | { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },\r | |
258 | { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",\r | |
259 | &set_dev, &show_dev, NULL },\r | |
260 | { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos },\r | |
261 | { 0 }\r | |
262 | };\r | |
263 | \r | |
264 | DEVICE td_dev = {\r | |
265 | "TD", td_unit, td_reg, td_mod,\r | |
266 | DT_NUMDR, 8, 24, 1, 8, 12,\r | |
267 | NULL, NULL, &td_reset,\r | |
268 | &td_boot, &td_attach, &td_detach,\r | |
269 | &td_dib, DEV_DISABLE | DEV_DIS\r | |
270 | };\r | |
271 | \r | |
272 | /* IOT routines */\r | |
273 | \r | |
274 | int32 td77 (int32 IR, int32 AC)\r | |
275 | {\r | |
276 | int32 pulse = IR & 07;\r | |
277 | int32 u = TDC_GETUNIT (td_cmd); /* get unit */\r | |
278 | int32 diff, t;\r | |
279 | \r | |
280 | switch (pulse) {\r | |
281 | \r | |
282 | case 01: /* SDSS */\r | |
283 | if (td_slf) return AC | IOT_SKP;\r | |
284 | break;\r | |
285 | \r | |
286 | case 02: /* SDST */\r | |
287 | if (td_tme) return AC | IOT_SKP;\r | |
288 | break;\r | |
289 | \r | |
290 | case 03: /* SDSQ */\r | |
291 | if (td_qlf) return AC | IOT_SKP;\r | |
292 | break;\r | |
293 | \r | |
294 | case 04: /* SDLC */\r | |
295 | td_tme = 0; /* clear tim err */\r | |
296 | diff = (td_cmd ^ AC) & TDC_MASK; /* cmd changes */\r | |
297 | td_cmd = AC & TDC_MASK; /* update cmd */\r | |
298 | if ((diff != 0) && (diff != TDC_RW)) { /* signif change? */\r | |
299 | if (td_newsa (td_cmd)) /* new command */\r | |
300 | return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON);\r | |
301 | }\r | |
302 | break;\r | |
303 | \r | |
304 | case 05: /* SDLD */\r | |
305 | td_slf = 0; /* clear flags */\r | |
306 | td_qlf = 0;\r | |
307 | td_qlctr = 0;\r | |
308 | td_dat = AC; /* load data reg */\r | |
309 | break;\r | |
310 | \r | |
311 | case 06: /* SDRC */\r | |
312 | td_slf = 0; /* clear flags */\r | |
313 | td_qlf = 0;\r | |
314 | td_qlctr = 0;\r | |
315 | t = td_cmd | td_mtk; /* form status */\r | |
316 | if (td_tme || !(td_unit[u].flags & UNIT_ATT)) /* tim/sel err? */\r | |
317 | t = t | TDS_TME;\r | |
318 | if (td_unit[u].flags & UNIT_WPRT) /* write locked? */\r | |
319 | t = t | TDS_WLO;\r | |
320 | return t; /* return status */\r | |
321 | \r | |
322 | case 07: /* SDRD */\r | |
323 | td_slf = 0; /* clear flags */\r | |
324 | td_qlf = 0;\r | |
325 | td_qlctr = 0;\r | |
326 | return td_dat; /* return data */\r | |
327 | }\r | |
328 | \r | |
329 | return AC;\r | |
330 | }\r | |
331 | \r | |
332 | /* Command register change (start/stop, forward/reverse, new unit)\r | |
333 | \r | |
334 | 1. If change in motion, stop to start\r | |
335 | - schedule up to speed\r | |
336 | - set function as next state\r | |
337 | 2. If change in motion, start to stop, or change in direction\r | |
338 | - schedule stop\r | |
339 | */\r | |
340 | \r | |
341 | t_bool td_newsa (int32 newf)\r | |
342 | {\r | |
343 | int32 prev_mving, new_mving, prev_dir, new_dir;\r | |
344 | UNIT *uptr;\r | |
345 | \r | |
346 | uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */\r | |
347 | if ((uptr->flags & UNIT_ATT) == 0) return FALSE; /* new unit attached? */\r | |
348 | \r | |
349 | new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */\r | |
350 | prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */\r | |
351 | new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */\r | |
352 | prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */\r | |
353 | \r | |
354 | td_mtk = 0; /* mark trk reg cleared */\r | |
355 | \r | |
356 | if (!prev_mving && !new_mving) return FALSE; /* stop from stop? */\r | |
357 | \r | |
358 | if (new_mving && !prev_mving) { /* start from stop? */\r | |
359 | if (td_setpos (uptr)) return TRUE; /* update pos */\r | |
360 | sim_cancel (uptr); /* stop current */\r | |
361 | sim_activate (uptr, td_dctime - (td_dctime >> 2)); /* sched accel */\r | |
362 | uptr->STATE = STA_ACC | new_dir; /* set status */\r | |
363 | td_slf = td_qlf = td_qlctr = 0; /* clear state */\r | |
364 | return FALSE;\r | |
365 | }\r | |
366 | \r | |
367 | if ((prev_mving && !new_mving) || /* stop from moving? */\r | |
368 | (prev_dir != new_dir)) { /* dir chg while moving? */\r | |
369 | if (uptr->STATE >= STA_ACC) { /* not stopping? */\r | |
370 | if (td_setpos (uptr)) return TRUE; /* update pos */\r | |
371 | sim_cancel (uptr); /* stop current */\r | |
372 | sim_activate (uptr, td_dctime); /* schedule decel */\r | |
373 | uptr->STATE = STA_DEC | prev_dir; /* set status */\r | |
374 | td_slf = td_qlf = td_qlctr = 0; /* clear state */\r | |
375 | }\r | |
376 | return FALSE;\r | |
377 | }\r | |
378 | \r | |
379 | return FALSE; \r | |
380 | }\r | |
381 | \r | |
382 | /* Update DECtape position\r | |
383 | \r | |
384 | DECtape motion is modeled as a constant velocity, with linear\r | |
385 | acceleration and deceleration. The motion equations are as follows:\r | |
386 | \r | |
387 | t = time since operation started\r | |
388 | tmax = time for operation (accel, decel only)\r | |
389 | v = at speed velocity in lines (= 1/td_ltime)\r | |
390 | \r | |
391 | Then:\r | |
392 | at speed dist = t * v\r | |
393 | accel dist = (t^2 * v) / (2 * tmax)\r | |
394 | decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)\r | |
395 | \r | |
396 | This routine uses the relative (integer) time, rather than the absolute\r | |
397 | (floating point) time, to allow save and restore of the start times.\r | |
398 | */\r | |
399 | \r | |
400 | t_bool td_setpos (UNIT *uptr)\r | |
401 | {\r | |
402 | uint32 new_time, ut, ulin, udelt;\r | |
403 | int32 delta;\r | |
404 | \r | |
405 | new_time = sim_grtime (); /* current time */\r | |
406 | ut = new_time - uptr->LASTT; /* elapsed time */\r | |
407 | if (ut == 0) return FALSE; /* no time gone? exit */\r | |
408 | uptr->LASTT = new_time; /* update last time */\r | |
409 | switch (uptr->STATE & ~STA_DIR) { /* case on motion */\r | |
410 | \r | |
411 | case STA_STOP: /* stop */\r | |
412 | delta = 0;\r | |
413 | break;\r | |
414 | \r | |
415 | case STA_DEC: /* slowing */\r | |
416 | ulin = ut / (uint32) td_ltime;\r | |
417 | udelt = td_dctime / td_ltime;\r | |
418 | delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);\r | |
419 | break;\r | |
420 | \r | |
421 | case STA_ACC: /* accelerating */\r | |
422 | ulin = ut / (uint32) td_ltime;\r | |
423 | udelt = (td_dctime - (td_dctime >> 2)) / td_ltime;\r | |
424 | delta = (ulin * ulin) / (2 * udelt);\r | |
425 | break;\r | |
426 | \r | |
427 | case STA_UTS: /* at speed */\r | |
428 | delta = ut / (uint32) td_ltime;\r | |
429 | break;\r | |
430 | }\r | |
431 | \r | |
432 | if (uptr->STATE & STA_DIR) uptr->pos = uptr->pos - delta; /* update pos */\r | |
433 | else uptr->pos = uptr->pos + delta;\r | |
434 | if (((int32) uptr->pos < 0) ||\r | |
435 | ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {\r | |
436 | detach_unit (uptr); /* off reel */\r | |
437 | sim_cancel (uptr); /* no timing pulses */\r | |
438 | return TRUE;\r | |
439 | }\r | |
440 | return FALSE;\r | |
441 | }\r | |
442 | \r | |
443 | /* Unit service - unit is either changing speed, or it is up to speed */\r | |
444 | \r | |
445 | t_stat td_svc (UNIT *uptr)\r | |
446 | {\r | |
447 | int32 mot = uptr->STATE & ~STA_DIR;\r | |
448 | int32 dir = uptr->STATE & STA_DIR;\r | |
449 | int32 unum = uptr - td_dev.units;\r | |
450 | int32 su = TDC_GETUNIT (td_cmd);\r | |
451 | int32 mtkb, datb;\r | |
452 | \r | |
453 | /* Motion cases\r | |
454 | \r | |
455 | Decelerating - if go, next state must be accel as specified by td_cmd\r | |
456 | Accelerating - next state must be up to speed, fall through\r | |
457 | Up to speed - process line */\r | |
458 | \r | |
459 | if (mot == STA_STOP) return SCPE_OK; /* stopped? done */\r | |
460 | if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */\r | |
461 | uptr->STATE = uptr->pos = 0; /* also done */\r | |
462 | return SCPE_UNATT;\r | |
463 | }\r | |
464 | \r | |
465 | switch (mot) { /* case on motion */\r | |
466 | \r | |
467 | case STA_DEC: /* deceleration */\r | |
468 | if (td_setpos (uptr)) /* upd pos; off reel? */\r | |
469 | return IORETURN (td_stopoffr, STOP_DTOFF);\r | |
470 | if ((unum != su) || !(td_cmd & TDC_STPGO)) /* not sel or stop? */\r | |
471 | uptr->STATE = 0; /* stop */\r | |
472 | else { /* selected and go */\r | |
473 | uptr->STATE = STA_ACC | /* accelerating */\r | |
474 | ((td_cmd & TDC_FWDRV)? STA_DIR: 0); /* in new dir */\r | |
475 | sim_activate (uptr, td_dctime - (td_dctime >> 2));\r | |
476 | }\r | |
477 | return SCPE_OK;\r | |
478 | \r | |
479 | case STA_ACC: /* accelerating */\r | |
480 | if (td_setpos (uptr)) /* upd pos; off reel? */\r | |
481 | return IORETURN (td_stopoffr, STOP_DTOFF);\r | |
482 | uptr->STATE = STA_UTS | dir; /* set up to speed */\r | |
483 | break;\r | |
484 | \r | |
485 | case STA_UTS: /* up to speed */\r | |
486 | if (dir) uptr->pos = uptr->pos - 1; /* adjust position */\r | |
487 | else uptr->pos = uptr->pos + 1;\r | |
488 | uptr->LASTT = sim_grtime (); /* save time */\r | |
489 | if (((int32) uptr->pos < 0) || /* off reel? */\r | |
490 | (uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) {\r | |
491 | detach_unit (uptr);\r | |
492 | return IORETURN (td_stopoffr, STOP_DTOFF);\r | |
493 | }\r | |
494 | break; /* check function */\r | |
495 | }\r | |
496 | \r | |
497 | /* At speed - process the current line\r | |
498 | \r | |
499 | Once the TD8E is running at speed, it operates line by line. If reading,\r | |
500 | the current mark track bit is shifted into the mark track register, and\r | |
501 | the current data nibble (3b) is shifted into the data register. If\r | |
502 | writing, the current mark track bit is shifted into the mark track\r | |
503 | register, the top nibble from the data register is written to tape, and\r | |
504 | the data register is shifted up. The complexity here comes from \r | |
505 | synthesizing the mark track, based on tape position, and the header data. */\r | |
506 | \r | |
507 | sim_activate (uptr, td_ltime); /* sched next line */\r | |
508 | if (unum != su) return SCPE_OK; /* not sel? done */\r | |
509 | td_slf = 1; /* set single */\r | |
510 | td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */\r | |
511 | if (td_qlctr == 0) { /* lines mod 4? */\r | |
512 | if (td_qlf) { /* quad line set? */\r | |
513 | td_tme = 1; /* timing error */\r | |
514 | td_cmd = td_cmd & ~TDC_RW; /* clear write */\r | |
515 | }\r | |
516 | else td_qlf = 1; /* no, set quad */\r | |
517 | }\r | |
518 | \r | |
519 | datb = 0; /* assume no data */\r | |
520 | if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */\r | |
521 | mtkb = MTK_BIT (MTK_REV_END, uptr->pos);\r | |
522 | else if (uptr->pos < DT_EZLIN) /* rev buffer? */\r | |
523 | mtkb = MTK_BIT (MTK_INTER, uptr->pos);\r | |
524 | else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */\r | |
525 | int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */\r | |
526 | int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */\r | |
527 | if (lineno < DT_HTLIN) { /* header? */\r | |
528 | if ((td_cmd & TDC_RW) == 0) /* read? */ \r | |
529 | datb = td_header (uptr, blkno, lineno); /* get nibble */\r | |
530 | }\r | |
531 | else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) { /* data? */\r | |
532 | if (td_cmd & TDC_RW) /* write? */\r | |
533 | td_write (uptr, blkno, /* write data nibble */\r | |
534 | lineno - DT_HTLIN, /* data rel line num */\r | |
535 | (td_dat >> 9) & 07);\r | |
536 | else datb = td_read (uptr, blkno, /* no, read */\r | |
537 | lineno - DT_HTLIN);\r | |
538 | }\r | |
539 | else if ((td_cmd & TDC_RW) == 0) /* trailer; read? */\r | |
540 | datb = td_trailer (uptr, blkno, lineno - /* get trlr nibble */\r | |
541 | (DTU_LPERB (uptr) - DT_HTLIN));\r | |
542 | mtkb = tdb_mtk[unum][lineno];\r | |
543 | }\r | |
544 | else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN))\r | |
545 | mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */\r | |
546 | else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */\r | |
547 | \r | |
548 | if (dir) { /* reverse? */\r | |
549 | mtkb = mtkb ^ 01; /* complement mark bit, */\r | |
550 | datb = datb ^ 07; /* data bits */\r | |
551 | }\r | |
552 | td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */\r | |
553 | td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */\r | |
554 | return SCPE_OK;\r | |
555 | }\r | |
556 | \r | |
557 | /* Header read - reads out 18b words in 3b increments\r | |
558 | \r | |
559 | word lines contents\r | |
560 | 0 0-5 0\r | |
561 | 1 6-11 block number\r | |
562 | 2 12-17 0\r | |
563 | 3 18-23 0\r | |
564 | 4 24-29 reverse checksum (0777777)\r | |
565 | */\r | |
566 | \r | |
567 | int32 td_header (UNIT *uptr, int32 blk, int32 line)\r | |
568 | {\r | |
569 | int32 nibp;\r | |
570 | \r | |
571 | switch (line) {\r | |
572 | \r | |
573 | case 8: case 9: case 10: case 11: /* block num */\r | |
574 | nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC));\r | |
575 | return (blk >> nibp) & 07;\r | |
576 | \r | |
577 | case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */\r | |
578 | return 07; /* 777777 */\r | |
579 | \r | |
580 | default:\r | |
581 | return 0;\r | |
582 | }\r | |
583 | }\r | |
584 | \r | |
585 | /* Trailer read - reads out 18b words in 3b increments\r | |
586 | Checksum is stored to avoid double calculation\r | |
587 | \r | |
588 | word lines contents\r | |
589 | 0 0-5 forward checksum (lines 0-1, rest 0)\r | |
590 | 1 6-11 0\r | |
591 | 2 12-17 0\r | |
592 | 3 18-23 reverse block mark\r | |
593 | 4 24-29 0\r | |
594 | \r | |
595 | Note that the reverse block mark (when read forward) appears\r | |
596 | as the complement obverse (3b nibbles swapped end for end and\r | |
597 | complemented).\r | |
598 | */\r | |
599 | \r | |
600 | int32 td_trailer (UNIT *uptr, int32 blk, int32 line)\r | |
601 | {\r | |
602 | int32 nibp, i, ba;\r | |
603 | int16 *fbuf= (int16 *) uptr->filebuf;\r | |
604 | \r | |
605 | switch (line) {\r | |
606 | \r | |
607 | case 0:\r | |
608 | td_csum = 07777; /* init csum */\r | |
609 | ba = blk * DTU_BSIZE (uptr);\r | |
610 | for (i = 0; i < DTU_BSIZE (uptr); i++) /* loop thru buf */\r | |
611 | td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777;\r | |
612 | td_csum = ((td_csum >> 6) ^ td_csum) & 077;\r | |
613 | return (td_csum >> 3) & 07;\r | |
614 | \r | |
615 | case 1:\r | |
616 | return (td_csum & 07);\r | |
617 | \r | |
618 | case 18: case 19: case 20: case 21:\r | |
619 | nibp = 3 * (line % DT_LPERMC);\r | |
620 | return ((blk >> nibp) & 07) ^ 07;\r | |
621 | \r | |
622 | default:\r | |
623 | return 0;\r | |
624 | }\r | |
625 | }\r | |
626 | \r | |
627 | /* Data read - convert block number/data line # to offset in data array */\r | |
628 | \r | |
629 | int32 td_read (UNIT *uptr, int32 blk, int32 line)\r | |
630 | {\r | |
631 | int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */\r | |
632 | uint32 ba = blk * DTU_BSIZE (uptr); /* block base */\r | |
633 | int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */\r | |
634 | \r | |
635 | ba = ba + (line / DT_WSIZE); /* block addr */\r | |
636 | return (fbuf[ba] >> nibp) & 07; /* get data nibble */\r | |
637 | }\r | |
638 | \r | |
639 | /* Data write - convert block number/data line # to offset in data array */\r | |
640 | \r | |
641 | void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat)\r | |
642 | {\r | |
643 | int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */\r | |
644 | uint32 ba = blk * DTU_BSIZE (uptr); /* block base */\r | |
645 | int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */\r | |
646 | \r | |
647 | ba = ba + (line / DT_WSIZE); /* block addr */\r | |
648 | fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */\r | |
649 | if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; /* upd length */\r | |
650 | return;\r | |
651 | }\r | |
652 | \r | |
653 | /* Reset routine */\r | |
654 | \r | |
655 | t_stat td_reset (DEVICE *dptr)\r | |
656 | {\r | |
657 | int32 i;\r | |
658 | UNIT *uptr;\r | |
659 | \r | |
660 | for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */\r | |
661 | uptr = td_dev.units + i;\r | |
662 | if (sim_is_running) { /* CAF? */\r | |
663 | if (uptr->STATE >= STA_ACC) { /* accel or uts? */\r | |
664 | if (td_setpos (uptr)) continue; /* update pos */\r | |
665 | sim_cancel (uptr);\r | |
666 | sim_activate (uptr, td_dctime); /* sched decel */\r | |
667 | uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR);\r | |
668 | }\r | |
669 | }\r | |
670 | else {\r | |
671 | sim_cancel (uptr); /* sim reset */\r | |
672 | uptr->STATE = 0; \r | |
673 | uptr->LASTT = sim_grtime ();\r | |
674 | }\r | |
675 | }\r | |
676 | td_slf = td_qlf = td_qlctr = 0; /* clear state */\r | |
677 | td_cmd = td_dat = td_mtk = 0;\r | |
678 | td_csum = 0;\r | |
679 | return SCPE_OK;\r | |
680 | }\r | |
681 | \r | |
682 | /* Bootstrap routine - OS/8 only \r | |
683 | \r | |
684 | 1) Read reverse until reverse end zone (mark track is complement obverse)\r | |
685 | 2) Read forward until mark track code 031. This is a composite code from\r | |
686 | the last 4b of the forward block number and the first two bits of the\r | |
687 | reverse guard (01 -0110 01- 1010). There are 16 lines before the first\r | |
688 | data word.\r | |
689 | 3) Store data words from 7354 to end of page. This includes header and\r | |
690 | trailer words.\r | |
691 | 4) Continue at location 7400.\r | |
692 | */\r | |
693 | \r | |
694 | #define BOOT_START 07300\r | |
695 | #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))\r | |
696 | \r | |
697 | static const uint16 boot_rom[] = {\r | |
698 | 01312, /* ST, TAD L4MT ;=2000, reverse */\r | |
699 | 04312, /* JMS L4MT ; rev lk for 022 */\r | |
700 | 04312, /* JMS L4MT ; fwd lk for 031 */\r | |
701 | 06773, /* DAT, SDSQ ; wait for 12b */\r | |
702 | 05303, /* JMP .-1 */\r | |
703 | 06777, /* SDRD ; read word */\r | |
704 | 03726, /* DCA I BUF ; store */\r | |
705 | 02326, /* ISZ BUF ; incr ptr */\r | |
706 | 05303, /* JMP DAT ; if not 0, cont */\r | |
707 | 05732, /* JMP I SCB ; jump to boot */\r | |
708 | 02000, /* L4MT,2000 ; overwritten */\r | |
709 | 01300, /* TAD ST ; =1312, go */\r | |
710 | 06774, /* SDLC ; new command */\r | |
711 | 06771, /* MTK, SDSS ; wait for mark */\r | |
712 | 05315, /* JMP .-1 */\r | |
713 | 06776, /* SDRC ; get mark code */\r | |
714 | 00331, /* AND K77 ; mask to 6b */\r | |
715 | 01327, /* CMP, TAD MCD ; got target code? */\r | |
716 | 07640, /* SZA CLA ; skip if yes */\r | |
717 | 05315, /* JMP MTK ; wait for mark */\r | |
718 | 02321, /* ISZ CMP ; next target */\r | |
719 | 05712, /* JMP I L4MT ; exit */\r | |
720 | 07354, /* BUF, 7354 ; loading point */\r | |
721 | 07756, /* MCD, -22 ; target 1 */\r | |
722 | 07747, /* -31 ; target 2 */\r | |
723 | 00077, /* 77 ; mask */\r | |
724 | 07400 /* SCB, 7400 ; secondary boot */\r | |
725 | };\r | |
726 | \r | |
727 | t_stat td_boot (int32 unitno, DEVICE *dptr)\r | |
728 | {\r | |
729 | int32 i;\r | |
730 | extern int32 saved_PC;\r | |
731 | \r | |
732 | if (unitno) return SCPE_ARG; /* only unit 0 */\r | |
733 | if (td_dib.dev != DEV_TD8E) return STOP_NOTSTD; /* only std devno */\r | |
734 | td_unit[unitno].pos = DT_EZLIN;\r | |
735 | for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];\r | |
736 | saved_PC = BOOT_START;\r | |
737 | return SCPE_OK;\r | |
738 | }\r | |
739 | \r | |
740 | /* Attach routine\r | |
741 | \r | |
742 | Determine 12b, 16b, or 18b/36b format\r | |
743 | Allocate buffer\r | |
744 | If 16b or 18b, read 16b or 18b format and convert to 12b in buffer\r | |
745 | If 12b, read data into buffer\r | |
746 | Set up mark track bit array\r | |
747 | */\r | |
748 | \r | |
749 | t_stat td_attach (UNIT *uptr, char *cptr)\r | |
750 | {\r | |
751 | uint32 pdp18b[D18_NBSIZE];\r | |
752 | uint16 pdp11b[D18_NBSIZE], *fbuf;\r | |
753 | int32 i, k, mtkpb;\r | |
754 | int32 u = uptr - td_dev.units;\r | |
755 | t_stat r;\r | |
756 | uint32 ba, sz;\r | |
757 | \r | |
758 | r = attach_unit (uptr, cptr); /* attach */\r | |
759 | if (r != SCPE_OK) return r; /* fail? */\r | |
760 | if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */\r | |
761 | uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;\r | |
762 | if (sim_switches & SWMASK ('F')) /* att 18b? */\r | |
763 | uptr->flags = uptr->flags & ~UNIT_8FMT;\r | |
764 | else if (sim_switches & SWMASK ('S')) /* att 16b? */\r | |
765 | uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;\r | |
766 | else if (!(sim_switches & SWMASK ('A')) && /* autosize? */\r | |
767 | (sz = sim_fsize (uptr->fileref))) {\r | |
768 | if (sz == D11_FILSIZ)\r | |
769 | uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;\r | |
770 | else if (sz > D8_FILSIZ)\r | |
771 | uptr->flags = uptr->flags & ~UNIT_8FMT;\r | |
772 | }\r | |
773 | }\r | |
774 | uptr->capac = DTU_CAPAC (uptr); /* set capacity */\r | |
775 | uptr->filebuf = calloc (uptr->capac, sizeof (int16));\r | |
776 | if (uptr->filebuf == NULL) { /* can't alloc? */\r | |
777 | detach_unit (uptr);\r | |
778 | return SCPE_MEM;\r | |
779 | }\r | |
780 | fbuf = (uint16 *) uptr->filebuf; /* file buffer */\r | |
781 | printf ("%s%d: ", sim_dname (&td_dev), u);\r | |
782 | if (uptr->flags & UNIT_8FMT) printf ("12b format");\r | |
783 | else if (uptr->flags & UNIT_11FMT) printf ("16b format");\r | |
784 | else printf ("18b/36b format");\r | |
785 | printf (", buffering file in memory\n");\r | |
786 | if (uptr->flags & UNIT_8FMT) /* 12b? */\r | |
787 | uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16),\r | |
788 | uptr->capac, uptr->fileref);\r | |
789 | else { /* 16b/18b */\r | |
790 | for (ba = 0; ba < uptr->capac; ) { /* loop thru file */\r | |
791 | if (uptr->flags & UNIT_11FMT) {\r | |
792 | k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref);\r | |
793 | for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i];\r | |
794 | }\r | |
795 | else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref);\r | |
796 | if (k == 0) break;\r | |
797 | for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0;\r | |
798 | for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */\r | |
799 | fbuf[ba] = (pdp18b[k] >> 6) & 07777;\r | |
800 | fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |\r | |
801 | ((pdp18b[k + 1] >> 12) & 077);\r | |
802 | fbuf[ba + 2] = pdp18b[k + 1] & 07777;\r | |
803 | ba = ba + 3;\r | |
804 | } /* end blk loop */\r | |
805 | } /* end file loop */\r | |
806 | uptr->hwmark = ba;\r | |
807 | } /* end else */\r | |
808 | uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */\r | |
809 | uptr->pos = DT_EZLIN; /* beyond leader */\r | |
810 | uptr->LASTT = sim_grtime (); /* last pos update */\r | |
811 | uptr->STATE = STA_STOP; /* stopped */\r | |
812 | \r | |
813 | mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */\r | |
814 | k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */\r | |
815 | k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */\r | |
816 | k = td_set_mtk (MTK_REV_GRD, u, k);\r | |
817 | for (i = 0; i < 4; i++) k = td_set_mtk (MTK_FWD_PRE, u, k);\r | |
818 | for (i = 0; i < (mtkpb - 4); i++) k = td_set_mtk (MTK_DATA, u, k);\r | |
819 | for (i = 0; i < 4; i++) k = td_set_mtk (MTK_REV_PRE, u, k);\r | |
820 | k = td_set_mtk (MTK_FWD_GRD, u, k);\r | |
821 | k = td_set_mtk (MTK_REV_BLK, u, k);\r | |
822 | k = td_set_mtk (MTK_INTER, u, k);\r | |
823 | return SCPE_OK;\r | |
824 | }\r | |
825 | \r | |
826 | /* Detach routine\r | |
827 | \r | |
828 | If 12b, write buffer to file\r | |
829 | If 16b or 18b, convert 12b buffer to 16b or 18b and write to file\r | |
830 | Deallocate buffer\r | |
831 | */\r | |
832 | \r | |
833 | t_stat td_detach (UNIT* uptr)\r | |
834 | {\r | |
835 | uint32 pdp18b[D18_NBSIZE];\r | |
836 | uint16 pdp11b[D18_NBSIZE], *fbuf;\r | |
837 | int32 i, k;\r | |
838 | int32 u = uptr - td_dev.units;\r | |
839 | uint32 ba;\r | |
840 | \r | |
841 | if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r | |
842 | fbuf = (uint16 *) uptr->filebuf; /* file buffer */\r | |
843 | if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */\r | |
844 | printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u);\r | |
845 | rewind (uptr->fileref); /* start of file */\r | |
846 | if (uptr->flags & UNIT_8FMT) /* PDP8? */\r | |
847 | fxwrite (uptr->filebuf, sizeof (uint16), /* write file */\r | |
848 | uptr->hwmark, uptr->fileref);\r | |
849 | else { /* 16b/18b */\r | |
850 | for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */\r | |
851 | for (k = 0; k < D18_NBSIZE; k = k + 2) {\r | |
852 | pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |\r | |
853 | ((uint32) (fbuf[ba + 1] >> 6) & 077);\r | |
854 | pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |\r | |
855 | ((uint32) (fbuf[ba + 2] & 07777));\r | |
856 | ba = ba + 3;\r | |
857 | } /* end loop blk */\r | |
858 | if (uptr->flags & UNIT_11FMT) { /* 16b? */\r | |
859 | for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i];\r | |
860 | fxwrite (pdp11b, sizeof (uint16),\r | |
861 | D18_NBSIZE, uptr->fileref);\r | |
862 | }\r | |
863 | else fxwrite (pdp18b, sizeof (uint32),\r | |
864 | D18_NBSIZE, uptr->fileref);\r | |
865 | } /* end loop buf */\r | |
866 | } /* end else */\r | |
867 | if (ferror (uptr->fileref)) perror ("I/O error");\r | |
868 | } /* end if hwmark */\r | |
869 | free (uptr->filebuf); /* release buf */\r | |
870 | uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */\r | |
871 | uptr->filebuf = NULL; /* clear buf ptr */\r | |
872 | uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */\r | |
873 | uptr->capac = DT_CAPAC; /* default size */\r | |
874 | uptr->pos = uptr->STATE = 0;\r | |
875 | sim_cancel (uptr); /* no more pulses */\r | |
876 | return detach_unit (uptr);\r | |
877 | }\r | |
878 | \r | |
879 | /* Set mark track code into bit array */\r | |
880 | \r | |
881 | int32 td_set_mtk (int32 code, int32 u, int32 k)\r | |
882 | {\r | |
883 | int32 i;\r | |
884 | \r | |
885 | for (i = 5; i >= 0; i--) tdb_mtk[u][k++] = (code >> i) & 1;\r | |
886 | return k;\r | |
887 | }\r | |
888 | \r | |
889 | /* Show position */\r | |
890 | \r | |
891 | t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
892 | {\r | |
893 | if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r | |
894 | if (uptr->pos < DT_EZLIN) /* rev end zone? */\r | |
895 | fprintf (st, "Reverse end zone\n");\r | |
896 | else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */\r | |
897 | int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */\r | |
898 | int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */\r | |
899 | fprintf (st, "Block %d, line %d, ", blkno, lineno);\r | |
900 | if (lineno < DT_HTLIN) /* header? */\r | |
901 | fprintf (st, "header cell %d, nibble %d\n",\r | |
902 | lineno / DT_LPERMC, lineno % DT_LPERMC);\r | |
903 | else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) /* data? */\r | |
904 | fprintf (st, "data word %d, nibble %d\n",\r | |
905 | (lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE);\r | |
906 | else fprintf (st, "trailer cell %d, nibble %d\n",\r | |
907 | (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC,\r | |
908 | (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC);\r | |
909 | }\r | |
910 | else fprintf (st, "Forward end zone\n"); /* fwd end zone */\r | |
911 | return SCPE_OK;\r | |
912 | }\r | |
913 | \r |