First Commit of my working state
[simh.git] / PDP8 / pdp8_td.c
CommitLineData
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
180int32 td_cmd = 0; /* command */\r
181int32 td_dat = 0; /* data */\r
182int32 td_mtk = 0; /* mark track */\r
183int32 td_slf = 0; /* single line flag */\r
184int32 td_qlf = 0; /* quad line flag */\r
185int32 td_tme = 0; /* timing error flag */\r
186int32 td_csum = 0; /* save check sum */\r
187int32 td_qlctr = 0; /* quad line ctr */\r
188int32 td_ltime = 20; /* interline time */\r
189int32 td_dctime = 40000; /* decel time */\r
190int32 td_stopoffr = 0;\r
191static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */\r
192\r
193DEVICE td_dev;\r
194int32 td77 (int32 IR, int32 AC);\r
195t_stat td_svc (UNIT *uptr);\r
196t_stat td_reset (DEVICE *dptr);\r
197t_stat td_attach (UNIT *uptr, char *cptr);\r
198t_stat td_detach (UNIT *uptr);\r
199t_stat td_boot (int32 unitno, DEVICE *dptr);\r
200t_bool td_newsa (int32 newf);\r
201t_bool td_setpos (UNIT *uptr);\r
202int32 td_header (UNIT *uptr, int32 blk, int32 line);\r
203int32 td_trailer (UNIT *uptr, int32 blk, int32 line);\r
204int32 td_read (UNIT *uptr, int32 blk, int32 line);\r
205void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb);\r
206int32 td_set_mtk (int32 code, int32 u, int32 k);\r
207t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc);\r
208\r
209extern uint16 M[];\r
210extern int32 sim_switches;\r
211extern 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
221DIB td_dib = { DEV_TD8E, 1, { &td77 } };\r
222\r
223UNIT 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
230REG 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
252MTAB 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
264DEVICE 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
274int32 td77 (int32 IR, int32 AC)\r
275{\r
276int32 pulse = IR & 07;\r
277int32 u = TDC_GETUNIT (td_cmd); /* get unit */\r
278int32 diff, t;\r
279\r
280switch (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
329return 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
341t_bool td_newsa (int32 newf)\r
342{\r
343int32 prev_mving, new_mving, prev_dir, new_dir;\r
344UNIT *uptr;\r
345\r
346uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */\r
347if ((uptr->flags & UNIT_ATT) == 0) return FALSE; /* new unit attached? */\r
348\r
349new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */\r
350prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */\r
351new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */\r
352prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */\r
353\r
354td_mtk = 0; /* mark trk reg cleared */\r
355\r
356if (!prev_mving && !new_mving) return FALSE; /* stop from stop? */\r
357\r
358if (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
367if ((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
379return 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
400t_bool td_setpos (UNIT *uptr)\r
401{\r
402uint32 new_time, ut, ulin, udelt;\r
403int32 delta;\r
404\r
405new_time = sim_grtime (); /* current time */\r
406ut = new_time - uptr->LASTT; /* elapsed time */\r
407if (ut == 0) return FALSE; /* no time gone? exit */\r
408uptr->LASTT = new_time; /* update last time */\r
409switch (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
432if (uptr->STATE & STA_DIR) uptr->pos = uptr->pos - delta; /* update pos */\r
433else uptr->pos = uptr->pos + delta;\r
434if (((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
440return FALSE;\r
441}\r
442\r
443/* Unit service - unit is either changing speed, or it is up to speed */\r
444\r
445t_stat td_svc (UNIT *uptr)\r
446{\r
447int32 mot = uptr->STATE & ~STA_DIR;\r
448int32 dir = uptr->STATE & STA_DIR;\r
449int32 unum = uptr - td_dev.units;\r
450int32 su = TDC_GETUNIT (td_cmd);\r
451int32 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
459if (mot == STA_STOP) return SCPE_OK; /* stopped? done */\r
460if ((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
465switch (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
507sim_activate (uptr, td_ltime); /* sched next line */\r
508if (unum != su) return SCPE_OK; /* not sel? done */\r
509td_slf = 1; /* set single */\r
510td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */\r
511if (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
519datb = 0; /* assume no data */\r
520if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */\r
521 mtkb = MTK_BIT (MTK_REV_END, uptr->pos);\r
522else if (uptr->pos < DT_EZLIN) /* rev buffer? */\r
523 mtkb = MTK_BIT (MTK_INTER, uptr->pos);\r
524else 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
544else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN))\r
545 mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */\r
546else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */\r
547\r
548if (dir) { /* reverse? */\r
549 mtkb = mtkb ^ 01; /* complement mark bit, */\r
550 datb = datb ^ 07; /* data bits */\r
551 }\r
552td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */\r
553td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */\r
554return 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
567int32 td_header (UNIT *uptr, int32 blk, int32 line)\r
568{\r
569int32 nibp;\r
570\r
571switch (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
600int32 td_trailer (UNIT *uptr, int32 blk, int32 line)\r
601{\r
602int32 nibp, i, ba;\r
603int16 *fbuf= (int16 *) uptr->filebuf;\r
604\r
605switch (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
629int32 td_read (UNIT *uptr, int32 blk, int32 line)\r
630{\r
631int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */\r
632uint32 ba = blk * DTU_BSIZE (uptr); /* block base */\r
633int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */\r
634\r
635ba = ba + (line / DT_WSIZE); /* block addr */\r
636return (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
641void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat)\r
642{\r
643int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */\r
644uint32 ba = blk * DTU_BSIZE (uptr); /* block base */\r
645int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */\r
646\r
647ba = ba + (line / DT_WSIZE); /* block addr */\r
648fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */\r
649if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; /* upd length */\r
650return;\r
651}\r
652\r
653/* Reset routine */\r
654\r
655t_stat td_reset (DEVICE *dptr)\r
656{\r
657int32 i;\r
658UNIT *uptr;\r
659\r
660for (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
676td_slf = td_qlf = td_qlctr = 0; /* clear state */\r
677td_cmd = td_dat = td_mtk = 0;\r
678td_csum = 0;\r
679return 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
697static 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
727t_stat td_boot (int32 unitno, DEVICE *dptr)\r
728{\r
729int32 i;\r
730extern int32 saved_PC;\r
731\r
732if (unitno) return SCPE_ARG; /* only unit 0 */\r
733if (td_dib.dev != DEV_TD8E) return STOP_NOTSTD; /* only std devno */\r
734td_unit[unitno].pos = DT_EZLIN;\r
735for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];\r
736saved_PC = BOOT_START;\r
737return 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
749t_stat td_attach (UNIT *uptr, char *cptr)\r
750{\r
751uint32 pdp18b[D18_NBSIZE];\r
752uint16 pdp11b[D18_NBSIZE], *fbuf;\r
753int32 i, k, mtkpb;\r
754int32 u = uptr - td_dev.units;\r
755t_stat r;\r
756uint32 ba, sz;\r
757\r
758r = attach_unit (uptr, cptr); /* attach */\r
759if (r != SCPE_OK) return r; /* fail? */\r
760if ((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
774uptr->capac = DTU_CAPAC (uptr); /* set capacity */\r
775uptr->filebuf = calloc (uptr->capac, sizeof (int16));\r
776if (uptr->filebuf == NULL) { /* can't alloc? */\r
777 detach_unit (uptr);\r
778 return SCPE_MEM;\r
779 }\r
780fbuf = (uint16 *) uptr->filebuf; /* file buffer */\r
781printf ("%s%d: ", sim_dname (&td_dev), u);\r
782if (uptr->flags & UNIT_8FMT) printf ("12b format");\r
783else if (uptr->flags & UNIT_11FMT) printf ("16b format");\r
784else printf ("18b/36b format");\r
785printf (", buffering file in memory\n");\r
786if (uptr->flags & UNIT_8FMT) /* 12b? */\r
787 uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16),\r
788 uptr->capac, uptr->fileref);\r
789else { /* 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
808uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */\r
809uptr->pos = DT_EZLIN; /* beyond leader */\r
810uptr->LASTT = sim_grtime (); /* last pos update */\r
811uptr->STATE = STA_STOP; /* stopped */\r
812\r
813mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */\r
814k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */\r
815k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */\r
816k = td_set_mtk (MTK_REV_GRD, u, k);\r
817for (i = 0; i < 4; i++) k = td_set_mtk (MTK_FWD_PRE, u, k);\r
818for (i = 0; i < (mtkpb - 4); i++) k = td_set_mtk (MTK_DATA, u, k);\r
819for (i = 0; i < 4; i++) k = td_set_mtk (MTK_REV_PRE, u, k);\r
820k = td_set_mtk (MTK_FWD_GRD, u, k);\r
821k = td_set_mtk (MTK_REV_BLK, u, k);\r
822k = td_set_mtk (MTK_INTER, u, k);\r
823return 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
833t_stat td_detach (UNIT* uptr)\r
834{\r
835uint32 pdp18b[D18_NBSIZE];\r
836uint16 pdp11b[D18_NBSIZE], *fbuf;\r
837int32 i, k;\r
838int32 u = uptr - td_dev.units;\r
839uint32 ba;\r
840\r
841if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r
842fbuf = (uint16 *) uptr->filebuf; /* file buffer */\r
843if (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
869free (uptr->filebuf); /* release buf */\r
870uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */\r
871uptr->filebuf = NULL; /* clear buf ptr */\r
872uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */\r
873uptr->capac = DT_CAPAC; /* default size */\r
874uptr->pos = uptr->STATE = 0;\r
875sim_cancel (uptr); /* no more pulses */\r
876return detach_unit (uptr);\r
877}\r
878\r
879/* Set mark track code into bit array */\r
880\r
881int32 td_set_mtk (int32 code, int32 u, int32 k)\r
882{\r
883int32 i;\r
884\r
885for (i = 5; i >= 0; i--) tdb_mtk[u][k++] = (code >> i) & 1;\r
886return k;\r
887}\r
888\r
889/* Show position */\r
890\r
891t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc)\r
892{\r
893if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r
894if (uptr->pos < DT_EZLIN) /* rev end zone? */\r
895 fprintf (st, "Reverse end zone\n");\r
896else 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
910else fprintf (st, "Forward end zone\n"); /* fwd end zone */\r
911return SCPE_OK;\r
912}\r
913\r