From 86a995b863a1662406dd51ba69bb71d4902875cc Mon Sep 17 00:00:00 2001 From: Bob Supnik Date: Mon, 12 Dec 2022 12:00:00 -0500 Subject: [PATCH] PDP11: numerous 11/70 compatibility fixes --- PDP11/pdp11_cpu.c | 102 ++++++++++++++++++++++++++++--------------- PDP11/pdp11_cpumod.c | 54 +++++++++++++++++------ PDP11/pdp11_defs.h | 13 ++++-- PDP11/pdp11_fp.c | 27 ++++++++---- PDP11/pdp11_rh.c | 2 +- PDP11/pdp11_rk.c | 6 ++- PDP11/pdp11_rl.c | 1 + 7 files changed, 139 insertions(+), 66 deletions(-) diff --git a/PDP11/pdp11_cpu.c b/PDP11/pdp11_cpu.c index 88553148..c37df8b2 100644 --- a/PDP11/pdp11_cpu.c +++ b/PDP11/pdp11_cpu.c @@ -25,6 +25,20 @@ cpu PDP-11 CPU + 30-Nov022 RMS More 11/45,11/70 trap hackery (Walter Mueller) + 29-Nov-22 RMS Trap stack abort must clear other traps/aborts (Walter Mueller) + 23-Oct-22 RMS Fixed priority of MME traps (Walter Mueller) + 02-Sep-22 RMS Fixed handling of PDR (Walter Mueller) + 31-Aug-22 RMS MMR0<15:13> != 0 locks bits<15:13> (Walter Mueller) + MMR0<12> = 1 disables further traps (Walter Mueller) + 25-Aug-22 RMS 11/45,70 clear MMR1 in trap sequence (Walter Mueller) + 23-Aug-22 RMS 11/45,70 detect red stack abort before memory write + in JSR, MFPx (Walter Mueller) + 20-Aug-22 RMS MMR1 reads as 0 on subset memory mmgt systems + 11/44, 45, 70 track PC changes (Walter Mueller) + J11 tracks PC changes on -(PC) and @-(PC) + 25-Jul-22 RMS Removed deprecated CPU models (Q22, UHR11, URH70) + 04-Jun-18 RMS Removed CPU model entries for UC15 (Mark Pizzolato) 04-Dec-16 RMS Removed duplicate IDLE entries in MTAB 30-Aug-16 RMS Fixed overloading of -d in ex/mod 14-Mar-16 RMS Added UC15 support @@ -373,22 +387,35 @@ extern int32 get_vector (int32 nipl); /* Trap data structures */ int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */ - VEC_RED, VEC_ODD, VEC_MME, VEC_NXM, + VEC_RED, VEC_ODD, VEC_NXM, VEC_MME, VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT, VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC, VEC_YEL, VEC_PWRFL, VEC_FPE }; +t_bool trap_load_mmr2[TRAP_V_MAX + 1] = { /* do trap requests load MMR2? */ + TRUE, TRUE, TRUE, TRUE, + TRUE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, TRUE, + TRUE, TRUE, TRUE, TRUE /* last is interrupt */ + }; + int32 trap_clear[TRAP_V_MAX] = { /* trap clears */ - TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_ODD+TRAP_NXM, - TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC, - TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC, - TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC, - TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC, - TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC, - TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC, - TRAP_TRAP+TRAP_TRC, TRAP_TRC, - TRAP_YEL, TRAP_PWRFL, TRAP_FPE + TRAP_RED+TRAP_ODD+TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_MME, /* red stack abort */ + TRAP_ODD+TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_MME, /* odd address abort */ + TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_MME, /* nxm abort */ + TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC, /* mme abort or trap */ + TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_PRV+TRAP_TRC, /* instruction traps */ + TRAP_ILL+TRAP_TRC, /* occur in fetch or */ + TRAP_BPT+TRAP_TRC, /* initial decode */ + TRAP_IOT+TRAP_TRC, /* no yelstk possible */ + TRAP_EMT+TRAP_TRC, + TRAP_TRAP+TRAP_TRC, + TRAP_TRC, + TRAP_YEL, + TRAP_PWRFL, + TRAP_FPE }; /* CPU data structures @@ -729,7 +756,7 @@ isenable = calc_is (cm); dsenable = calc_ds (cm); put_PIRQ (PIRQ); /* rewrite PIRQ */ STKLIM = STKLIM & STKLIM_RW; /* clean up STKLIM */ -MMR0 = MMR0 | MMR0_IC; /* usually on */ +MMR0 = MMR0 & ~MMR0_IC; /* usually off */ trap_req = calc_ints (ipl, trap_req); /* upd int req */ trapea = 0; @@ -791,7 +818,8 @@ else { (CPUT (STOP_STKA) || stop_spabort)) reason = STOP_SPABORT; if (trapea == ~MD_KER) { /* kernel stk abort? */ - setTRAP (TRAP_RED); + trap_req = trap_req & ~trap_clear[TRAP_RED];/* clear all traps */ + setTRAP (TRAP_RED); /* set red stack trap */ setCPUERR (CPUE_RED); STACKFILE[MD_KER] = 4; if (cm == MD_KER) @@ -879,20 +907,19 @@ while (reason == 0) { 6. Update SP, PSW, and PC 7. If not stack overflow, check for stack overflow - If the reads in step 3, or the writes in step 5, match a data breakpoint, - the breakpoint status will be set but the interrupt actions will continue. - The breakpoint stop will occur at the beginning of the next instruction - cycle. + If the MMU registers are not frozen, the 11/45 and 11/70 will + also clear MMR1 and store the trap vector in MMR2, + for the four instruction traps (EMT, TRAP, IOT, BPT). */ wait_state = 0; /* exit wait state */ STACKFILE[cm] = SP; PSW = get_PSW (); /* assemble PSW */ oldrs = rs; - if (CPUT (HAS_MMTR)) { /* 45,70? */ - if (update_MM) /* save vector */ - MMR2 = trapea; - MMR0 = MMR0 & ~MMR0_IC; /* clear IC */ + if ((CPUT (HAS_MMTR)) && (update_MM)) { /* 45,70, not frozen? */ + MMR1 = 0; /* clear MMR1 */ + if (trap_load_mmr2[trapnum]) /* load MMR2? */ + MMR2 = trapea; /* save vector */ } src = ReadCW (trapea | calc_ds (MD_KER)); /* new PC */ src2 = ReadCW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */ @@ -917,7 +944,6 @@ while (reason == 0) { if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)) && (trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL)) set_stack_trap (SP); - MMR0 = MMR0 | MMR0_IC; /* back to instr */ continue; /* end if traps */ } @@ -2887,14 +2913,16 @@ switch (apr & PDR_ACF) { /* case on ACF */ case 1: case 4: /* trap read */ if (CPUT (HAS_MMTR)) { /* traps implemented? */ - APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ - if (MMR0 & MMR0_TENB) { /* traps enabled? */ + int32 old_mmr0 = MMR0; + APRFILE[apridx] |= PDR_A; /* set A */ + MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ + if ((MMR0 & MMR0_TENB) != 0) { /* traps enabled? */ if (update_MM) /* update MMR0 */ MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); - MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ - setTRAP (TRAP_MME); /* set trap */ + if ((old_mmr0 & MMR0_TRAP) == 0) /* first trap? */ + setTRAP (TRAP_MME); /* set trap */ } - return; /* continue op */ + return; /* continue */ } /* not impl, abort NR */ case 0: case 3: case 7: /* non-resident */ err = MMR0_NR; /* set MMR0 */ @@ -2920,10 +2948,10 @@ return ((apr & PDR_ED)? (dbn < plf): (dbn > plf)); /* pg lnt error? */ void reloc_abort (int32 err, int32 apridx) { -if (update_MM) MMR0 = /* update MMR0 */ - (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); -APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ -MMR0 = MMR0 | err; /* set aborts */ +if (update_MM) { /* MMR0 not frozen? */ + MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); /* record page */ + MMR0 = MMR0 | err; /* OR in aborts */ + } ABORT (TRAP_MME); /* abort ref */ return; } @@ -2955,7 +2983,7 @@ if (MMR0 & MMR0_MME) { /* if mmgt */ relocW_test (va, apridx); /* long test */ if (PLF_test (va, apr)) /* pg lnt error? */ reloc_abort (MMR0_PL, apridx); - APRFILE[apridx] = apr | PDR_W; /* set W */ + APRFILE[apridx] |= PDR_W; /* set W */ pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; if ((MMR3 & MMR3_M22E) == 0) { pa = pa & 0777777; @@ -2995,14 +3023,16 @@ switch (apr & PDR_ACF) { /* case on ACF */ case 4: case 5: /* trap write */ if (CPUT (HAS_MMTR)) { /* traps implemented? */ - APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ - if (MMR0 & MMR0_TENB) { /* traps enabled? */ + int32 old_mmr0 = MMR0; + APRFILE[apridx] |= PDR_A; /* set PDR */ + MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ + if ((MMR0 & MMR0_TENB) != 0) { /* traps enabled? */ if (update_MM) /* update MMR0 */ MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); - MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ - setTRAP (TRAP_MME); /* set trap */ + if ((old_mmr0 & MMR0_TRAP) == 0) /* first trap? */ + setTRAP (TRAP_MME); /* set trap */ } - return; /* continue op */ + return; /* continue, set A */ } /* not impl, abort NR */ case 0: case 3: case 7: /* non-resident */ err = MMR0_NR; /* MMR0 status */ diff --git a/PDP11/pdp11_cpumod.c b/PDP11/pdp11_cpumod.c index 08972422..9f7e80e0 100644 --- a/PDP11/pdp11_cpumod.c +++ b/PDP11/pdp11_cpumod.c @@ -1,6 +1,6 @@ /* pdp11_cpumod.c: PDP-11 CPU model-specific features - Copyright (c) 2004-2020, Robert M Supnik + Copyright (c) 2004-2022, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ system PDP-11 model-specific registers + 19-Nov-22 RMS Fixed byte access errors in PIRQ, STKLIM, CDR (Walter Mueller) 15-Sep-20 RMS Fixed problem in KDJ11E programmable clock (Paul Koning) 04-Mar-16 RMS Fixed maximum memory sizes to exclude IO page 14-Mar-16 RMS Modified to keep cpu_memsize in sync with MEMSIZE @@ -49,12 +50,21 @@ #include "pdp11_defs.h" #include "pdp11_cpumod.h" -/* Byte write macros for system registers */ +/* Byte write macros for system registers + EVN_IGN byte writes to the even byte are ignored + ODD_IGN byte writes to the odd byte are ignored + ODD_SHF byte writes to the odd byte zero the even byte + ODD_MRG byte writes write appropriate byte +*/ + +#define EVN_IGN(cur) \ + if ((access == WRITEB) && ((pa & 1) == 0)) \ + return SCPE_OK #define ODD_IGN(cur) \ if ((access == WRITEB) && (pa & 1)) \ return SCPE_OK -#define ODD_WO(cur) \ +#define ODD_SHF(cur) \ if ((access == WRITEB) && (pa & 1)) \ cur = cur << 8 #define ODD_MRG(prv,cur) \ @@ -475,7 +485,8 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */ return SCPE_OK; case 015: /* PIRQ */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); put_PIRQ (data); return SCPE_OK; } @@ -511,12 +522,14 @@ t_stat CPU45_wr (int32 data, int32 pa, int32 access) switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 015: /* PIRQ */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); put_PIRQ (data); return SCPE_OK; case 016: /* STKLIM */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); STKLIM = data & STKLIM_RW; return SCPE_OK; } /* end switch pa */ @@ -590,7 +603,8 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */ return SCPE_OK; case 016: /* STKLIM */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); STKLIM = data & STKLIM_RW; return SCPE_OK; } /* end switch pa */ @@ -661,12 +675,19 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */ return SCPE_NXM; /* unimplemented */ } +/* From Walter Mueller: MEMERR is always written using the standard data + on the bus: the whole word for DATO, the high byte for DATOB odd, + and the low byte with DATOB even. The simulator always puts a byte + in the low 8 bits, so for an odd byte reference, it must be shifted + to the high byte. +*/ + t_stat CPU70_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 002: /* MEMERR */ - ODD_WO (data); + ODD_SHF (data); MEMERR = MEMERR & ~data; return SCPE_OK; @@ -697,12 +718,14 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */ return SCPE_OK; case 015: /* PIRQ */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); put_PIRQ (data); return SCPE_OK; case 016: /* STKLIM */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); STKLIM = data & STKLIM_RW; return SCPE_OK; } /* end switch pa */ @@ -779,7 +802,8 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */ return SCPE_OK; case 015: /* PIRQ */ - ODD_WO (data); + EVN_IGN (data); + ODD_SHF (data); put_PIRQ (data); return SCPE_OK; } /* end switch pa */ @@ -818,12 +842,14 @@ switch ((pa >> 1) & 03) { /* decode pa<2:1> */ ODD_MRG (JPCR, data); JPCR = data & PCRFB_RW; return SCPE_OK; + case 1: /* MAINT */ ODD_MRG (MAINT, data); MAINT = data; return SCPE_OK; + case 2: /* CDR */ - ODD_WO (data); + ODD_IGN (data); DR = data & CDRFB_WR; return SCPE_OK; } @@ -881,7 +907,7 @@ switch ((pa >> 1) & 03) { /* decode pa<2:1> */ return SCPE_OK; case 2: /* CDR */ - ODD_WO (data); + ODD_IGN (data); DR = data & CDRJB_WR; return SCPE_OK; } @@ -972,7 +998,7 @@ switch ((pa >> 1) & 03) { /* decode pa<2:1> */ return SCPE_OK; case 2: /* CDR */ - ODD_WO (data); + ODD_IGN (data); DR = data & CDRJE_WR; return SCPE_OK; diff --git a/PDP11/pdp11_defs.h b/PDP11/pdp11_defs.h index 62339c7f..16afa98a 100644 --- a/PDP11/pdp11_defs.h +++ b/PDP11/pdp11_defs.h @@ -26,6 +26,8 @@ The author gratefully acknowledges the help of Max Burnet, Megan Gentry, and John Wilson in resolving questions about the PDP-11 + 23-Oct-22 RMS Moved NXM abort priority above MME trap priority + 25-Jul-22 RMS Removed OPT_RH11 (Mark Pizzolato) 10-Feb-17 RMS Fixed RJS11 register block length (Mark Hill) 19-Jan-17 RMS Moved CR11 to BR6, leaving CD11 at BR4 (Mark Pizzolato) 10-Mar-16 RMS Added UC15 support @@ -409,14 +411,17 @@ typedef struct { #define CSR_BUSY (1u << CSR_V_BUSY) #define CSR_ERR (1u << CSR_V_ERR) -/* Trap masks, descending priority order, following J-11 - An interrupt summary bit is kept with traps, to minimize overhead +/* Trap masks, descending priority order. Rules: + + - Aborts are mutually exclusive, no more than one per instrution. + - Aborts must be higher priority than traps. Because MME can be + either an abort or a trap, it is lower priority than NXM. */ #define TRAP_V_RED 0 /* red stk abort 4 */ #define TRAP_V_ODD 1 /* odd address 4 */ -#define TRAP_V_MME 2 /* mem mgt 250 */ -#define TRAP_V_NXM 3 /* nx memory 4 */ +#define TRAP_V_NXM 2 /* nx memory 4 */ +#define TRAP_V_MME 3 /* mem mgt 250 */ #define TRAP_V_PAR 4 /* parity err 114 */ #define TRAP_V_PRV 5 /* priv inst 4 */ #define TRAP_V_ILL 6 /* illegal inst 10 */ diff --git a/PDP11/pdp11_fp.c b/PDP11/pdp11_fp.c index 89aa1639..3c440d14 100644 --- a/PDP11/pdp11_fp.c +++ b/PDP11/pdp11_fp.c @@ -23,6 +23,9 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 10-Dec-22 RMS Fixed bug in FUIV operation (James Fehlinger) + 21-Aug-22 RMS Restored MMR1 operation for 11/44, 11/45-70 (Walter Mueller) + 28-May-18 RMS Fixed FPCHG macro to avoid undefined operation (Mark Pizzolato) 24-Mar-15 RMS MMR1 does not track register changes (Johnny Billquist) 20-Apr-13 RMS MMR1 does not track PC changes (Johnny Billquist) 22-Sep-05 RMS Fixed declarations (Sterling Garwood) @@ -86,12 +89,17 @@ accessed; if the operand is 32b or 64b, these are the high order 16b of the operand. - The FP11 cannot update MMR1 on specifier changes, because the - quantity field is too narrow for +8 or -8. Instead, the simulator - records changes to be made and only commits them at instruction - completion. Instructions that can overwrite a general register - (STFPS, STST, STEXP, STCFi in mode 0) need not check for conflicts; - in mode 0, no general register changes occur in the specifier flow. + The J11 cannot update MMR1 on specifier changes, because the + quantity field is too narrow for +8 or -8. However, the 11/44 and + 11/70 can. So the simulator treats the two cases differently. + On the J11, the simulator records changes to be made and only + commits them at instruction. On all other systems, changes occur + as they happen and are recorded in MMR1. However, all systems + update the general registers on floating point exceptions. Thus, + when an exception occurs, the simulator in most cases cannot + abort but must let the instruction "run to completion." For + undefined variable and divide by zero, this means skipping + the actual processing logic. */ #include "pdp11_defs.h" @@ -536,7 +544,7 @@ switch ((IR >> 8) & 017) { /* decode IR<11:8> */ case 003: /* MODf */ if (ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf)) { - F_LOAD (qdouble, FR[ac], fac); + F_LOAD (qdouble, FR[ac], fac); newV = modfp11 (&fac, &fsrc, &modfrac); F_STORE (qdouble, fac, FR[ac | 1]); F_STORE (qdouble, modfrac, FR[ac]); @@ -787,8 +795,9 @@ else { } if ((GET_SIGN (fptr->h) != 0) && (GET_EXP (fptr->h) == 0) && - (fpnotrap (FEC_UNDFV) == 0)) - return FALSE; + !fpnotrap (FEC_UNDFV)) { /* trap enabled? */ + return FALSE; /* NOP instruction */ + } return TRUE; } diff --git a/PDP11/pdp11_rh.c b/PDP11/pdp11_rh.c index 34e29a82..71db1236 100644 --- a/PDP11/pdp11_rh.c +++ b/PDP11/pdp11_rh.c @@ -573,7 +573,7 @@ for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ massbus[mb].ba = ba & DMASK; /* update ba */ massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ -massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ +massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); return i; } diff --git a/PDP11/pdp11_rk.c b/PDP11/pdp11_rk.c index 2d34d286..7e785fb3 100644 --- a/PDP11/pdp11_rk.c +++ b/PDP11/pdp11_rk.c @@ -1,6 +1,6 @@ /* pdp11_rk.c: RK11/RKV11 cartridge disk simulator - Copyright (c) 1993-2016, Robert M Supnik + Copyright (c) 1993-2022, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ rk RK11/RKV11/RK05 cartridge disk + 28-Nov-22 RMS Fixed word count adjustment on NXM (Anthony Lawrence) 12-Mar-16 RMS Revised to support UC15 (18b IO) 23-Oct-13 RMS Revised for new boot setup routine 20-Mar-09 RMS Fixed bug in read header (Walter F Mueller) @@ -764,7 +765,7 @@ if (wc && (err == 0)) { /* seek ok? */ rkxb[i] = comp; } else { /* normal fetch */ - if ((t = MAP_RDW (ma, wc << 1, rkxb))) { /* get buf */ + if ((t = MAP_RDW (ma, wc << 1, rkxb))) { /* get buf */ rker = rker | RKER_NXM; /* NXM? set flg */ wc = wc - t; /* adj wd cnt */ } @@ -816,6 +817,7 @@ if ((uptr->FUNC == RKCS_READ) && (rkcs & RKCS_FMT)) /* read format? */ else da = da + wc + (RK_NUMWD - 1); /* count by words */ track = (da / RK_NUMWD) / RK_NUMSC; sect = (da / RK_NUMWD) % RK_NUMSC; +uptr->CYL = track / RK_NUMSF; /* update position */ rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT); rk_set_done (0); diff --git a/PDP11/pdp11_rl.c b/PDP11/pdp11_rl.c index 9e9f9cf8..0d7e0ec7 100644 --- a/PDP11/pdp11_rl.c +++ b/PDP11/pdp11_rl.c @@ -25,6 +25,7 @@ rl RL11(RLV12)/RL01/RL02 cartridge disk + 28-Nov-22 RMS Fixed word count adjustment on NXM 23-Oct-13 RMS Revised for new boot setup routine 24-Mar-11 JAD Various changes to support diagnostics, including: - distinguish between RLV11 & 12