1 /* pdp11_rp.c - RP04/05/06/07 RM02/03/05/80 Massbus disk controller
3 Copyright (c) 1993-2007, 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 rp RH/RP/RM moving head disks
28 17-May-07 RMS CS1 DVA resides in device, not MBA
29 21-Nov-05 RMS Enable/disable device also enables/disables Massbus adapter
30 12-Nov-05 RMS Fixed DriveClear, does not clear disk address
31 16-Aug-05 RMS Fixed C++ declaration and cast problems
32 18-Mar-05 RMS Added attached test to detach routine
33 12-Sep-04 RMS Cloned from pdp11_rp.c
35 Note: The VMS driver and the RP controller documentation state that
40 But the TOPS-10 and TOPS-20 drivers, and the RP schematics state that
45 The simulation follows the schematics. The VMS drivers defines but does
46 not use these offsets, and the error logger follows the schematics.
49 #if defined (VM_PDP10)
50 #error "PDP-10 uses pdp10_rp.c!"
52 #elif defined (VM_PDP11)
53 #include "pdp11_defs.h"
54 #define INIT_DTYPE RM03_DTYPE
55 #define INIT_SIZE RM03_SIZE
57 #elif defined (VM_VAX)
59 #define INIT_DTYPE RP06_DTYPE
60 #define INIT_SIZE RP06_SIZE
63 #error "Qbus not supported!"
70 #define RP_CTRL 0 /* ctrl is RP */
71 #define RM_CTRL 1 /* ctrl is RM */
72 #define RP_NUMDR 8 /* #drives */
73 #define RP_NUMWD 256 /* words/sector */
74 #define RP_MAXFR (1 << 16) /* max transfer */
75 #define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \
76 ((double) drv_tab[d].sect)))
77 #define RM_OF (MBA_RMASK + 1)
79 /* Flags in the unit flags word */
81 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
82 #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
83 #define UNIT_M_DTYPE 7
84 #define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */
85 #define UNIT_V_DUMMY (UNIT_V_UF + 5) /* dummy flag */
86 #define UNIT_WLK (1 << UNIT_V_WLK)
87 #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
88 #define UNIT_AUTO (1 << UNIT_V_AUTO)
89 #define UNIT_DUMMY (1 << UNIT_V_DUMMY)
90 #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
91 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */
93 /* Parameters in the unit descriptor */
95 #define CYL u3 /* current cylinder */
97 /* RPCS1, RMCS1 - control/status 1 - offset 0 */
100 #define RM_CS1_OF (0 + RM_OF)
101 #define CS1_GO CSR_GO /* go */
102 #define CS1_V_FNC 1 /* function pos */
103 #define CS1_M_FNC 037 /* function mask */
104 #define CS1_N_FNC (CS1_M_FNC + 1)
105 #define FNC_NOP 000 /* no operation */
106 #define FNC_UNLOAD 001 /* unload */
107 #define FNC_SEEK 002 /* seek */
108 #define FNC_RECAL 003 /* recalibrate */
109 #define FNC_DCLR 004 /* drive clear */
110 #define FNC_RELEASE 005 /* port release */
111 #define FNC_OFFSET 006 /* offset */
112 #define FNC_RETURN 007 /* return to center */
113 #define FNC_PRESET 010 /* read-in preset */
114 #define FNC_PACK 011 /* pack acknowledge */
115 #define FNC_SEARCH 014 /* search */
116 #define FNC_XFER 024 /* >=? data xfr */
117 #define FNC_WCHK 024 /* write check */
118 #define FNC_WRITE 030 /* write */
119 #define FNC_WRITEH 031 /* write w/ headers */
120 #define FNC_READ 034 /* read */
121 #define FNC_READH 035 /* read w/ headers */
123 #define CS1_DVA 04000 /* drive avail */
124 #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC)
126 /* RPDS, RMDS - drive status - offset 1 */
129 #define RM_DS_OF (1 + RM_OF)
130 #define DS_OFM 0000001 /* offset mode */
131 #define DS_VV 0000100 /* volume valid */
132 #define DS_RDY 0000200 /* drive ready */
133 #define DS_DPR 0000400 /* drive present */
134 #define DS_PGM 0001000 /* programable NI */
135 #define DS_LST 0002000 /* last sector */
136 #define DS_WRL 0004000 /* write locked */
137 #define DS_MOL 0010000 /* medium online */
138 #define DS_PIP 0020000 /* pos in progress */
139 #define DS_ERR 0040000 /* error */
140 #define DS_ATA 0100000 /* attention active */
141 #define DS_MBZ 0000076
143 /* RPER1, RMER1 - error status 1 - offset 2 */
146 #define RM_ER1_OF (2 + RM_OF)
147 #define ER1_ILF 0000001 /* illegal func */
148 #define ER1_ILR 0000002 /* illegal register */
149 #define ER1_RMR 0000004 /* reg mod refused */
150 #define ER1_PAR 0000010 /* parity err */
151 #define ER1_FER 0000020 /* format err NI */
152 #define ER1_WCF 0000040 /* write clk fail NI */
153 #define ER1_ECH 0000100 /* ECC hard err NI */
154 #define ER1_HCE 0000200 /* hdr comp err NI */
155 #define ER1_HCR 0000400 /* hdr CRC err NI */
156 #define ER1_AOE 0001000 /* addr ovflo err */
157 #define ER1_IAE 0002000 /* invalid addr err */
158 #define ER1_WLE 0004000 /* write lock err */
159 #define ER1_DTE 0010000 /* drive time err NI */
160 #define ER1_OPI 0020000 /* op incomplete */
161 #define ER1_UNS 0040000 /* drive unsafe */
162 #define ER1_DCK 0100000 /* data check NI */
164 /* RPMR, RMMR - maintenace register - offset 3*/
167 #define RM_MR_OF (3 + RM_OF)
169 /* RPAS, RMAS - attention summary - offset 4 */
172 #define RM_AS_OF (4 + RM_OF)
173 #define AS_U0 0000001 /* unit 0 flag */
175 /* RPDA, RMDA - sector/track - offset 5 */
178 #define RM_DA_OF (5 + RM_OF)
179 #define DA_V_SC 0 /* sector pos */
180 #define DA_M_SC 077 /* sector mask */
181 #define DA_V_SF 8 /* track pos */
182 #define DA_M_SF 077 /* track mask */
183 #define DA_MBZ 0140300
184 #define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC)
185 #define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF)
187 /* RPDT, RMDT - drive type - offset 6 */
190 #define RM_DT_OF (6 + RM_OF)
192 /* RPLA, RMLA - look ahead register - offset 7 */
195 #define RM_LA_OF (7 + RM_OF)
196 #define LA_V_SC 6 /* sector pos */
198 /* RPSN, RMSN - serial number - offset 8 */
201 #define RM_SN_OF (8 + RM_OF)
203 /* RPOF, RMOF - offset register - offset 9 */
206 #define RM_OF_OF (9 + RM_OF)
207 #define OF_HCI 0002000 /* hdr cmp inh NI */
208 #define OF_ECI 0004000 /* ECC inhibit NI */
209 #define OF_F22 0010000 /* format NI */
210 #define OF_MBZ 0161400
212 /* RPDC, RMDC - desired cylinder - offset 10 */
215 #define RM_DC_OF (10 + RM_OF)
216 #define DC_V_CY 0 /* cylinder pos */
217 #define DC_M_CY 01777 /* cylinder mask */
218 #define DC_MBZ 0176000
219 #define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY)
220 #define GET_DA(c,fs,d) ((((GET_CY (c) * drv_tab[d].surf) + \
221 GET_SF (fs)) * drv_tab[d].sect) + GET_SC (fs))
223 /* RPCC - current cylinder - offset 11
224 RMHR - holding register - offset 11 */
227 #define RM_HR_OF (11 + RM_OF)
229 /* RPER2 - error status 2 - drive unsafe conditions - unimplemented - offset 12
230 RMMR2 - maintenance register - unimplemented - offset 12 */
233 #define RM_MR2_OF (12 + RM_OF)
235 /* RPER3 - error status 3 - more unsafe conditions - unimplemented - offset 13
236 RMER2 - error status 2 - unimplemented - offset 13 */
239 #define RM_ER2_OF (13 + RM_OF)
241 /* RPEC1, RMEC1 - ECC status 1 - unimplemented - offset 14 */
244 #define RM_EC1_OF (14 + RM_OF)
246 /* RPEC2, RMEC1 - ECC status 2 - unimplemented - offset 15 */
249 #define RM_EC2_OF (15 + RM_OF)
251 /* This controller supports many different disk drive types:
253 type #sectors/ #surfaces/ #cylinders/
254 surface cylinder drive
256 RM02/3 32 5 823 =67MB
257 RP04/5 22 19 411 =88MB
258 RM80 31 14 559 =124MB
259 RP06 22 19 815 =176MB
260 RM05 32 19 823 =256MB
261 RP07 50 32 630 =516MB
263 In theory, each drive can be a different type. The size field in
264 each unit selects the drive capacity for each drive and thus the
265 drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE.
267 Note: the RP07, despite its designation, belongs to the RM family
274 #define RM03_DEV 020024
275 #define RM03_SIZE (RM03_SECT * RM03_SURF * RM03_CYL * RP_NUMWD)
281 #define RP04_DEV 020020
282 #define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD)
288 #define RM80_DEV 020026
289 #define RM80_SIZE (RM80_SECT * RM80_SURF * RM80_CYL * RP_NUMWD)
295 #define RP06_DEV 020022
296 #define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD)
302 #define RM05_DEV 020027
303 #define RM05_SIZE (RM05_SECT * RM05_SURF * RM05_CYL * RP_NUMWD)
309 #define RP07_DEV 020042
310 #define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD)
316 int32 sect
; /* sectors */
317 int32 surf
; /* surfaces */
318 int32 cyl
; /* cylinders */
319 int32 size
; /* #blocks */
320 int32 devtype
; /* device type */
321 int32 ctrl
; /* ctrl type */
324 static struct drvtyp drv_tab
[] = {
325 { RM03_SECT
, RM03_SURF
, RM03_CYL
, RM03_SIZE
, RM03_DEV
, RM_CTRL
},
326 { RP04_SECT
, RP04_SURF
, RP04_CYL
, RP04_SIZE
, RP04_DEV
, RP_CTRL
},
327 { RM80_SECT
, RM80_SURF
, RM80_CYL
, RM80_SIZE
, RM80_DEV
, RM_CTRL
},
328 { RP06_SECT
, RP06_SURF
, RP06_CYL
, RP06_SIZE
, RP06_DEV
, RP_CTRL
},
329 { RM05_SECT
, RM05_SURF
, RM05_CYL
, RM05_SIZE
, RM05_DEV
, RM_CTRL
},
330 { RP07_SECT
, RP07_SURF
, RP07_CYL
, RP07_SIZE
, RP07_DEV
, RM_CTRL
},
334 uint16
*rpxb
= NULL
; /* xfer buffer */
335 uint16 rpcs1
[RP_NUMDR
] = { 0 }; /* control/status 1 */
336 uint16 rpda
[RP_NUMDR
] = { 0 }; /* track/sector */
337 uint16 rpds
[RP_NUMDR
] = { 0 }; /* drive status */
338 uint16 rper1
[RP_NUMDR
] = { 0 }; /* error status 1 */
339 uint16 rmhr
[RP_NUMDR
] = { 0 }; /* holding reg */
340 uint16 rpmr
[RP_NUMDR
] = { 0 }; /* maint reg */
341 uint16 rmmr2
[RP_NUMDR
] = { 0 }; /* maint reg 2 */
342 uint16 rpof
[RP_NUMDR
] = { 0 }; /* offset */
343 uint16 rpdc
[RP_NUMDR
] = { 0 }; /* cylinder */
344 uint16 rper2
[RP_NUMDR
] = { 0 }; /* error status 2 */
345 uint16 rper3
[RP_NUMDR
] = { 0 }; /* error status 3 */
346 uint16 rpec1
[RP_NUMDR
] = { 0 }; /* ECC correction 1 */
347 uint16 rpec2
[RP_NUMDR
] = { 0 }; /* ECC correction 2 */
348 int32 rp_stopioe
= 1; /* stop on error */
349 int32 rp_swait
= 10; /* seek time */
350 int32 rp_rwait
= 10; /* rotate time */
351 static const char *rp_fname
[CS1_N_FNC
] = {
352 "NOP", "UNLD", "SEEK", "RECAL", "DCLR", "RLS", "OFFS", "RETN",
353 "PRESET", "PACK", "12", "13", "SCH", "15", "16", "17",
354 "20", "21", "22", "23", "WRCHK", "25", "26", "27",
355 "WRITE", "WRHDR", "32", "33", "READ", "RDHDR", "36", "37"
358 extern FILE *sim_deb
;
360 t_stat
rp_mbrd (int32
*data
, int32 ofs
, int32 drv
);
361 t_stat
rp_mbwr (int32 data
, int32 ofs
, int32 drv
);
362 t_stat
rp_svc (UNIT
*uptr
);
363 t_stat
rp_reset (DEVICE
*dptr
);
364 t_stat
rp_attach (UNIT
*uptr
, char *cptr
);
365 t_stat
rp_detach (UNIT
*uptr
);
366 t_stat
rp_boot (int32 unitno
, DEVICE
*dptr
);
367 void rp_set_er (int32 flg
, int32 drv
);
368 void rp_clr_as (int32 mask
);
369 void rp_update_ds (int32 flg
, int32 drv
);
370 t_stat
rp_go (int32 drv
);
371 t_stat
rp_set_size (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
372 t_stat
rp_set_bad (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
373 int32
rp_abort (void);
375 extern t_stat
pdp11_bad_block (UNIT
*uptr
, int32 sec
, int32 wds
);
377 /* RP data structures
379 rp_dev RP device descriptor
381 rp_reg RP register list
382 rp_mod RP modifier list
385 DIB rp_dib
= { MBA_RP
, 0, &rp_mbrd
, &rp_mbwr
, 0, 0, 0, { &rp_abort
} };
388 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
389 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
390 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
391 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
392 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
393 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
394 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
395 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
396 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
397 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
398 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
399 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
400 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
401 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) },
402 { UDATA (&rp_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_DISABLE
+UNIT_AUTO
+
403 UNIT_ROABLE
+(INIT_DTYPE
<< UNIT_V_DTYPE
), INIT_SIZE
) }
407 { BRDATA (CS1
, rpcs1
, DEV_RDX
, 16, RP_NUMDR
) },
408 { BRDATA (DA
, rpda
, DEV_RDX
, 16, RP_NUMDR
) },
409 { BRDATA (DS
, rpds
, DEV_RDX
, 16, RP_NUMDR
) },
410 { BRDATA (ER1
, rper1
, DEV_RDX
, 16, RP_NUMDR
) },
411 { BRDATA (HR
, rmhr
, DEV_RDX
, 16, RP_NUMDR
) },
412 { BRDATA (OF
, rpof
, DEV_RDX
, 16, RP_NUMDR
) },
413 { BRDATA (DC
, rpdc
, DEV_RDX
, 16, RP_NUMDR
) },
414 { BRDATA (ER2
, rper2
, DEV_RDX
, 16, RP_NUMDR
) },
415 { BRDATA (ER3
, rper3
, DEV_RDX
, 16, RP_NUMDR
) },
416 { BRDATA (EC1
, rpec1
, DEV_RDX
, 16, RP_NUMDR
) },
417 { BRDATA (EC2
, rpec2
, DEV_RDX
, 16, RP_NUMDR
) },
418 { BRDATA (MR
, rpmr
, DEV_RDX
, 16, RP_NUMDR
) },
419 { BRDATA (MR2
, rmmr2
, DEV_RDX
, 16, RP_NUMDR
) },
420 { DRDATA (STIME
, rp_swait
, 24), REG_NZ
+ PV_LEFT
},
421 { DRDATA (RTIME
, rp_rwait
, 24), REG_NZ
+ PV_LEFT
},
422 { URDATA (CAPAC
, rp_unit
[0].capac
, 10, T_ADDR_W
, 0,
423 RP_NUMDR
, PV_LEFT
| REG_HRO
) },
424 { FLDATA (STOP_IOE
, rp_stopioe
, 0) },
425 { GRDATA (CTRLTYPE
, rp_dib
.lnt
, DEV_RDX
, 16, 0), REG_HRO
},
430 { MTAB_XTD
|MTAB_VDV
, 0, "MASSBUS", "MASSBUS", NULL
, &mba_show_num
},
431 { UNIT_WLK
, 0, "write enabled", "WRITEENABLED", NULL
},
432 { UNIT_WLK
, UNIT_WLK
, "write locked", "LOCKED", NULL
},
433 { UNIT_DUMMY
, 0, NULL
, "BADBLOCK", &rp_set_bad
},
434 { (UNIT_DTYPE
+UNIT_ATT
), (RM03_DTYPE
<< UNIT_V_DTYPE
) + UNIT_ATT
,
435 "RM03", NULL
, NULL
},
436 { (UNIT_DTYPE
+UNIT_ATT
), (RP04_DTYPE
<< UNIT_V_DTYPE
) + UNIT_ATT
,
437 "RP04", NULL
, NULL
},
438 { (UNIT_DTYPE
+UNIT_ATT
), (RM80_DTYPE
<< UNIT_V_DTYPE
) + UNIT_ATT
,
439 "RM80", NULL
, NULL
},
440 { (UNIT_DTYPE
+UNIT_ATT
), (RP06_DTYPE
<< UNIT_V_DTYPE
) + UNIT_ATT
,
441 "RP06", NULL
, NULL
},
442 { (UNIT_DTYPE
+UNIT_ATT
), (RM05_DTYPE
<< UNIT_V_DTYPE
) + UNIT_ATT
,
443 "RM05", NULL
, NULL
},
444 { (UNIT_DTYPE
+UNIT_ATT
), (RP07_DTYPE
<< UNIT_V_DTYPE
) + UNIT_ATT
,
445 "RP07", NULL
, NULL
},
446 { (UNIT_AUTO
+UNIT_DTYPE
+UNIT_ATT
), (RM03_DTYPE
<< UNIT_V_DTYPE
),
447 "RM03", NULL
, NULL
},
448 { (UNIT_AUTO
+UNIT_DTYPE
+UNIT_ATT
), (RP04_DTYPE
<< UNIT_V_DTYPE
),
449 "RP04", NULL
, NULL
},
450 { (UNIT_AUTO
+UNIT_DTYPE
+UNIT_ATT
), (RM80_DTYPE
<< UNIT_V_DTYPE
),
451 "RM80", NULL
, NULL
},
452 { (UNIT_AUTO
+UNIT_DTYPE
+UNIT_ATT
), (RP06_DTYPE
<< UNIT_V_DTYPE
),
453 "RP06", NULL
, NULL
},
454 { (UNIT_AUTO
+UNIT_DTYPE
+UNIT_ATT
), (RM05_DTYPE
<< UNIT_V_DTYPE
),
455 "RM05", NULL
, NULL
},
456 { (UNIT_AUTO
+UNIT_DTYPE
+UNIT_ATT
), (RP07_DTYPE
<< UNIT_V_DTYPE
),
457 "RP07", NULL
, NULL
},
458 { (UNIT_AUTO
+UNIT_ATT
), UNIT_AUTO
, "autosize", NULL
, NULL
},
459 { UNIT_AUTO
, UNIT_AUTO
, NULL
, "AUTOSIZE", NULL
},
460 { (UNIT_AUTO
+UNIT_DTYPE
), (RM03_DTYPE
<< UNIT_V_DTYPE
),
461 NULL
, "RM03", &rp_set_size
},
462 { (UNIT_AUTO
+UNIT_DTYPE
), (RP04_DTYPE
<< UNIT_V_DTYPE
),
463 NULL
, "RP04", &rp_set_size
},
464 { (UNIT_AUTO
+UNIT_DTYPE
), (RM80_DTYPE
<< UNIT_V_DTYPE
),
465 NULL
, "RM80", &rp_set_size
},
466 { (UNIT_AUTO
+UNIT_DTYPE
), (RP06_DTYPE
<< UNIT_V_DTYPE
),
467 NULL
, "RP06", &rp_set_size
},
468 { (UNIT_AUTO
+UNIT_DTYPE
), (RM05_DTYPE
<< UNIT_V_DTYPE
),
469 NULL
, "RM05", &rp_set_size
},
470 { (UNIT_AUTO
+UNIT_DTYPE
), (RP07_DTYPE
<< UNIT_V_DTYPE
),
471 NULL
, "RP07", &rp_set_size
},
476 "RP", rp_unit
, rp_reg
, rp_mod
,
477 RP_NUMDR
, DEV_RDX
, 30, 1, DEV_RDX
, 16,
478 NULL
, NULL
, &rp_reset
,
479 &rp_boot
, &rp_attach
, &rp_detach
,
480 &rp_dib
, DEV_DISABLE
| DEV_UBUS
| DEV_QBUS
| DEV_MBUS
| DEV_DEBUG
483 /* Massbus register read */
485 t_stat
rp_mbrd (int32
*data
, int32 ofs
, int32 drv
)
487 uint32 val
, dtype
, i
;
490 rp_update_ds (0, drv
); /* update ds */
491 uptr
= rp_dev
.units
+ drv
; /* get unit */
492 if (uptr
->flags
& UNIT_DIS
) { /* nx disk */
496 dtype
= GET_DTYPE (uptr
->flags
); /* get drive type */
497 ofs
= ofs
& MBA_RMASK
; /* mask offset */
498 if (drv_tab
[dtype
].ctrl
== RM_CTRL
) ofs
= ofs
+ RM_OF
; /* RM? convert */
500 switch (ofs
) { /* decode offset */
502 case RP_CS1_OF
: case RM_CS1_OF
: /* RPCS1 */
503 val
= (rpcs1
[drv
] & CS1_RW
) | CS1_DVA
; /* DVA always set */
506 case RP_DA_OF
: case RM_DA_OF
: /* RPDA */
507 val
= rpda
[drv
] = rpda
[drv
] & ~DA_MBZ
;
510 case RP_DS_OF
: case RM_DS_OF
: /* RPDS */
514 case RP_ER1_OF
: case RM_ER1_OF
: /* RPER1 */
518 case RP_AS_OF
: case RM_AS_OF
: /* RPAS */
520 for (i
= 0; i
< RP_NUMDR
; i
++)
521 if (rpds
[i
] & DS_ATA
) val
|= (AS_U0
<< i
);
524 case RP_LA_OF
: case RM_LA_OF
: /* RPLA */
525 val
= GET_SECTOR (rp_rwait
, dtype
) << LA_V_SC
;
528 case RP_MR_OF
: case RM_MR_OF
: /* RPMR */
532 case RP_DT_OF
: case RM_DT_OF
: /* RPDT */
533 val
= drv_tab
[dtype
].devtype
;
536 case RP_SN_OF
: case RM_SN_OF
: /* RPSN */
537 val
= 020 | (drv
+ 1);
540 case RP_OF_OF
: case RM_OF_OF
: /* RPOF */
541 val
= rpof
[drv
] = rpof
[drv
] & ~OF_MBZ
;
544 case RP_DC_OF
: case RM_DC_OF
: /* RPDC */
545 val
= rpdc
[drv
] = rpdc
[drv
] & ~DC_MBZ
;
548 case RP_CC_OF
: /* RPCC */
549 val
= rp_unit
[drv
].CYL
;
552 case RP_ER2_OF
: case RM_ER2_OF
: /* RPER2 */
556 case RP_ER3_OF
: /* RPER3 */
560 case RP_EC1_OF
: case RM_EC1_OF
: /* RPEC1 */
564 case RP_EC2_OF
: case RM_EC2_OF
: /* RPEC2 */
568 case RM_HR_OF
: /* RMHR */
569 val
= rmhr
[drv
] ^ DMASK
;
572 case RM_MR2_OF
: /* RHMR2 */
576 default: /* all others */
585 /* Massbus register write */
587 t_stat
rp_mbwr (int32 data
, int32 ofs
, int32 drv
)
592 uptr
= rp_dev
.units
+ drv
; /* get unit */
593 if (uptr
->flags
& UNIT_DIS
) return MBE_NXD
; /* nx disk */
594 if ((ofs
!= RP_AS_OF
) && sim_is_active (uptr
)) { /* unit busy? */
595 rp_set_er (ER1_RMR
, drv
); /* won't write */
596 rp_update_ds (0, drv
);
599 rmhr
[drv
] = data
; /* save write */
600 dtype
= GET_DTYPE (uptr
->flags
); /* get drive type */
601 ofs
= ofs
& MBA_RMASK
; /* mask offset */
602 if (drv_tab
[dtype
].ctrl
== RM_CTRL
) ofs
= ofs
+ RM_OF
; /* RM? convert */
604 switch (ofs
) { /* decode PA<5:1> */
606 case RP_CS1_OF
: case RM_CS1_OF
: /* RPCS1 */
607 rpcs1
[drv
] = data
& CS1_RW
;
608 if (data
& CS1_GO
) return rp_go (drv
); /* start op */
611 case RP_DA_OF
: case RM_DA_OF
: /* RPDA */
612 rpda
[drv
] = data
& ~DA_MBZ
;
615 case RP_AS_OF
: case RM_AS_OF
: /* RPAS */
619 case RP_MR_OF
: case RM_MR_OF
: /* RPMR */
623 case RP_OF_OF
: case RM_OF_OF
: /* RPOF */
624 rpof
[drv
] = data
& ~OF_MBZ
;
627 case RP_DC_OF
: case RM_DC_OF
: /* RPDC */
628 rpdc
[drv
] = data
& ~DC_MBZ
;
631 case RM_MR2_OF
: /* RMMR2 */
635 case RP_ER1_OF
: case RM_ER1_OF
: /* RPER1 */
636 case RP_DS_OF
: case RM_DS_OF
: /* RPDS */
637 case RP_LA_OF
: case RM_LA_OF
: /* RPLA */
638 case RP_DT_OF
: case RM_DT_OF
: /* RPDT */
639 case RP_SN_OF
: case RM_SN_OF
: /* RPSN */
640 case RP_CC_OF
: /* RPCC */
641 case RP_ER2_OF
: case RM_ER2_OF
: /* RPER2 */
642 case RP_ER3_OF
: /* RPER3 */
643 case RP_EC1_OF
: case RM_EC1_OF
: /* RPEC1 */
644 case RP_EC2_OF
: case RM_EC2_OF
: /* RPEC2 */
645 case RM_HR_OF
: /* RMHR */
646 break; /* read only */
648 default: /* all others */
652 rp_update_ds (0, drv
); /* update status */
656 /* Initiate operation - unit not busy, function set */
658 t_stat
rp_go (int32 drv
)
660 int32 dc
, fnc
, dtype
, t
;
663 fnc
= GET_FNC (rpcs1
[drv
]); /* get function */
664 if (DEBUG_PRS (rp_dev
)) fprintf (sim_deb
,
665 ">>RP%d STRT: fnc=%s, ds=%o, cyl=%o, da=%o, er=%o\n",
666 drv
, rp_fname
[fnc
], rpds
[drv
], rpdc
[drv
], rpda
[drv
], rper1
[drv
]);
667 uptr
= rp_dev
.units
+ drv
; /* get unit */
668 rp_clr_as (AS_U0
<< drv
); /* clear attention */
669 dtype
= GET_DTYPE (uptr
->flags
); /* get drive type */
670 dc
= rpdc
[drv
]; /* assume seek, sch */
671 if ((fnc
!= FNC_DCLR
) && (rpds
[drv
] & DS_ERR
)) { /* err & ~clear? */
672 rp_set_er (ER1_ILF
, drv
); /* not allowed */
673 rp_update_ds (DS_ATA
, drv
); /* set attention */
677 switch (fnc
) { /* case on function */
679 case FNC_DCLR
: /* drive clear */
680 rper1
[drv
] = rper2
[drv
] = rper3
[drv
] = 0; /* clear errors */
681 rpec2
[drv
] = 0; /* clear EC2 */
682 if (drv_tab
[dtype
].ctrl
== RM_CTRL
) /* RM? */
683 rpmr
[drv
] = 0; /* clear maint */
684 else rpec1
[drv
] = 0; /* RP, clear EC1 */
685 case FNC_NOP
: /* no operation */
686 case FNC_RELEASE
: /* port release */
689 case FNC_PRESET
: /* read-in preset */
690 rpdc
[drv
] = 0; /* clear disk addr */
692 rpof
[drv
] = 0; /* clear offset */
693 case FNC_PACK
: /* pack acknowledge */
694 rpds
[drv
] = rpds
[drv
] | DS_VV
; /* set volume valid */
697 case FNC_OFFSET
: /* offset mode */
699 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not attached? */
700 rp_set_er (ER1_UNS
, drv
); /* unsafe */
703 rpds
[drv
] = (rpds
[drv
] & ~DS_RDY
) | DS_PIP
; /* set positioning */
704 sim_activate (uptr
, rp_swait
); /* time operation */
707 case FNC_UNLOAD
: /* unload */
708 case FNC_RECAL
: /* recalibrate */
709 dc
= 0; /* seek to 0 */
710 case FNC_SEEK
: /* seek */
711 case FNC_SEARCH
: /* search */
712 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not attached? */
713 rp_set_er (ER1_UNS
, drv
); /* unsafe */
716 if ((GET_CY (dc
) >= drv_tab
[dtype
].cyl
) || /* bad cylinder */
717 (GET_SF (rpda
[drv
]) >= drv_tab
[dtype
].surf
) || /* bad surface */
718 (GET_SC (rpda
[drv
]) >= drv_tab
[dtype
].sect
)) { /* or bad sector? */
719 rp_set_er (ER1_IAE
, drv
);
722 rpds
[drv
] = (rpds
[drv
] & ~DS_RDY
) | DS_PIP
; /* set positioning */
723 t
= abs (dc
- uptr
->CYL
); /* cyl diff */
724 if (t
== 0) t
= 1; /* min time */
725 sim_activate (uptr
, rp_swait
* t
); /* schedule */
726 uptr
->CYL
= dc
; /* save cylinder */
729 case FNC_WRITEH
: /* write headers */
730 case FNC_WRITE
: /* write */
731 case FNC_WCHK
: /* write check */
732 case FNC_READ
: /* read */
733 case FNC_READH
: /* read headers */
734 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not attached? */
735 rp_set_er (ER1_UNS
, drv
); /* unsafe */
738 if ((GET_CY (dc
) >= drv_tab
[dtype
].cyl
) || /* bad cylinder */
739 (GET_SF (rpda
[drv
]) >= drv_tab
[dtype
].surf
) || /* bad surface */
740 (GET_SC (rpda
[drv
]) >= drv_tab
[dtype
].sect
)) { /* or bad sector? */
741 rp_set_er (ER1_IAE
, drv
);
744 rpds
[drv
] = rpds
[drv
] & ~DS_RDY
; /* clear drive rdy */
745 sim_activate (uptr
, rp_rwait
+ (rp_swait
* abs (dc
- uptr
->CYL
)));
746 uptr
->CYL
= dc
; /* save cylinder */
749 default: /* all others */
750 rp_set_er (ER1_ILF
, drv
); /* not supported */
754 rp_update_ds (DS_ATA
, drv
); /* set attn, req int */
758 /* Abort opertion - there is a data transfer in progress */
760 int32
rp_abort (void)
762 return rp_reset (&rp_dev
);
765 /* Service unit timeout
767 Complete movement or data transfer command
768 Unit must exist - can't remove an active unit
769 Unit must be attached - detach cancels in progress operations
772 t_stat
rp_svc (UNIT
*uptr
)
774 int32 i
, fnc
, dtype
, drv
, err
;
775 int32 wc
, abc
, awc
, mbc
, da
;
777 dtype
= GET_DTYPE (uptr
->flags
); /* get drive type */
778 drv
= (int32
) (uptr
- rp_dev
.units
); /* get drv number */
779 da
= GET_DA (rpdc
[drv
], rpda
[drv
], dtype
) * RP_NUMWD
; /* get disk addr */
780 fnc
= GET_FNC (rpcs1
[drv
]); /* get function */
782 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not attached? */
783 rp_set_er (ER1_UNS
, drv
); /* set drive error */
784 if (fnc
>= FNC_XFER
) mba_set_don (rp_dib
.ba
); /* xfr? set done */
785 rp_update_ds (DS_ATA
, drv
); /* set attn */
786 return (rp_stopioe
? SCPE_UNATT
: SCPE_OK
);
788 rpds
[drv
] = (rpds
[drv
] & ~DS_PIP
) | DS_RDY
; /* change drive status */
790 switch (fnc
) { /* case on function */
792 case FNC_OFFSET
: /* offset */
793 rp_update_ds (DS_OFM
| DS_ATA
, drv
);
796 case FNC_RETURN
: /* return to centerline */
797 rpds
[drv
] = rpds
[drv
] & ~DS_OFM
; /* clear offset, set attn */
798 rp_update_ds (DS_ATA
, drv
);
801 case FNC_UNLOAD
: /* unload */
802 rp_detach (uptr
); /* detach unit */
805 case FNC_RECAL
: /* recalibrate */
806 case FNC_SEARCH
: /* search */
807 case FNC_SEEK
: /* seek */
808 rp_update_ds (DS_ATA
, drv
);
811 case FNC_WRITE
: /* write */
812 if (uptr
->flags
& UNIT_WPRT
) { /* write locked? */
813 rp_set_er (ER1_WLE
, drv
); /* set drive error */
814 mba_set_exc (rp_dib
.ba
); /* set exception */
815 rp_update_ds (DS_ATA
, drv
); /* set attn */
818 case FNC_WCHK
: /* write check */
819 case FNC_READ
: /* read */
820 case FNC_READH
: /* read headers */
821 err
= fseek (uptr
->fileref
, da
* sizeof (int16
), SEEK_SET
);
822 mbc
= mba_get_bc (rp_dib
.ba
); /* get byte count */
823 wc
= (mbc
+ 1) >> 1; /* convert to words */
824 if ((da
+ wc
) > drv_tab
[dtype
].size
) { /* disk overrun? */
825 rp_set_er (ER1_AOE
, drv
); /* set err */
826 wc
= drv_tab
[dtype
].size
- da
; /* trim xfer */
827 mbc
= wc
<< 1; /* trim mb count */
828 if (da
>= drv_tab
[dtype
].size
) { /* none left? */
829 mba_set_exc (rp_dib
.ba
); /* set exception */
830 rp_update_ds (DS_ATA
, drv
); /* set attn */
834 if (fnc
== FNC_WRITE
) { /* write? */
835 abc
= mba_rdbufW (rp_dib
.ba
, mbc
, rpxb
); /* get buffer */
836 wc
= (abc
+ 1) >> 1; /* actual # wds */
837 awc
= (wc
+ (RP_NUMWD
- 1)) & ~(RP_NUMWD
- 1);
838 for (i
= wc
; i
< awc
; i
++) rpxb
[i
] = 0; /* fill buf */
839 if (wc
&& !err
) { /* write buf */
840 fxwrite (rpxb
, sizeof (uint16
), awc
, uptr
->fileref
);
841 err
= ferror (uptr
->fileref
);
844 else { /* read or wchk */
845 awc
= fxread (rpxb
, sizeof (uint16
), wc
, uptr
->fileref
);
846 err
= ferror (uptr
->fileref
);
847 for (i
= awc
; i
< wc
; i
++) rpxb
[i
] = 0; /* fill buf */
848 if (fnc
== FNC_WCHK
) /* write check? */
849 mba_chbufW (rp_dib
.ba
, mbc
, rpxb
); /* check vs mem */
850 else mba_wrbufW (rp_dib
.ba
, mbc
, rpxb
); /* store in mem */
852 da
= da
+ wc
+ (RP_NUMWD
- 1);
853 if (da
>= drv_tab
[dtype
].size
) rpds
[drv
] = rpds
[drv
] | DS_LST
;
855 rpda
[drv
] = da
% drv_tab
[dtype
].sect
;
856 da
= da
/ drv_tab
[dtype
].sect
;
857 rpda
[drv
] = rpda
[drv
] | ((da
% drv_tab
[dtype
].surf
) << DA_V_SF
);
858 rpdc
[drv
] = da
/ drv_tab
[dtype
].surf
;
859 uptr
->CYL
= rpdc
[drv
];
861 if (err
!= 0) { /* error? */
862 rp_set_er (ER1_PAR
, drv
); /* set drive error */
863 mba_set_exc (rp_dib
.ba
); /* set exception */
864 rp_update_ds (DS_ATA
, drv
);
865 perror ("RP I/O error");
866 clearerr (uptr
->fileref
);
870 case FNC_WRITEH
: /* write headers stub */
871 mba_set_don (rp_dib
.ba
); /* set done */
872 rp_update_ds (0, drv
); /* update ds */
874 } /* end case func */
876 if (DEBUG_PRS (rp_dev
)) fprintf (sim_deb
,
877 ">>RP%d DONE: fnc=%s, ds=%o, cyl=%o, da=%o, er=%d\n",
878 drv
, rp_fname
[fnc
], rpds
[drv
], rpdc
[drv
], rpda
[drv
], rper1
[drv
]);
882 /* Set drive error */
884 void rp_set_er (int32 flag
, int32 drv
)
886 rper1
[drv
] = rper1
[drv
] | flag
;
887 rpds
[drv
] = rpds
[drv
] | DS_ATA
;
888 mba_upd_ata (rp_dib
.ba
, 1);
892 /* Clear attention flags */
894 void rp_clr_as (int32 mask
)
898 for (i
= as
= 0; i
< RP_NUMDR
; i
++) {
899 if (mask
& (AS_U0
<< i
)) rpds
[i
] &= ~DS_ATA
;
900 if (rpds
[i
] & DS_ATA
) as
= 1;
902 mba_upd_ata (rp_dib
.ba
, as
);
906 /* Drive status update */
908 void rp_update_ds (int32 flag
, int32 drv
)
910 if (rp_unit
[drv
].flags
& UNIT_DIS
) rpds
[drv
] = rper1
[drv
] = 0;
911 else rpds
[drv
] = (rpds
[drv
] | DS_DPR
) & ~DS_PGM
;
912 if (rp_unit
[drv
].flags
& UNIT_ATT
) rpds
[drv
] = rpds
[drv
] | DS_MOL
;
913 else rpds
[drv
] = rpds
[drv
] & ~(DS_MOL
| DS_VV
| DS_RDY
);
914 if (rper1
[drv
] | rper2
[drv
] | rper3
[drv
]) rpds
[drv
] = rpds
[drv
] | DS_ERR
;
915 else rpds
[drv
] = rpds
[drv
] & ~DS_ERR
;
916 rpds
[drv
] = rpds
[drv
] | flag
;
917 if (flag
& DS_ATA
) mba_upd_ata (rp_dib
.ba
, 1);
923 t_stat
rp_reset (DEVICE
*dptr
)
928 mba_set_enbdis (MBA_RP
, rp_dev
.flags
& DEV_DIS
);
929 for (i
= 0; i
< RP_NUMDR
; i
++) {
930 uptr
= rp_dev
.units
+ i
;
933 if (uptr
->flags
& UNIT_ATT
) rpds
[i
] = (rpds
[i
] & DS_VV
) |
934 DS_DPR
| DS_RDY
| DS_MOL
| ((uptr
->flags
& UNIT_WPRT
)? DS_WRL
: 0);
935 else if (uptr
->flags
& UNIT_DIS
) rpds
[i
] = 0;
936 else rpds
[i
] = DS_DPR
;
950 if (rpxb
== NULL
) rpxb
= (uint16
*) calloc (RP_MAXFR
, sizeof (uint16
));
951 if (rpxb
== NULL
) return SCPE_MEM
;
957 t_stat
rp_attach (UNIT
*uptr
, char *cptr
)
962 uptr
->capac
= drv_tab
[GET_DTYPE (uptr
->flags
)].size
;
963 r
= attach_unit (uptr
, cptr
); /* attach unit */
964 if (r
!= SCPE_OK
) return r
; /* error? */
965 drv
= (int32
) (uptr
- rp_dev
.units
); /* get drv number */
966 rpds
[drv
] = DS_MOL
| DS_RDY
| DS_DPR
| /* upd drv status */
967 ((uptr
->flags
& UNIT_WPRT
)? DS_WRL
: 0);
969 rp_update_ds (DS_ATA
, drv
); /* upd ctlr status */
971 if ((p
= sim_fsize (uptr
->fileref
)) == 0) { /* new disk image? */
972 if (uptr
->flags
& UNIT_RO
) return SCPE_OK
;
973 return pdp11_bad_block (uptr
,
974 drv_tab
[GET_DTYPE (uptr
->flags
)].sect
, RP_NUMWD
);
976 if ((uptr
->flags
& UNIT_AUTO
) == 0) return SCPE_OK
; /* autosize? */
977 for (i
= 0; drv_tab
[i
].sect
!= 0; i
++) {
978 if (p
<= (drv_tab
[i
].size
* (int) sizeof (int16
))) {
979 uptr
->flags
= (uptr
->flags
& ~UNIT_DTYPE
) | (i
<< UNIT_V_DTYPE
);
980 uptr
->capac
= drv_tab
[i
].size
;
989 t_stat
rp_detach (UNIT
*uptr
)
993 if (!(uptr
->flags
& UNIT_ATT
)) return SCPE_OK
; /* attached? */
994 drv
= (int32
) (uptr
- rp_dev
.units
); /* get drv number */
995 rpds
[drv
] = rpds
[drv
] & ~(DS_MOL
| DS_RDY
| DS_WRL
| DS_VV
| DS_OFM
);
996 rp_update_ds (DS_ATA
, drv
); /* request intr */
997 return detach_unit (uptr
);
1000 /* Set size command validation routine */
1002 t_stat
rp_set_size (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
1004 int32 dtype
= GET_DTYPE (val
);
1006 if (uptr
->flags
& UNIT_ATT
) return SCPE_ALATT
;
1007 uptr
->capac
= drv_tab
[dtype
].size
;
1011 /* Set bad block routine */
1013 t_stat
rp_set_bad (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
1015 return pdp11_bad_block (uptr
, drv_tab
[GET_DTYPE (uptr
->flags
)].sect
, RP_NUMWD
);
1020 #if defined (VM_PDP11)
1022 #define BOOT_START 02000 /* start */
1023 #define BOOT_ENTRY (BOOT_START + 002) /* entry */
1024 #define BOOT_UNIT (BOOT_START + 010) /* unit number */
1025 #define BOOT_CSR (BOOT_START + 014) /* CSR */
1026 #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16))
1028 static const uint16 boot_rom
[] = {
1030 0012706, BOOT_START
, /* mov #boot_start, sp */
1031 0012700, 0000000, /* mov #unit, r0 */
1032 0012701, 0176700, /* mov #RPCS1, r1 */
1033 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */
1034 0010061, 0000010, /* mov r0, 10(r1) ; set unit */
1035 0012711, 0000021, /* mov #RIP+GO, (r1) ; pack ack */
1036 0012761, 0010000, 0000032, /* mov #FMT16B, 32(r1) ; 16b mode */
1037 0012761, 0177000, 0000002, /* mov #-512., 2(r1) ; set wc */
1038 0005061, 0000004, /* clr 4(r1) ; clr ba */
1039 0005061, 0000006, /* clr 6(r1) ; clr da */
1040 0005061, 0000034, /* clr 34(r1) ; clr cyl */
1041 0012711, 0000071, /* mov #READ+GO, (r1) ; read */
1042 0105711, /* tstb (r1) ; wait */
1043 0100376, /* bpl .-2 */
1044 0005002, /* clr R2 */
1045 0005003, /* clr R3 */
1046 0012704, BOOT_START
+020, /* mov #start+020, r4 */
1047 0005005, /* clr R5 */
1048 0105011, /* clrb (r1) */
1049 0005007 /* clr PC */
1052 t_stat
rp_boot (int32 unitno
, DEVICE
*dptr
)
1055 extern int32 saved_PC
;
1057 UNIT
*uptr
= rp_dev
.units
+ unitno
;
1059 for (i
= 0; i
< BOOT_LEN
; i
++) M
[(BOOT_START
>> 1) + i
] = boot_rom
[i
];
1060 M
[BOOT_UNIT
>> 1] = unitno
& (RP_NUMDR
- 1);
1061 M
[BOOT_CSR
>> 1] = mba_get_csr (rp_dib
.ba
) & DMASK
;
1062 if (drv_tab
[GET_DTYPE (uptr
->flags
)].ctrl
== RP_CTRL
)
1063 M
[BOOT_START
>> 1] = 042102; /* "BD" */
1064 else M
[BOOT_START
>> 1] = 042122; /* "RD" */
1065 saved_PC
= BOOT_ENTRY
;
1071 t_stat
rp_boot (int32 unitno
, DEVICE
*dptr
)