PDP11: RP11: Implement original RP11 controller flavor

Studying RSX11M source code I found out that the original RP11
controller (vs. -C and -E later versions) was still supported
in the software, and notably UMD (User Mode Diagnostics) was
still capable of handing it and dealing with its testing,
even in much later OS versions (compared to when the original
RP11 was phased out).

The change in this commit basically implements that flavor
(which only supported the RP02 drives), and also makes a few
minor fixes / cleanups for the device code, here and there.

The default mode for the RP11 controller remains the more widely
used -C/-E version, but if so desired, it can be downgraded to
support its predecessor with the "SET RR <type>" command now.
This commit is contained in:
Tony Lawrence 2024-09-02 16:41:40 -04:00 committed by Paul Koning
parent 0e83a37a99
commit c093bcdcf6

View file

@ -1,4 +1,4 @@
/* pdp11_rr.c: RP11/-C/-E/RP02/RP03 disk pack device
/* pdp11_rr.c: RP11/-C/-E/RP02/RPR02/RP03 disk pack device
Copyright (c) 2022 Tony Lawrence
@ -48,7 +48,7 @@
#define MAP_RDW(a,c,b) (Map_ReadW (a, (c) << 1, b) >> 1)
#define MAP_WRW(a,c,b) (Map_WriteW(a, (c) << 1, b) >> 1)
/* RP02 parameters; RP03 doubles # of cylinders (both total and spare) */
/* RP02/RPR02 parameters; RP03 doubles # of cylinders (both total and spare) */
#define RP_NUMWD 256 /* words/sector */
#define RP_NUMCY 203 /* cylinders/drive */
#define RP_SPARE 3 /* of those, spare */
@ -63,14 +63,21 @@
#define RP_SIZE_RP03 RP_SIZE(RP_NUMBL*2) /* RP03 capacity, words */
#define RP_ROT_12 125 /* Half rotation, 0.1ms */
#define GET_DA(c,h,s) (((c) * RP_NUMSF + (h)) * RP_NUMSC + (s)) /* disk addr */
/* Flags in the unit flags word */
#define UNIT_NOAUTO DKUF_NOAUTOSIZE /* autosize disabled */
#define UNIT_V_RP03 (DKUF_V_UF + 0)
#define UNIT_RP03 (1 << UNIT_V_RP03) /* RP03 vs RP02 (0) */
#define GET_DTYPE(x) (((x) & UNIT_RP03) >> UNIT_V_RP03)
/* Flags in the device flags word */
#define DEV_V_RP11CE (DEV_V_FFUF + 0)
#define DEV_RP11CE (1 << DEV_V_RP11CE) /* RP11-C/-E vs RP11(0) */
/* Controller / drive types */
#define RP_RP11 "RP11-C"
#define RP_RP11 "RP11"
#define RP_RP11CE "RP11-C/-E"
#define RP_RP02 "RP02"
#define RP_RP03 "RP03"
@ -86,7 +93,7 @@
/* 12 real UNIBUS registers, so 16 registers total: 32 addresses */
#define RP_IOLN 040
/* RP02/RP03 particulars */
/* RP(R)02/RP03 particulars (all disks rotated at 2400rpm) */
static struct drv_typ {
const char* name; /* device type name */
int32 cyl; /* cylinders */
@ -176,7 +183,6 @@ static BITFIELD rp_er_bits[] = {
static const char* rp_funcs[] = {
"RESET", "WRITE", "READ", "WCHK", "SEEK", "WRNOSEEK", "HOME", "RDNOSEEK"
};
static BITFIELD rp_cs_bits[] = {
/* CSR_GO */ /* the GO! bit */
BIT(GO),
@ -238,14 +244,24 @@ static BITFIELD rp_ba_bits[] = {
/* RPCA 776722, cylinder address */
static BITFIELD rp_ca_bits[] = {
#define RPCA_IMP 0000777 /* implemented */
#define RPCA_IMP 0177777 /* implemented */
#define RPCA_RW 0000777 /* RP11: 0377 */
BITFFMT(CYL,9,%u),
ENDBITS
};
static BITFIELD rp_ca11_bits[] = { /* RP11 version */
#define RPCA_M_CYL 0000377
BITFFMT(CYL,8,%u),
#define RPCA_V_SUCA 8
#define RPCA_M_SUCA 0000377
#define RPCA_SUCA (RPCA_M_SUCA << RPCA_V_SUCA)
BITFFMT(SUCA,8,%u),
ENDBITS
};
/* RPDA 776724, disk address (track/sector) */
static BITFIELD rp_da_bits[] = {
#define RPDA_IMPL 0017777 /* implemented */
#define RPDA_IMP 0017777 /* implemented */
#define RPDA_RW 0017417 /* bits here */
#define RPDA_M_SECT 017
#define RPDA_SECT RPDA_M_SECT /* sector */
@ -259,7 +275,6 @@ static BITFIELD rp_da_bits[] = {
BITFFMT(SURF,5,%u),
#define GET_SECT(x) ((x) & RPDA_SECT)
#define GET_TRACK(x) (((x) & RPDA_TRACK) >> RPDA_V_TRACK)
#define GET_DA(c,h,s) (((c) * RP_NUMSF + (h)) * RP_NUMSC + (s))
ENDBITS
};
@ -271,7 +286,8 @@ static BITFIELD rp_da_bits[] = {
/* SUCA 776734 selected unit cylinder address, read-only */
static BITFIELD rp_suca_bits[] = {
BITFFMT(CYL,9,%u),
#define SUCA_IMP 0000777 /* RP11: 0377 */
BITFFMT(CYL,9,%u), /* RP11: RPCA<15:08> */
ENDBITS
};
@ -280,14 +296,16 @@ static BITFIELD rp_suca_bits[] = {
/* Maintenance Write Lockout Address (LOA) (the switches on the maint. panel) */
static const char* offon[] = { "OFF", "ON" };
static BITFIELD rp_wloa_bits[] = {
#define RPWLOA_IMPL 03777
#define RPWLOA_CYL2 0377 /* cyls locked (x2+1) */
BITFFMT(CYL2,8,%u),
#define RPWLOA_IMP 03777
#define RPWLOA_CYL 0377 /* cyls locked */
BITFFMT(CYL,8,%u),
#define RPWLOA_V_DRV 8
#define RPWLOA_M_DRV 7
#define RPWLOA_DRV (RPWLOA_M_DRV << RPWLOA_V_DRV) /* drive(s) locked */
#define RPWLOA_DRV (RPWLOA_M_DRV << RPWLOA_V_DRV) /* drives locked */
BITFFMT(DRV,3,%u),
#define GET_WLOACYL(x) ((((x) & RPWLOA_CYL2) << 1) | 1)
#define GET_WLOACYL(x) (rr_dev.flags & DEV_RP11CE \
? (((x) & RPWLOA_CYL) << 1) | 1 /* x2 + 1 */ \
: ((x) & RPWLOA_CYL))
#define GET_WLOADRV(x) (((x) & RPWLOA_DRV) >> RPWLOA_V_DRV)
BITNCF(4),
#define RPWLOA_ON 0100000
@ -364,6 +382,7 @@ static t_stat rr_detach (UNIT *uptr);
static t_stat rr_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat rr_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat rr_set_wloa (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat rr_set_ctrl (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat rr_show_ctrl (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static const char *rr_description (DEVICE *dptr);
@ -392,7 +411,7 @@ static REG rr_reg[] = {
{ ORDATADF(RPBA, rpba, 16, "memory address", rp_ba_bits) },
{ ORDATADF(RPCA, rpca, 16, "cylinder address", rp_ca_bits) },
{ ORDATADF(RPDA, rpda, 16, "disk address", rp_da_bits) },
{ ORDATADF(SUCA, suca, 16, "current cylinder", rp_suca_bits) },
{ ORDATADF(SUCA, suca, 16, "current cylinder", rp_suca_bits), REG_RO },
{ ORDATADF(WLOA, wloa, 16, "write lockout address", rp_wloa_bits) },
/* standard stuff */
@ -437,6 +456,14 @@ static MTAB rr_mod[] = {
"TYPE", NULL,
NULL, rr_show_ctrl, NULL,
"Display controller type" },
{ MTAB_VDV, 0/*RP11*/,
NULL, RP_RP11,
rr_set_ctrl, NULL, NULL,
"Set " RP_RP11 " controller type" },
{ MTAB_VDV, DEV_RP11CE,
NULL, "RP11CE",
rr_set_ctrl, NULL, NULL,
"Set " RP_RP11CE " controller type" },
{ MTAB_VDV | MTAB_VALR, 0,
NULL, "PROTECT",
rr_set_wloa, NULL, NULL,
@ -460,7 +487,7 @@ static MTAB rr_mod[] = {
{ MTAB_VUN, UNIT_RP03,
NULL, RP_RP03,
rr_set_type, NULL, NULL,
"Set " RP_RP03 " disk type"},
"Set " RP_RP03 " disk type (only " RP_RP11CE ")"},
{ UNIT_NOAUTO, 0,
"autosize", "AUTOSIZE",
NULL, NULL, NULL,
@ -473,7 +500,7 @@ static MTAB rr_mod[] = {
"FORMAT", "FORMAT={AUTO|SIMH|VHD|RAW}",
sim_disk_set_fmt, sim_disk_show_fmt, NULL,
"Set/Display disk format" },
{ MTAB_VDV | MTAB_VALR, 010,
{ MTAB_VDV | MTAB_VALR, RP_IOLN/*modulus*/,
"ADDRESS", "ADDRESS",
set_addr, show_addr, NULL,
"Bus address" },
@ -491,11 +518,11 @@ DEVICE rr_dev = {
NULL/*examine()*/, NULL/*deposit()*/,
rr_reset, rr_boot, rr_attach, rr_detach,
&rr_dib,
DEV_DIS | DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG | DEV_DISK,
DEV_DIS | DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG | DEV_DISK | DEV_RP11CE,
0/*debug control*/, rr_deb,
NULL/*msize()*/, NULL/*logical name*/,
rr_help, NULL/*attach_help()*/, NULL/*help_ctx*/,
rr_description,
rr_description
};
/* I/O dispatch routine, I/O addresses 17776710 - 17776736
@ -513,25 +540,30 @@ DEVICE rr_dev = {
17776734 SUCA read-only
17776736 SILO read/write, unimplemented
http://www.bitsavers.org/pdf/dec/unibus/RP11-C_schemMay1973.pdf
http://www.bitsavers.org/pdf/dec/unibus/DEC-11-HRPCA-C-D_RP11-C_Maint_Aug74.pdf
RP11-C actually responds to the range 17776700 - 17776736 with the first 4 word
locations unused.
Some operating systems want you to specify the latter range (RSTS/E), but some
just want to know where the CSR is located, so they auto-calculate the range.
The original RP11 had the following differences: it responded to the address
range 17776710 - 17776746: RPM3 was followed by 3 buffer registers RPB1-RPB3,
The _original_ RP11 had the following differences: it responded to the address
range 17776710 - 17776746: RPM3 was followed by 3 buffer registers RPB1 - RPB3,
then 3 locations (42-46) were unused. RPCA was both the cylinder address in the
lower 8 bits <00:07> (read-write), and the Selected Unit current cylinder address
(a la SUCA in RP11-C) in the higher 8 bits <08:15> (read-only). Since only the
lower 8 bits <07:00> (read-write), and the Selected Unit current cylinder address
(a la SUCA in RP11-C) in the higher 8 bits <15:08> (read-only). Since only the
RP02 disk drives were supported, it only required 8 bits for cylinder addresses.
There was no separate SUCA register (the location was occupied by RPB1). The
RP03 bit in RPDS was always 0. But programmatically it was mostly compatible
with the -C revision (except for the SU current cylinder address location, which
was not used by and/or important to the major part of software, anyways).
There were no separate SUCA/SILO registers (the locations were occupied by RPBs).
The RP03 bit in RPDS was always 0. But programmatically RP11 was mostly compatible
with the -C revision (except for the SU current cylinder address, which was not
widely used by major software, anyways).
RP11-E was just a newer version of RP11-C and supported both RP02 and RP03 disk
drives on the same controller.
http://www.bitsavers.org/pdf/dec/unibus/RP11_schem_Sep74.pdf
RP11-E was just a newer version of RP11-C and supported both RP02(or RPR02) and
RP03 disk drives on the same controller.
*/
@ -539,6 +571,7 @@ static t_stat rr_rd (int32 *data, int32 PA, int32 access)
{
/* offset by base then decode <4:1> */
int32 rn = (((PA - rr_dib.ba) & (RP_IOLN - 1)) >> 1) - RP_IOFF;
BITFIELD* bits = 0;
UNIT* uptr;
switch (rn) {
@ -585,24 +618,33 @@ static t_stat rr_rd (int32 *data, int32 PA, int32 access)
case 5: /* RPCA */
*data = rpca;
if (!(rr_dev.flags & DEV_RP11CE)) {
assert(!(suca & ~RPCA_M_SUCA));
*data |= suca << RPCA_V_SUCA;
bits = rp_ca11_bits;
}
break;
case 6: /* RPDA */
rpda &= RPDA_RW;
rpda |= (rand() % RP_NUMSC) << RPDA_V_SOT; /* inject a random sect */
uptr = rr_dev.units + GET_DRIVE(rpcs);
if (uptr->flags & UNIT_ATT)
rpda |= (rand() % RP_NUMSC) << RPDA_V_SOT; /* inject a random sect */
*data = rpda;
break;
case 10: /* SUCA */
*data = suca;
break;
if (rr_dev.flags & DEV_RP11CE) {
*data = suca;
break;
}
/* FALLTHRU */
default: /* not implemented */
*data = 0;
return SCPE_OK;
}
sim_debug(RRDEB_RRD, &rr_dev, ">>RR read: %s=%#o\n", rr_regs[rn].name, *data);
sim_debug_bits(RRDEB_RRD, &rr_dev, rr_regs[rn].bits, *data, *data, 1);
sim_debug_bits(RRDEB_RRD, &rr_dev, bits ? bits : rr_regs[rn].bits, *data, *data, 1);
return SCPE_OK;
}
@ -671,7 +713,8 @@ static t_stat rr_wr (int32 data, int32 PA, int32 access)
break;
case 5: /* RPCA */
rpca = data & RPCA_IMP;
const_assert(RPCA_M_CYL == (RPCA_RW >> 1));
rpca = data & (rr_dev.flags & DEV_RP11CE ? RPCA_RW : RPCA_M_CYL);
break;
case 6: /* RPDA */
@ -680,8 +723,9 @@ static t_stat rr_wr (int32 data, int32 PA, int32 access)
break;
case 10: /* SUCA: read-only */
break;
if (rr_dev.flags & DEV_RP11CE)
break;
/*FALLTHRU*/
default:
return SCPE_OK;
}
@ -706,7 +750,7 @@ static t_stat rr_seek_init (UNIT *uptr)
static void rr_go (int16 func)
{
int32 i, type, cyl, head;
int32 i, n, type, cyl, head;
t_bool rd, wr;
UNIT* uptr;
@ -808,9 +852,10 @@ static void rr_go (int16 func)
}
/* seek time */
n = cyl - uptr->CYL; /* cyl departure */
if (func == RPCS_HOME)
i = drv_tab[type].seek_ave / 2;
else if (!(i = abs(cyl - uptr->CYL)))
else if (!(i = abs(n)))
i = drv_tab[type].seek_1 / 2;
else if (i <= 2)
i *= drv_tab[type].seek_1;
@ -821,6 +866,7 @@ static void rr_go (int16 func)
if (func == RPCS_HOME || func == RPCS_SEEK) { /* seek? */
uptr->action = rr_seek_init;
uptr->SEEKING = i; /* drive is seeking */
assert(i);
i = 10; /* enough for 16us */
/* XXDP ZRPF-B, Test 0 (cylinder positioning) fails because, even though
* it initializes INTFLG late (_after_ initiating the SEEK), the delayed
@ -842,6 +888,7 @@ static void rr_go (int16 func)
uptr->STATUS |= RPDS_SEEK; /* drive is seeking */
}
i += RP_ROT_12; /* I/O takes longer */
assert(i);
/* XXDP ZRPB-E / ZRPF-B have two data race conditions in Test 5 (data
* reliability), which in ZRPB-E can be worked around with the following
* multiplier for all 15 steady patterns, but it does not help eliminate
@ -854,9 +901,11 @@ static void rr_go (int16 func)
}
sim_activate(uptr, i); /* schedule */
assert(suca == uptr->CYL); /* actually */
uptr->FUNC = func; /* save new func */
uptr->HEAD = head; /* save head too */
uptr->CYL = cyl; /* put on cylinder */
suca += n >> 2; /* show motion */
return;
}
@ -906,6 +955,7 @@ static t_stat rr_svc (UNIT *uptr)
uptr->FUNC = 0; /* idle */
rr_seek_done(uptr, 0); /* complete seek, if any */
assert(0 < func && func < sizeof(rp_funcs)/sizeof(rp_funcs[0]));
assert(!uptr->SEEKING && !(uptr->STATUS & RPDS_SEEK));
if (func == RPCS_HOME || func == RPCS_SEEK)
return SCPE_OK; /* all done */
@ -936,8 +986,8 @@ static t_stat rr_svc (UNIT *uptr)
&& (n <= GET_WLOADRV(wloa) || cyl <= GET_WLOACYL(wloa))) {
uptr->STATUS |= RPDS_WLK; /* DA write-locked */
rper |= RPER_WPV;
} else if (uptr->flags & UNIT_WPRT) /* write and locked? */
rper |= RPER_WPV;
} else if (uptr->flags & UNIT_WPRT)
rper |= RPER_WPV; /* unit write-locked */
}
if (rper) { /* control in error? */
@ -1121,14 +1171,13 @@ static t_stat rr_svc (UNIT *uptr)
todo = head / RP_NUMSF; /* new cyl (tentative) */
if (todo == drv_tab[n].cyl) { /* at the end? */
cyl = drv_tab[n].cyl - 1; /* keep on last cyl */
todo = 0; /* wrap cyl for rpda */
todo = 0; /* wrap cyl for rpda... */
head = 0; /* ...and head, too */
assert(!sect);
} else {
cyl = todo; /* new cyl */
head %= RP_NUMSF; /* isolate head */
}
uptr->HEAD = head; /* update head */
if ((func == RPCS_RD_NOSEEK || func == RPCS_WR_NOSEEK) /* no SEEK I/O... */
&& (uptr->CYL != cyl /* ...and: arm moved or... */
|| (rper & RPER_EOP))) { /* ...boundary exceeded? */
@ -1140,6 +1189,7 @@ static t_stat rr_svc (UNIT *uptr)
SET_INT(RR); /* request interrupt */
}
}
uptr->HEAD = head; /* update head */
uptr->CYL = cyl; /* update cyl */
rpda = (head << RPDA_V_TRACK) | sect; /* updated head / sect */
rpca = todo; /* wrapped up cyl */
@ -1151,10 +1201,10 @@ static t_stat rr_svc (UNIT *uptr)
const char* name = uptr->uname;
const char* file = uptr->filename;
const char* errstr = errno ? strerror(errno) : "";
sim_printf("RR%u %s [%s:%s] FUNC=%o(%c) RPER=%06o I/O error (%s)%s%s",
sim_printf("RR%u %s [%s:%s] FUNC=%s(%c) RPER=%06o I/O error (%s)%s%s",
(int) GET_DRIVE(rpcs), GET_DTYPE(uptr->flags) ? RP_RP03 : RP_RP02,
name ? name : "???", file ? file : "<NULL>",
(int) func, "WR"[!wr], (int) rper, sim_error_text(ioerr),
rp_funcs[func], "WR"[!wr], (int) rper, sim_error_text(ioerr),
errstr && *errstr ? ": " : "", errstr ? errstr : "");
return SCPE_IOERR;
}
@ -1209,6 +1259,8 @@ static t_stat rr_reset (DEVICE *dptr)
/* compile-time sanity check first */
const_assert(sizeof(rr_regs)/sizeof(rr_regs[0]) == RP_IOLN/2 - RP_IOFF);
assert(dptr == &rr_dev);
/* clear everything now */
rpds = 0;
rper = 0;
@ -1247,7 +1299,9 @@ static t_stat rr_attach (UNIT *uptr, CONST char *cptr)
t_stat err = sim_disk_attach_ex2(uptr, cptr,
RP_SIZE(sizeof(*rpxb)), sizeof(*rpxb),
TRUE, 0, drv_tab[type].name,
0, 0, rr_types, 0);
0, 0,
rr_dev.flags & DEV_RP11CE ? rr_types : NULL,
0);
if (err == SCPE_OK && !(uptr->STATUS & RPDS_DKER))
uptr->STATUS &= ~RPDS_UNSAFE;
return err;
@ -1280,10 +1334,14 @@ static t_stat rr_detach (UNIT *uptr)
static t_stat rr_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
assert(find_dev_from_unit(uptr) == &rr_dev);
if ((val & ~UNIT_RP03) || cptr)
return SCPE_ARG;
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
if (val && !(rr_dev.flags & DEV_RP11CE))
return SCPE_ARG;
assert(!(uptr->CYL | uptr->HEAD));
uptr->capac = RP_SIZE(drv_tab[GET_DTYPE(val)].size);
uptr->flags &= ~UNIT_RP03;
uptr->flags |= val;
@ -1300,7 +1358,7 @@ static t_stat rr_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
static t_stat rr_set_wloa (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE* dptr = find_dev_from_unit(uptr);
assert(find_dev_from_unit(uptr) == &rr_dev);
if (!cptr || !*cptr)
return SCPE_2FARG;
if (strncasecmp(cptr, "OFF", 3) == 0) {
@ -1322,9 +1380,9 @@ static t_stat rr_set_wloa (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
return SCPE_MISVAL;
errno = 0;
val = strtol(cptr, &end, 0);
if (errno || !end || *end || end == cptr || (val & ~RPWLOA_IMPL))
if (errno || !end || *end || end == cptr || (val & ~RPWLOA_IMP))
return SCPE_ARG;
wloa &= ~RPWLOA_IMPL;
wloa &= ~RPWLOA_IMP;
wloa |= val;
} else if (*cptr)
return SCPE_ARG;
@ -1390,14 +1448,13 @@ static t_stat rr_boot (int32 unitno, DEVICE *dptr)
/* Misc */
#define RP_DESCRIPTION RP_RP11 "/" RP_RP02 "/" RP_RP03 " disk pack device"
static t_stat rr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
size_t i;
size_t i, n;
assert(dptr == &rr_dev);
fputs(
/*567901234567890123456789012345678901234567890123456789012345678901234567890*/
RP_DESCRIPTION "\n\n"
RP_RP11 "/" RP_RP11CE "/" RP_RP02 "/" RP_RP03 " disk pack device\n\n"
"A detailed description of this device can be found in the\n"
"\"PDP-11 Peripherals Handbook\" (1973 - 1976) and in the technical manual\n"
"\"RP11-C Disk Pack Drive Controller Maintenance Manual\" (1974)\n"
@ -1409,9 +1466,10 @@ static t_stat rr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const cha
"(17776710 - 17776736), yet some just want to know where the CSR is located\n"
"(17776714 by default), so they can auto-calculate the range on their own.\n\n"
"Disk drive parameters (all decimal):\n\n"
" Cylinders Heads Sects/Trk Capacity Average access\n"
"Type Cylinders Heads Sects/Trk Capacity Average access\n"
" Total Spare Nominal Usable time, ms\n", st);
for (i = 0; i < sizeof(drv_tab)/sizeof(drv_tab[0]); ++i) {
n = dptr->flags & DEV_RP11CE ? sizeof(drv_tab)/sizeof(drv_tab[0]) : 1;
for (i = 0; i < n; ++i) {
uint32 spare = GET_DA(drv_tab[i].spare, RP_NUMSF, RP_NUMSC);
uint32 total = drv_tab[i].size;
fprintf(st, "%.6s: %5u %5u %5u %5u"
@ -1426,16 +1484,18 @@ static t_stat rr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const cha
"formatting operations yet supports the Write Lockout Address (LOA) register,\n"
"which can be set with a PROTECT command:\n\n"
" sim> set RR PROTECT=ON;0407\n\n"
"to turn the protection on (in this case, the entire units 0 and 1, and\n"
"7 x 2 + 1 = 15(10) first cylinders of unit 2 will become write-locked).\n"
"to turn the protection on (in this case, the entire units 0 and 1, and\n", st);
fprintf(st, "cylinders 0 thru 7%s in unit 2 will become write-locked).\n",
dptr->flags & DEV_RP11CE ? " x 2 + 1 = 15(10)" : "");
fputs(
"The current setting can be obtained by examining the WLOA register in\n"
"the device (the sign bit not present in hardware controls the feature):\n\n"
" sim> examine RR WLOA\n"
" WLOA: 100407 PROTECT=ON DRV=1 CYL2=7\n\n"
" WLOA: 100407 PROTECT=ON DRV=1 CYL=7\n\n"
"To remove the lockout:\n\n"
" sim> set RR PROTECT=OFF\n"
" sim> examine RR WLOA\n"
" WLOA: 000407 PROTECT=OFF DRV=1 CYL2=7\n\n"
" WLOA: 000407 PROTECT=OFF DRV=1 CYL=7\n\n"
"Note that it does not clear the address but turns the feature off. Also,\n"
"the WLOA register is unaffected by the device RESET.\n", st);
fprint_set_help (st, dptr);
@ -1448,14 +1508,43 @@ static t_stat rr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const cha
static const char *rr_description (DEVICE *dptr)
{
return RP_DESCRIPTION;
assert(dptr == &rr_dev);
return dptr->flags & DEV_RP11CE
? RP_RP11CE "/" RP_RP02 "/" RP_RP03 " disk pack device"
: RP_RP11 "/" RP_RP02 " disk pack device";
}
/* Show / switch controller type */
/* Set / show controller type */
static t_stat rr_set_ctrl (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
size_t i;
assert(find_dev_from_unit(uptr) == &rr_dev);
if ((val & ~DEV_RP11CE) || cptr)
return SCPE_ARG;
if (!val) { /* going RP11? */
for (i = 0; i < RP_NUMDR; ++i) {
uptr = rr_dev.units + i;
if ((uptr->flags & UNIT_ATT) && (uptr->flags & UNIT_RP03))
return SCPE_ALATT; /* no RP03 allowed */
}
}
rr_dev.flags &= ~DEV_RP11CE;
rr_dev.flags |= val;
for (i = 0; i < RP_NUMDR; ++i) {
uptr = rr_dev.units + i;
if (uptr->flags & UNIT_ATT)
continue;
if (rr_set_type(uptr, val ? UNIT_RP03 : 0, 0, 0) != SCPE_OK)
return SCPE_IERR;
}
return SCPE_OK;
}
static t_stat rr_show_ctrl (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fputs(RP_RP11, st);
assert(find_dev_from_unit(uptr) == &rr_dev);
fputs(rr_dev.flags & DEV_RP11CE ? RP_RP11CE : RP_RP11, st);
return SCPE_OK;
}