1 /* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator
3 Copyright (c) 1993-2006, Robert M Supnik
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
26 This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones'
27 PDP8 simulator but tracks the hardware implementation more closely.
31 23-Jun-06 RMS Fixed switch conflict in ATTACH
32 16-Aug-05 RMS Fixed C++ declaration and cast problems
33 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR
35 PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words.
36 Three file formats are supported:
38 18b/36b 256 words per block [256 x 18b]
39 16b 256 words per block [256 x 16b]
40 12b 129 words per block [129 x 12b]
42 When a 16b or 18/36b DECtape file is read in, it is converted to 12b format.
44 DECtape motion is measured in 3b lines. Time between lines is 33.33us.
45 Tape density is nominally 300 lines per inch. The format of a DECtape (as
46 taken from the TD8E formatter) is:
48 reverse end zone 8192 reverse end zone codes ~ 10 feet
49 reverse buffer 200 interblock codes
53 forward buffer 200 interblock codes
54 forward end zone 8192 forward end zone codes ~ 10 feet
56 A block consists of five 18b header words, a tape-specific number of data
57 words, and five 18b trailer words. All systems except the PDP-8 use a
58 standard block length of 256 words; the PDP-8 uses a standard block length
59 of 86 words (x 18b = 129 words x 12b).
61 Because a DECtape file only contains data, the simulator cannot support
62 write timing and mark track and can only do a limited implementation
63 of non-data words. Read assumes that the tape has been conventionally
67 header word 1 block number (for forward reads)
69 header word 4 checksum (for reverse reads)
71 trailer word 4 checksum (for forward reads)
73 trailer word 1 block number (for reverse reads)
76 Write modifies only the data words and dumps the non-data words in the
80 #include "pdp8_defs.h"
82 #define DT_NUMDR 2 /* #drives */
83 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
84 #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */
85 #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */
86 #define UNIT_WLK (1 << UNIT_V_WLK)
87 #define UNIT_8FMT (1 << UNIT_V_8FMT)
88 #define UNIT_11FMT (1 << UNIT_V_11FMT)
89 #define STATE u3 /* unit state */
90 #define LASTT u4 /* last time update */
91 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
93 /* System independent DECtape constants */
95 #define DT_LPERMC 6 /* lines per mark track */
96 #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */
97 #define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */
98 #define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */
100 /* 16b, 18b, 36b DECtape constants */
102 #define D18_WSIZE 6 /* word sizein lines */
103 #define D18_BSIZE 384 /* block size in 12b */
104 #define D18_TSIZE 578 /* tape size */
105 #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
106 #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))
107 #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */
109 #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)
110 #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32))
111 #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16))
113 /* 12b DECtape constants */
115 #define D8_WSIZE 4 /* word size in lines */
116 #define D8_BSIZE 129 /* block size in 12b */
117 #define D8_TSIZE 1474 /* tape size */
118 #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
119 #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))
120 #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */
121 #define D8_FILSIZ (D8_CAPAC * sizeof (int16))
123 /* This controller */
125 #define DT_CAPAC D8_CAPAC /* default */
126 #define DT_WSIZE D8_WSIZE
128 /* Calculated constants, per unit */
130 #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
131 #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
132 #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
133 #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
134 #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)
136 #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))
137 #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))
139 /* Command register */
141 #define TDC_UNIT 04000 /* unit select */
142 #define TDC_FWDRV 02000 /* fwd/rev */
143 #define TDC_STPGO 01000 /* stop/go */
144 #define TDC_RW 00400 /* read/write */
145 #define TDC_MASK 07400 /* implemented */
146 #define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0)
148 /* Status register */
150 #define TDS_WLO 00200 /* write lock */
151 #define TDS_TME 00100 /* timing/sel err */
153 /* Mark track register and codes */
156 #define MTK_REV_END 055 /* rev end zone */
157 #define MTK_INTER 025 /* interblock */
158 #define MTK_FWD_BLK 026 /* fwd block */
159 #define MTK_REV_GRD 032 /* reverse guard */
160 #define MTK_FWD_PRE 010 /* lock, etc */
161 #define MTK_DATA 070 /* data */
162 #define MTK_REV_PRE 073 /* lock, etc */
163 #define MTK_FWD_GRD 051 /* fwd guard */
164 #define MTK_REV_BLK 045 /* rev block */
165 #define MTK_FWD_END 022 /* fwd end zone */
169 #define STA_STOP 0 /* stopped */
170 #define STA_DEC 2 /* decelerating */
171 #define STA_ACC 4 /* accelerating */
172 #define STA_UTS 6 /* up to speed */
173 #define STA_DIR 1 /* fwd/rev */
175 #define ABS(x) (((x) < 0)? (-(x)): (x))
176 #define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1)
178 /* State and declarations */
180 int32 td_cmd
= 0; /* command */
181 int32 td_dat
= 0; /* data */
182 int32 td_mtk
= 0; /* mark track */
183 int32 td_slf
= 0; /* single line flag */
184 int32 td_qlf
= 0; /* quad line flag */
185 int32 td_tme
= 0; /* timing error flag */
186 int32 td_csum
= 0; /* save check sum */
187 int32 td_qlctr
= 0; /* quad line ctr */
188 int32 td_ltime
= 20; /* interline time */
189 int32 td_dctime
= 40000; /* decel time */
190 int32 td_stopoffr
= 0;
191 static uint8 tdb_mtk
[DT_NUMDR
][D18_LPERB
]; /* mark track bits */
194 int32
td77 (int32 IR
, int32 AC
);
195 t_stat
td_svc (UNIT
*uptr
);
196 t_stat
td_reset (DEVICE
*dptr
);
197 t_stat
td_attach (UNIT
*uptr
, char *cptr
);
198 t_stat
td_detach (UNIT
*uptr
);
199 t_stat
td_boot (int32 unitno
, DEVICE
*dptr
);
200 t_bool
td_newsa (int32 newf
);
201 t_bool
td_setpos (UNIT
*uptr
);
202 int32
td_header (UNIT
*uptr
, int32 blk
, int32 line
);
203 int32
td_trailer (UNIT
*uptr
, int32 blk
, int32 line
);
204 int32
td_read (UNIT
*uptr
, int32 blk
, int32 line
);
205 void td_write (UNIT
*uptr
, int32 blk
, int32 line
, int32 datb
);
206 int32
td_set_mtk (int32 code
, int32 u
, int32 k
);
207 t_stat
td_show_pos (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
210 extern int32 sim_switches
;
211 extern int32 sim_is_running
;
213 /* TD data structures
215 td_dev DT device descriptor
217 td_reg DT register list
218 td_mod DT modifier list
221 DIB td_dib
= { DEV_TD8E
, 1, { &td77
} };
224 { UDATA (&td_svc
, UNIT_8FMT
+UNIT_FIX
+UNIT_ATTABLE
+
225 UNIT_DISABLE
+UNIT_ROABLE
, DT_CAPAC
) },
226 { UDATA (&td_svc
, UNIT_8FMT
+UNIT_FIX
+UNIT_ATTABLE
+
227 UNIT_DISABLE
+UNIT_ROABLE
, DT_CAPAC
) }
231 { GRDATA (TDCMD
, td_cmd
, 8, 4, 8) },
232 { ORDATA (TDDAT
, td_dat
, 12) },
233 { ORDATA (TDMTK
, td_mtk
, 6) },
234 { FLDATA (TDSLF
, td_slf
, 0) },
235 { FLDATA (TDQLF
, td_qlf
, 0) },
236 { FLDATA (TDTME
, td_tme
, 0) },
237 { ORDATA (TDQL
, td_qlctr
, 2) },
238 { ORDATA (TDCSUM
, td_csum
, 6), REG_RO
},
239 { DRDATA (LTIME
, td_ltime
, 31), REG_NZ
| PV_LEFT
},
240 { DRDATA (DCTIME
, td_dctime
, 31), REG_NZ
| PV_LEFT
},
241 { URDATA (POS
, td_unit
[0].pos
, 10, T_ADDR_W
, 0,
242 DT_NUMDR
, PV_LEFT
| REG_RO
) },
243 { URDATA (STATT
, td_unit
[0].STATE
, 8, 18, 0,
245 { URDATA (LASTT
, td_unit
[0].LASTT
, 10, 32, 0,
246 DT_NUMDR
, REG_HRO
) },
247 { FLDATA (STOP_OFFR
, td_stopoffr
, 0) },
248 { ORDATA (DEVNUM
, td_dib
.dev
, 6), REG_HRO
},
253 { UNIT_WLK
, 0, "write enabled", "WRITEENABLED", NULL
},
254 { UNIT_WLK
, UNIT_WLK
, "write locked", "LOCKED", NULL
},
255 { UNIT_8FMT
+ UNIT_11FMT
, 0, "18b", NULL
, NULL
},
256 { UNIT_8FMT
+ UNIT_11FMT
, UNIT_8FMT
, "12b", NULL
, NULL
},
257 { UNIT_8FMT
+ UNIT_11FMT
, UNIT_11FMT
, "16b", NULL
, NULL
},
258 { MTAB_XTD
|MTAB_VDV
, 0, "DEVNO", "DEVNO",
259 &set_dev
, &show_dev
, NULL
},
260 { MTAB_XTD
|MTAB_VUN
|MTAB_NMO
, 0, "POSITION", NULL
, NULL
, &td_show_pos
},
265 "TD", td_unit
, td_reg
, td_mod
,
266 DT_NUMDR
, 8, 24, 1, 8, 12,
267 NULL
, NULL
, &td_reset
,
268 &td_boot
, &td_attach
, &td_detach
,
269 &td_dib
, DEV_DISABLE
| DEV_DIS
274 int32
td77 (int32 IR
, int32 AC
)
276 int32 pulse
= IR
& 07;
277 int32 u
= TDC_GETUNIT (td_cmd
); /* get unit */
283 if (td_slf
) return AC
| IOT_SKP
;
287 if (td_tme
) return AC
| IOT_SKP
;
291 if (td_qlf
) return AC
| IOT_SKP
;
295 td_tme
= 0; /* clear tim err */
296 diff
= (td_cmd
^ AC
) & TDC_MASK
; /* cmd changes */
297 td_cmd
= AC
& TDC_MASK
; /* update cmd */
298 if ((diff
!= 0) && (diff
!= TDC_RW
)) { /* signif change? */
299 if (td_newsa (td_cmd
)) /* new command */
300 return AC
| (IORETURN (td_stopoffr
, STOP_DTOFF
) << IOT_V_REASON
);
305 td_slf
= 0; /* clear flags */
308 td_dat
= AC
; /* load data reg */
312 td_slf
= 0; /* clear flags */
315 t
= td_cmd
| td_mtk
; /* form status */
316 if (td_tme
|| !(td_unit
[u
].flags
& UNIT_ATT
)) /* tim/sel err? */
318 if (td_unit
[u
].flags
& UNIT_WPRT
) /* write locked? */
320 return t
; /* return status */
323 td_slf
= 0; /* clear flags */
326 return td_dat
; /* return data */
332 /* Command register change (start/stop, forward/reverse, new unit)
334 1. If change in motion, stop to start
335 - schedule up to speed
336 - set function as next state
337 2. If change in motion, start to stop, or change in direction
341 t_bool
td_newsa (int32 newf
)
343 int32 prev_mving
, new_mving
, prev_dir
, new_dir
;
346 uptr
= td_dev
.units
+ TDC_GETUNIT (newf
); /* new unit */
347 if ((uptr
->flags
& UNIT_ATT
) == 0) return FALSE
; /* new unit attached? */
349 new_mving
= ((newf
& TDC_STPGO
) != 0); /* new moving? */
350 prev_mving
= (uptr
->STATE
!= STA_STOP
); /* previous moving? */
351 new_dir
= ((newf
& TDC_FWDRV
) != 0); /* new dir? */
352 prev_dir
= ((uptr
->STATE
& STA_DIR
) != 0); /* previous dir? */
354 td_mtk
= 0; /* mark trk reg cleared */
356 if (!prev_mving
&& !new_mving
) return FALSE
; /* stop from stop? */
358 if (new_mving
&& !prev_mving
) { /* start from stop? */
359 if (td_setpos (uptr
)) return TRUE
; /* update pos */
360 sim_cancel (uptr
); /* stop current */
361 sim_activate (uptr
, td_dctime
- (td_dctime
>> 2)); /* sched accel */
362 uptr
->STATE
= STA_ACC
| new_dir
; /* set status */
363 td_slf
= td_qlf
= td_qlctr
= 0; /* clear state */
367 if ((prev_mving
&& !new_mving
) || /* stop from moving? */
368 (prev_dir
!= new_dir
)) { /* dir chg while moving? */
369 if (uptr
->STATE
>= STA_ACC
) { /* not stopping? */
370 if (td_setpos (uptr
)) return TRUE
; /* update pos */
371 sim_cancel (uptr
); /* stop current */
372 sim_activate (uptr
, td_dctime
); /* schedule decel */
373 uptr
->STATE
= STA_DEC
| prev_dir
; /* set status */
374 td_slf
= td_qlf
= td_qlctr
= 0; /* clear state */
382 /* Update DECtape position
384 DECtape motion is modeled as a constant velocity, with linear
385 acceleration and deceleration. The motion equations are as follows:
387 t = time since operation started
388 tmax = time for operation (accel, decel only)
389 v = at speed velocity in lines (= 1/td_ltime)
392 at speed dist = t * v
393 accel dist = (t^2 * v) / (2 * tmax)
394 decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)
396 This routine uses the relative (integer) time, rather than the absolute
397 (floating point) time, to allow save and restore of the start times.
400 t_bool
td_setpos (UNIT
*uptr
)
402 uint32 new_time
, ut
, ulin
, udelt
;
405 new_time
= sim_grtime (); /* current time */
406 ut
= new_time
- uptr
->LASTT
; /* elapsed time */
407 if (ut
== 0) return FALSE
; /* no time gone? exit */
408 uptr
->LASTT
= new_time
; /* update last time */
409 switch (uptr
->STATE
& ~STA_DIR
) { /* case on motion */
411 case STA_STOP
: /* stop */
415 case STA_DEC
: /* slowing */
416 ulin
= ut
/ (uint32
) td_ltime
;
417 udelt
= td_dctime
/ td_ltime
;
418 delta
= ((ulin
* udelt
* 2) - (ulin
* ulin
)) / (2 * udelt
);
421 case STA_ACC
: /* accelerating */
422 ulin
= ut
/ (uint32
) td_ltime
;
423 udelt
= (td_dctime
- (td_dctime
>> 2)) / td_ltime
;
424 delta
= (ulin
* ulin
) / (2 * udelt
);
427 case STA_UTS
: /* at speed */
428 delta
= ut
/ (uint32
) td_ltime
;
432 if (uptr
->STATE
& STA_DIR
) uptr
->pos
= uptr
->pos
- delta
; /* update pos */
433 else uptr
->pos
= uptr
->pos
+ delta
;
434 if (((int32
) uptr
->pos
< 0) ||
435 ((int32
) uptr
->pos
> (DTU_FWDEZ (uptr
) + DT_EZLIN
))) {
436 detach_unit (uptr
); /* off reel */
437 sim_cancel (uptr
); /* no timing pulses */
443 /* Unit service - unit is either changing speed, or it is up to speed */
445 t_stat
td_svc (UNIT
*uptr
)
447 int32 mot
= uptr
->STATE
& ~STA_DIR
;
448 int32 dir
= uptr
->STATE
& STA_DIR
;
449 int32 unum
= uptr
- td_dev
.units
;
450 int32 su
= TDC_GETUNIT (td_cmd
);
455 Decelerating - if go, next state must be accel as specified by td_cmd
456 Accelerating - next state must be up to speed, fall through
457 Up to speed - process line */
459 if (mot
== STA_STOP
) return SCPE_OK
; /* stopped? done */
460 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not attached? */
461 uptr
->STATE
= uptr
->pos
= 0; /* also done */
465 switch (mot
) { /* case on motion */
467 case STA_DEC
: /* deceleration */
468 if (td_setpos (uptr
)) /* upd pos; off reel? */
469 return IORETURN (td_stopoffr
, STOP_DTOFF
);
470 if ((unum
!= su
) || !(td_cmd
& TDC_STPGO
)) /* not sel or stop? */
471 uptr
->STATE
= 0; /* stop */
472 else { /* selected and go */
473 uptr
->STATE
= STA_ACC
| /* accelerating */
474 ((td_cmd
& TDC_FWDRV
)? STA_DIR
: 0); /* in new dir */
475 sim_activate (uptr
, td_dctime
- (td_dctime
>> 2));
479 case STA_ACC
: /* accelerating */
480 if (td_setpos (uptr
)) /* upd pos; off reel? */
481 return IORETURN (td_stopoffr
, STOP_DTOFF
);
482 uptr
->STATE
= STA_UTS
| dir
; /* set up to speed */
485 case STA_UTS
: /* up to speed */
486 if (dir
) uptr
->pos
= uptr
->pos
- 1; /* adjust position */
487 else uptr
->pos
= uptr
->pos
+ 1;
488 uptr
->LASTT
= sim_grtime (); /* save time */
489 if (((int32
) uptr
->pos
< 0) || /* off reel? */
490 (uptr
->pos
>= (((uint32
) DTU_FWDEZ (uptr
)) + DT_EZLIN
))) {
492 return IORETURN (td_stopoffr
, STOP_DTOFF
);
494 break; /* check function */
497 /* At speed - process the current line
499 Once the TD8E is running at speed, it operates line by line. If reading,
500 the current mark track bit is shifted into the mark track register, and
501 the current data nibble (3b) is shifted into the data register. If
502 writing, the current mark track bit is shifted into the mark track
503 register, the top nibble from the data register is written to tape, and
504 the data register is shifted up. The complexity here comes from
505 synthesizing the mark track, based on tape position, and the header data. */
507 sim_activate (uptr
, td_ltime
); /* sched next line */
508 if (unum
!= su
) return SCPE_OK
; /* not sel? done */
509 td_slf
= 1; /* set single */
510 td_qlctr
= (td_qlctr
+ 1) % DT_WSIZE
; /* count words */
511 if (td_qlctr
== 0) { /* lines mod 4? */
512 if (td_qlf
) { /* quad line set? */
513 td_tme
= 1; /* timing error */
514 td_cmd
= td_cmd
& ~TDC_RW
; /* clear write */
516 else td_qlf
= 1; /* no, set quad */
519 datb
= 0; /* assume no data */
520 if (uptr
->pos
< (DT_EZLIN
- DT_BFLIN
)) /* rev end zone? */
521 mtkb
= MTK_BIT (MTK_REV_END
, uptr
->pos
);
522 else if (uptr
->pos
< DT_EZLIN
) /* rev buffer? */
523 mtkb
= MTK_BIT (MTK_INTER
, uptr
->pos
);
524 else if (uptr
->pos
< ((uint32
) DTU_FWDEZ (uptr
))) { /* data zone? */
525 int32 blkno
= DT_LIN2BL (uptr
->pos
, uptr
); /* block # */
526 int32 lineno
= DT_LIN2OF (uptr
->pos
, uptr
); /* line # within block */
527 if (lineno
< DT_HTLIN
) { /* header? */
528 if ((td_cmd
& TDC_RW
) == 0) /* read? */
529 datb
= td_header (uptr
, blkno
, lineno
); /* get nibble */
531 else if (lineno
< (DTU_LPERB (uptr
) - DT_HTLIN
)) { /* data? */
532 if (td_cmd
& TDC_RW
) /* write? */
533 td_write (uptr
, blkno
, /* write data nibble */
534 lineno
- DT_HTLIN
, /* data rel line num */
536 else datb
= td_read (uptr
, blkno
, /* no, read */
539 else if ((td_cmd
& TDC_RW
) == 0) /* trailer; read? */
540 datb
= td_trailer (uptr
, blkno
, lineno
- /* get trlr nibble */
541 (DTU_LPERB (uptr
) - DT_HTLIN
));
542 mtkb
= tdb_mtk
[unum
][lineno
];
544 else if (uptr
->pos
< (((uint32
) DTU_FWDEZ (uptr
)) + DT_BFLIN
))
545 mtkb
= MTK_BIT (MTK_INTER
, uptr
->pos
); /* fwd buffer? */
546 else mtkb
= MTK_BIT (MTK_FWD_END
, uptr
->pos
); /* fwd end zone */
548 if (dir
) { /* reverse? */
549 mtkb
= mtkb
^ 01; /* complement mark bit, */
550 datb
= datb
^ 07; /* data bits */
552 td_mtk
= ((td_mtk
<< 1) | mtkb
) & MTK_MASK
; /* shift mark reg */
553 td_dat
= ((td_dat
<< 3) | datb
) & 07777; /* shift data reg */
557 /* Header read - reads out 18b words in 3b increments
564 4 24-29 reverse checksum (0777777)
567 int32
td_header (UNIT
*uptr
, int32 blk
, int32 line
)
573 case 8: case 9: case 10: case 11: /* block num */
574 nibp
= 3 * (DT_LPERMC
- 1 - (line
% DT_LPERMC
));
575 return (blk
>> nibp
) & 07;
577 case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */
578 return 07; /* 777777 */
585 /* Trailer read - reads out 18b words in 3b increments
586 Checksum is stored to avoid double calculation
589 0 0-5 forward checksum (lines 0-1, rest 0)
592 3 18-23 reverse block mark
595 Note that the reverse block mark (when read forward) appears
596 as the complement obverse (3b nibbles swapped end for end and
600 int32
td_trailer (UNIT
*uptr
, int32 blk
, int32 line
)
603 int16
*fbuf
= (int16
*) uptr
->filebuf
;
608 td_csum
= 07777; /* init csum */
609 ba
= blk
* DTU_BSIZE (uptr
);
610 for (i
= 0; i
< DTU_BSIZE (uptr
); i
++) /* loop thru buf */
611 td_csum
= (td_csum
^ ~fbuf
[ba
+ i
]) & 07777;
612 td_csum
= ((td_csum
>> 6) ^ td_csum
) & 077;
613 return (td_csum
>> 3) & 07;
616 return (td_csum
& 07);
618 case 18: case 19: case 20: case 21:
619 nibp
= 3 * (line
% DT_LPERMC
);
620 return ((blk
>> nibp
) & 07) ^ 07;
627 /* Data read - convert block number/data line # to offset in data array */
629 int32
td_read (UNIT
*uptr
, int32 blk
, int32 line
)
631 int16
*fbuf
= (int16
*) uptr
->filebuf
; /* buffer */
632 uint32 ba
= blk
* DTU_BSIZE (uptr
); /* block base */
633 int32 nibp
= 3 * (DT_WSIZE
- 1 - (line
% DT_WSIZE
)); /* nibble pos */
635 ba
= ba
+ (line
/ DT_WSIZE
); /* block addr */
636 return (fbuf
[ba
] >> nibp
) & 07; /* get data nibble */
639 /* Data write - convert block number/data line # to offset in data array */
641 void td_write (UNIT
*uptr
, int32 blk
, int32 line
, int32 dat
)
643 int16
*fbuf
= (int16
*) uptr
->filebuf
; /* buffer */
644 uint32 ba
= blk
* DTU_BSIZE (uptr
); /* block base */
645 int32 nibp
= 3 * (DT_WSIZE
- 1 - (line
% DT_WSIZE
)); /* nibble pos */
647 ba
= ba
+ (line
/ DT_WSIZE
); /* block addr */
648 fbuf
[ba
] = (fbuf
[ba
] & ~(07 << nibp
)) | (dat
<< nibp
); /* upd data nibble */
649 if (ba
>= uptr
->hwmark
) uptr
->hwmark
= ba
+ 1; /* upd length */
655 t_stat
td_reset (DEVICE
*dptr
)
660 for (i
= 0; i
< DT_NUMDR
; i
++) { /* stop all activity */
661 uptr
= td_dev
.units
+ i
;
662 if (sim_is_running
) { /* CAF? */
663 if (uptr
->STATE
>= STA_ACC
) { /* accel or uts? */
664 if (td_setpos (uptr
)) continue; /* update pos */
666 sim_activate (uptr
, td_dctime
); /* sched decel */
667 uptr
->STATE
= STA_DEC
| (uptr
->STATE
& STA_DIR
);
671 sim_cancel (uptr
); /* sim reset */
673 uptr
->LASTT
= sim_grtime ();
676 td_slf
= td_qlf
= td_qlctr
= 0; /* clear state */
677 td_cmd
= td_dat
= td_mtk
= 0;
682 /* Bootstrap routine - OS/8 only
684 1) Read reverse until reverse end zone (mark track is complement obverse)
685 2) Read forward until mark track code 031. This is a composite code from
686 the last 4b of the forward block number and the first two bits of the
687 reverse guard (01 -0110 01- 1010). There are 16 lines before the first
689 3) Store data words from 7354 to end of page. This includes header and
691 4) Continue at location 7400.
694 #define BOOT_START 07300
695 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
697 static const uint16 boot_rom
[] = {
698 01312, /* ST, TAD L4MT ;=2000, reverse */
699 04312, /* JMS L4MT ; rev lk for 022 */
700 04312, /* JMS L4MT ; fwd lk for 031 */
701 06773, /* DAT, SDSQ ; wait for 12b */
703 06777, /* SDRD ; read word */
704 03726, /* DCA I BUF ; store */
705 02326, /* ISZ BUF ; incr ptr */
706 05303, /* JMP DAT ; if not 0, cont */
707 05732, /* JMP I SCB ; jump to boot */
708 02000, /* L4MT,2000 ; overwritten */
709 01300, /* TAD ST ; =1312, go */
710 06774, /* SDLC ; new command */
711 06771, /* MTK, SDSS ; wait for mark */
713 06776, /* SDRC ; get mark code */
714 00331, /* AND K77 ; mask to 6b */
715 01327, /* CMP, TAD MCD ; got target code? */
716 07640, /* SZA CLA ; skip if yes */
717 05315, /* JMP MTK ; wait for mark */
718 02321, /* ISZ CMP ; next target */
719 05712, /* JMP I L4MT ; exit */
720 07354, /* BUF, 7354 ; loading point */
721 07756, /* MCD, -22 ; target 1 */
722 07747, /* -31 ; target 2 */
723 00077, /* 77 ; mask */
724 07400 /* SCB, 7400 ; secondary boot */
727 t_stat
td_boot (int32 unitno
, DEVICE
*dptr
)
730 extern int32 saved_PC
;
732 if (unitno
) return SCPE_ARG
; /* only unit 0 */
733 if (td_dib
.dev
!= DEV_TD8E
) return STOP_NOTSTD
; /* only std devno */
734 td_unit
[unitno
].pos
= DT_EZLIN
;
735 for (i
= 0; i
< BOOT_LEN
; i
++) M
[BOOT_START
+ i
] = boot_rom
[i
];
736 saved_PC
= BOOT_START
;
742 Determine 12b, 16b, or 18b/36b format
744 If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
745 If 12b, read data into buffer
746 Set up mark track bit array
749 t_stat
td_attach (UNIT
*uptr
, char *cptr
)
751 uint32 pdp18b
[D18_NBSIZE
];
752 uint16 pdp11b
[D18_NBSIZE
], *fbuf
;
754 int32 u
= uptr
- td_dev
.units
;
758 r
= attach_unit (uptr
, cptr
); /* attach */
759 if (r
!= SCPE_OK
) return r
; /* fail? */
760 if ((sim_switches
& SIM_SW_REST
) == 0) { /* not from rest? */
761 uptr
->flags
= (uptr
->flags
| UNIT_8FMT
) & ~UNIT_11FMT
;
762 if (sim_switches
& SWMASK ('F')) /* att 18b? */
763 uptr
->flags
= uptr
->flags
& ~UNIT_8FMT
;
764 else if (sim_switches
& SWMASK ('S')) /* att 16b? */
765 uptr
->flags
= (uptr
->flags
| UNIT_11FMT
) & ~UNIT_8FMT
;
766 else if (!(sim_switches
& SWMASK ('A')) && /* autosize? */
767 (sz
= sim_fsize (uptr
->fileref
))) {
768 if (sz
== D11_FILSIZ
)
769 uptr
->flags
= (uptr
->flags
| UNIT_11FMT
) & ~UNIT_8FMT
;
770 else if (sz
> D8_FILSIZ
)
771 uptr
->flags
= uptr
->flags
& ~UNIT_8FMT
;
774 uptr
->capac
= DTU_CAPAC (uptr
); /* set capacity */
775 uptr
->filebuf
= calloc (uptr
->capac
, sizeof (int16
));
776 if (uptr
->filebuf
== NULL
) { /* can't alloc? */
780 fbuf
= (uint16
*) uptr
->filebuf
; /* file buffer */
781 printf ("%s%d: ", sim_dname (&td_dev
), u
);
782 if (uptr
->flags
& UNIT_8FMT
) printf ("12b format");
783 else if (uptr
->flags
& UNIT_11FMT
) printf ("16b format");
784 else printf ("18b/36b format");
785 printf (", buffering file in memory\n");
786 if (uptr
->flags
& UNIT_8FMT
) /* 12b? */
787 uptr
->hwmark
= fxread (uptr
->filebuf
, sizeof (uint16
),
788 uptr
->capac
, uptr
->fileref
);
790 for (ba
= 0; ba
< uptr
->capac
; ) { /* loop thru file */
791 if (uptr
->flags
& UNIT_11FMT
) {
792 k
= fxread (pdp11b
, sizeof (uint16
), D18_NBSIZE
, uptr
->fileref
);
793 for (i
= 0; i
< k
; i
++) pdp18b
[i
] = pdp11b
[i
];
795 else k
= fxread (pdp18b
, sizeof (uint32
), D18_NBSIZE
, uptr
->fileref
);
797 for ( ; k
< D18_NBSIZE
; k
++) pdp18b
[k
] = 0;
798 for (k
= 0; k
< D18_NBSIZE
; k
= k
+ 2) { /* loop thru blk */
799 fbuf
[ba
] = (pdp18b
[k
] >> 6) & 07777;
800 fbuf
[ba
+ 1] = ((pdp18b
[k
] & 077) << 6) |
801 ((pdp18b
[k
+ 1] >> 12) & 077);
802 fbuf
[ba
+ 2] = pdp18b
[k
+ 1] & 07777;
805 } /* end file loop */
808 uptr
->flags
= uptr
->flags
| UNIT_BUF
; /* set buf flag */
809 uptr
->pos
= DT_EZLIN
; /* beyond leader */
810 uptr
->LASTT
= sim_grtime (); /* last pos update */
811 uptr
->STATE
= STA_STOP
; /* stopped */
813 mtkpb
= (DTU_BSIZE (uptr
) * DT_WSIZE
) / DT_LPERMC
; /* mtk codes per blk */
814 k
= td_set_mtk (MTK_INTER
, u
, 0); /* fill mark track */
815 k
= td_set_mtk (MTK_FWD_BLK
, u
, k
); /* bit array */
816 k
= td_set_mtk (MTK_REV_GRD
, u
, k
);
817 for (i
= 0; i
< 4; i
++) k
= td_set_mtk (MTK_FWD_PRE
, u
, k
);
818 for (i
= 0; i
< (mtkpb
- 4); i
++) k
= td_set_mtk (MTK_DATA
, u
, k
);
819 for (i
= 0; i
< 4; i
++) k
= td_set_mtk (MTK_REV_PRE
, u
, k
);
820 k
= td_set_mtk (MTK_FWD_GRD
, u
, k
);
821 k
= td_set_mtk (MTK_REV_BLK
, u
, k
);
822 k
= td_set_mtk (MTK_INTER
, u
, k
);
828 If 12b, write buffer to file
829 If 16b or 18b, convert 12b buffer to 16b or 18b and write to file
833 t_stat
td_detach (UNIT
* uptr
)
835 uint32 pdp18b
[D18_NBSIZE
];
836 uint16 pdp11b
[D18_NBSIZE
], *fbuf
;
838 int32 u
= uptr
- td_dev
.units
;
841 if (!(uptr
->flags
& UNIT_ATT
)) return SCPE_OK
;
842 fbuf
= (uint16
*) uptr
->filebuf
; /* file buffer */
843 if (uptr
->hwmark
&& ((uptr
->flags
& UNIT_RO
)== 0)) { /* any data? */
844 printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev
), u
);
845 rewind (uptr
->fileref
); /* start of file */
846 if (uptr
->flags
& UNIT_8FMT
) /* PDP8? */
847 fxwrite (uptr
->filebuf
, sizeof (uint16
), /* write file */
848 uptr
->hwmark
, uptr
->fileref
);
850 for (ba
= 0; ba
< uptr
->hwmark
; ) { /* loop thru buf */
851 for (k
= 0; k
< D18_NBSIZE
; k
= k
+ 2) {
852 pdp18b
[k
] = ((uint32
) (fbuf
[ba
] & 07777) << 6) |
853 ((uint32
) (fbuf
[ba
+ 1] >> 6) & 077);
854 pdp18b
[k
+ 1] = ((uint32
) (fbuf
[ba
+ 1] & 077) << 12) |
855 ((uint32
) (fbuf
[ba
+ 2] & 07777));
858 if (uptr
->flags
& UNIT_11FMT
) { /* 16b? */
859 for (i
= 0; i
< D18_NBSIZE
; i
++) pdp11b
[i
] = pdp18b
[i
];
860 fxwrite (pdp11b
, sizeof (uint16
),
861 D18_NBSIZE
, uptr
->fileref
);
863 else fxwrite (pdp18b
, sizeof (uint32
),
864 D18_NBSIZE
, uptr
->fileref
);
867 if (ferror (uptr
->fileref
)) perror ("I/O error");
868 } /* end if hwmark */
869 free (uptr
->filebuf
); /* release buf */
870 uptr
->flags
= uptr
->flags
& ~UNIT_BUF
; /* clear buf flag */
871 uptr
->filebuf
= NULL
; /* clear buf ptr */
872 uptr
->flags
= (uptr
->flags
| UNIT_8FMT
) & ~UNIT_11FMT
; /* default fmt */
873 uptr
->capac
= DT_CAPAC
; /* default size */
874 uptr
->pos
= uptr
->STATE
= 0;
875 sim_cancel (uptr
); /* no more pulses */
876 return detach_unit (uptr
);
879 /* Set mark track code into bit array */
881 int32
td_set_mtk (int32 code
, int32 u
, int32 k
)
885 for (i
= 5; i
>= 0; i
--) tdb_mtk
[u
][k
++] = (code
>> i
) & 1;
891 t_stat
td_show_pos (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
893 if ((uptr
->flags
& UNIT_ATT
) == 0) return SCPE_UNATT
;
894 if (uptr
->pos
< DT_EZLIN
) /* rev end zone? */
895 fprintf (st
, "Reverse end zone\n");
896 else if (uptr
->pos
< ((uint32
) DTU_FWDEZ (uptr
))) { /* data zone? */
897 int32 blkno
= DT_LIN2BL (uptr
->pos
, uptr
); /* block # */
898 int32 lineno
= DT_LIN2OF (uptr
->pos
, uptr
); /* line # within block */
899 fprintf (st
, "Block %d, line %d, ", blkno
, lineno
);
900 if (lineno
< DT_HTLIN
) /* header? */
901 fprintf (st
, "header cell %d, nibble %d\n",
902 lineno
/ DT_LPERMC
, lineno
% DT_LPERMC
);
903 else if (lineno
< (DTU_LPERB (uptr
) - DT_HTLIN
)) /* data? */
904 fprintf (st
, "data word %d, nibble %d\n",
905 (lineno
- DT_HTLIN
) / DT_WSIZE
, (lineno
- DT_HTLIN
) % DT_WSIZE
);
906 else fprintf (st
, "trailer cell %d, nibble %d\n",
907 (lineno
- (DTU_LPERB (uptr
) - DT_HTLIN
)) / DT_LPERMC
,
908 (lineno
- (DTU_LPERB (uptr
) - DT_HTLIN
)) % DT_LPERMC
);
910 else fprintf (st
, "Forward end zone\n"); /* fwd end zone */