From a653bbfcec093df1ecd9bd469c0750935e3605f9 Mon Sep 17 00:00:00 2001 From: Tony Lawrence Date: Wed, 7 Jun 2023 20:21:51 -0400 Subject: [PATCH] PDP11: RP11-C (RP02/03) disk implementation --- PDP11/CMakeLists.txt | 1 + PDP11/pdp11_defs.h | 3 + PDP11/pdp11_io_lib.c | 4 +- PDP11/pdp11_rr.c | 1266 ++++++++++++++++++ PDP11/pdp11_sys.c | 2 + Visual Studio Projects/PDP11.vcproj | 4 + Visual Studio Projects/PDP11.vcxproj | 1 + Visual Studio Projects/PDP11.vcxproj.filters | 3 + makefile | 2 +- 9 files changed, 1283 insertions(+), 3 deletions(-) create mode 100644 PDP11/pdp11_rr.c diff --git a/PDP11/CMakeLists.txt b/PDP11/CMakeLists.txt index 971843f4..7a077796 100644 --- a/PDP11/CMakeLists.txt +++ b/PDP11/CMakeLists.txt @@ -65,6 +65,7 @@ add_simulator(pdp11 pdp11_daz.c pdp11_tv.c pdp11_mb.c + pdp11_rr.c ${DISPLAYNG} ${DISPLAYVT} INCLUDES diff --git a/PDP11/pdp11_defs.h b/PDP11/pdp11_defs.h index 16afa98a..361cc16e 100644 --- a/PDP11/pdp11_defs.h +++ b/PDP11/pdp11_defs.h @@ -640,6 +640,7 @@ typedef struct pdp_dib DIB; #define INT_V_NG 27 #define INT_V_DHRX 28 #define INT_V_DHTX 29 +#define INT_V_RR 30 #define INT_V_PIR4 0 /* BR4 */ #define INT_V_TTI 1 @@ -682,6 +683,7 @@ typedef struct pdp_dib DIB; #define INT_RX (1u << INT_V_RX) #define INT_TM (1u << INT_V_TM) #define INT_RP (1u << INT_V_RP) +#define INT_RR (1u << INT_V_RR) #define INT_TS (1u << INT_V_TS) #define INT_HK (1u << INT_V_HK) #define INT_RQ (1u << INT_V_RQ) @@ -750,6 +752,7 @@ typedef struct pdp_dib DIB; #define IPL_RX 5 #define IPL_TM 5 #define IPL_RP 5 +#define IPL_RR 5 #define IPL_TS 5 #define IPL_HK 5 #define IPL_RQ 5 diff --git a/PDP11/pdp11_io_lib.c b/PDP11/pdp11_io_lib.c index b8b37545..1cac658e 100644 --- a/PDP11/pdp11_io_lib.c +++ b/PDP11/pdp11_io_lib.c @@ -673,6 +673,8 @@ AUTO_CON auto_tab[] = {/*c #v am vm fxa fxv */ {010700} }, /* KG11-A - fx CSR, no VEC */ { { "RHA" }, 1, 1, 0, 0, {016700}, {0254} }, /* RH11/RH70 - fx CSR, fx VEC */ + { { "RR" }, 1, 1, 0, 0, + {016710}, {0254} }, /* RP11/-C/-E - fx CSR, fx VEC */ { { "RHB" }, 1, 1, 0, 0, {012440}, {0224} }, /* RH11/RH70 - fx CSR, fx VEC */ { { "RHC" }, 1, 1, 0, 0, @@ -704,8 +706,6 @@ AUTO_CON auto_tab[] = {/*c #v am vm fxa fxv */ {015606}, {0250} }, /* RB730 - fx CSR, fx VEC */ { { "RL" }, 1, 1, 0, 0, {014400}, {0160} }, /* RL11 - fx CSR, fx VEC */ - { { "RL" }, 1, 1, 0, 0, - {014400}, {0160} }, /* RL11 - fx CSR, fx VEC */ { { "DCI" }, 1, 2, 0, 8, {014000, 014010, 014020, 014030, 014040, 014050, 014060, 014070, diff --git a/PDP11/pdp11_rr.c b/PDP11/pdp11_rr.c new file mode 100644 index 00000000..17f30136 --- /dev/null +++ b/PDP11/pdp11_rr.c @@ -0,0 +1,1266 @@ +/* pdp11_rr.c: RP11/-C/-E/RP02/RP03 disk pack device + + Copyright (c) 2022 Tony Lawrence + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + Inspired by PDP-11's RK/RL implementations by Robert Supnik. +*/ + +#if defined(VM_PDP11) && !defined(UC15) + +#include + +#include "pdp11_defs.h" +#include "sim_disk.h" + +/* Constants */ + +#define RPCONTR uint16 +#define RPWRDSZ 16 +#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) */ +#define RP_NUMWD 256 /* words/sector */ +#define RP_NUMCY 203 /* cylinders/drive */ +#define RP_SPARE 3 /* of those, spare */ +#define RP_NUMSF 20 /* surfaces/cylinder */ +#define RP_NUMSC 10 /* sectors/track */ +#define RP_NUMTR (RP_NUMCY * RP_NUMSF) /* tracks/drive */ +#define RP_NUMBL (RP_NUMTR * RP_NUMSC) /* blocks/drive */ +#define RP_NUMDR 8 /* drives/controller */ +#define RP_MAXFR (1 << 16) /* max transfer */ +#define RP_SIZE(n) (RP_NUMWD * (n)) /* words in n blocks */ +#define RP_SIZE_RP02 RP_SIZE(RP_NUMBL) /* RP02 capacity, words */ +#define RP_SIZE_RP03 RP_SIZE(RP_NUMBL*2) /* RP03 capacity, words */ +#define RP_ROT_12 125 /* Half rotation, 0.1ms */ + +/* 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) + +/* Controller / drive types */ +#define RP_RP11 "RP11-C" +#define RP_RP02 "RP02" +#define RP_RP03 "RP03" + +/* Parameters in the unit descriptor */ +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ +#define HEAD u5 /* current track */ +#define SEEKING u6 /* unit performing a seek */ + +/* 12 UNIBUS registers */ +#define RP_IOLN 030 + +/* RP02/RP03 particulars */ +static struct drv_typ { + const char* name; /* device type name */ + int32 cyl; /* cylinders */ + int32 size; /* #blocks */ + int32 spare; /* spare (out of cyl) */ + int32 seek_1; /* one track move, 0.1ms */ + int32 seek_ave; /* average seek, 0.1ms */ + int32 seek_max; /* maximal seek, 0.1ms */ +} drv_tab[] = { + { RP_RP02, RP_NUMCY, RP_NUMBL, RP_SPARE, 200, 500, 800 }, + { RP_RP03, RP_NUMCY*2, RP_NUMBL*2, RP_SPARE*2, 75, 290, 550 }, + { NULL } +}; + +/* RPDS 776710, selected drive status, read-only except for the attention bits */ +static BITFIELD rp_ds_bits[] = { +#define RPDS_ATTN 0000377 /* attention (read/clear) */ + BITF(ATTN,8), +#define RPDS_WLK 0000400 /* write locked */ + BIT(WLK), +#define RPDS_UNSAFE 0001000 /* unsafe */ + BIT(UNSAFE), +#define RPDS_SEEK 0002000 /* seek underway */ + BIT(SEEK), +#define RPDS_INC 0004000 /* seek incomplete */ + BIT(INC), +#define RPDS_HNF 0010000 /* header not found */ + BIT(HNF), +#define RPDS_RP03 0020000 /* drive is RP03 */ + BIT(RP03=), +#define RPDS_ONLN 0040000 /* unit online */ + BIT(ONLN), +#define RPDS_RDY 0100000 /* unit ready */ + BIT(RDY), + ENDBITS +}; +#define RPDS_DKER(x) ((x) & (RPDS_HNF | RPDS_INC) ? RPER_DRE : 0) + +/* RPER 776712, error register, read-only */ +static BITFIELD rp_er_bits[] = { +#define RPER_DRE 0000001 /* drive error (HNF|INC) */ + BIT(DRE), +#define RPER_EOP 0000002 /* end of pack (overrun) */ + BIT(EOP), +#define RPER_NXM 0000004 /* nx memory */ + BIT(NXM), +#define RPER_WCE 0000010 /* write check error */ + BIT(WCE), +#define RPER_TE 0000020 /* timing error */ + BIT(TE), +#define RPER_CSE 0000040 /* serial checksum error */ + BIT(CSE), +#define RPER_WPE 0000100 /* word parity error */ + BIT(WPE), +#define RPER_LPE 0000200 /* longitudinal parity error */ + BIT(LPE), +#define RPER_MODE 0000400 /* mode error */ + BIT(MODE), +#define RPER_FMTE 0001000 /* format error */ + BIT(FMTE), +#define RPER_PGE 0002000 /* programming error */ + BIT(PGE), +#define RPER_NXS 0004000 /* nx sector */ + BIT(NXS), +#define RPER_NXT 0010000 /* nx track */ + BIT(NXT), +#define RPER_NXC 0020000 /* nx cylinder */ + BIT(NXC), +#define RPER_FUV 0040000 /* unsafe violation */ + BIT(FUV), +#define RPER_WPV 0100000 /* write lock violation */ + BIT(WPV), + ENDBITS +}; +#define RPER_REAL 0177776 +/* hard errors: drawing 19 */ +#define RPER_HARDERR (RPER_WPV | RPER_FUV | RPER_NXC | RPER_NXT | \ + RPER_NXS | RPER_PGE | RPER_NXM | RPER_DRE | RPER_MODE) +/* soft errors: drawing 19 */ +#define RPER_SOFTERR (RPER_LPE | RPER_WPE | RPER_CSE | RPER_WCE | \ + RPER_EOP | RPER_TE | RPER_FMTE) +#define RPER_HARD(x) ((x) & RPER_HARDERR ? (RPCS_ERR | RPCS_HERR) : 0) +#define RPER_SOFT(x) ((x) & RPER_SOFTERR ? RPCS_ERR : 0) + +/* RPCS 776714, command/status register */ +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), +#define RPCS_V_FUNC 1 +#define RPCS_M_FUNC 7 +#define RPCS_FUNC (RPCS_M_FUNC << RPCS_V_FUNC) /* function */ +#define RPCS_RESET 0 +#define RPCS_WRITE 1 +#define RPCS_READ 2 +#define RPCS_WCHK 3 +#define RPCS_SEEK 4 +#define RPCS_WR_NOSEEK 5 +#define RPCS_HOME 6 +#define RPCS_RD_NOSEEK 7 + BITFNAM(FUNC,3,rp_funcs), +#define RPCS_V_MEX 4 +#define RPCS_M_MEX 3 +#define RPCS_MEX (RPCS_M_MEX << RPCS_V_MEX) /* memory extension */ + BITF(MEX,2), +/* CSR_IE */ /* interrupt enable */ + BIT(IE), +/* CSR_DONE */ /* controller ready */ + BIT(DONE), +#define RPCS_V_DRV 8 +#define RPCS_M_DRV 7 +#define RPCS_DRV (RPCS_M_DRV << RPCS_V_DRV) /* drive id */ + BITFFMT(DRV,3,%u), +#define RPCS_HDR 0004000 /* header operation */ + BIT(HDR), +#define RPCS_MODE 0010000 /* 0=PDP-11; 1=PDP-10/-15 or format */ + BIT(MODE), +#define RPCS_AIE 0020000 /* attention interrupt enable */ + BIT(AIE), +#define RPCS_HERR 0040000 /* hard error */ + BIT(HERR), +#define RPCS_ERR CSR_ERR /* error (hard or soft) */ + BIT(ERR), +#define RPCS_REAL 0037776 /* bits kept here */ +#define RPCS_RW 0037576 /* read/write */ +#define GET_FUNC(x) (((x) & RPCS_FUNC) >> RPCS_V_FUNC) +#define GET_DRIVE(x) (((x) & RPCS_DRV) >> RPCS_V_DRV) + ENDBITS +}; + +/* RPWC 776716, two's complement word count */ +/* For PDP-11 must be even for data, and in multiples of 3 for format */ +static BITFIELD rp_wc_bits[] = { + BITFFMT(WC,16,%u), + ENDBITS +}; + +/* RPBA 776720, bus address */ +static BITFIELD rp_ba_bits[] = { +#define RPBA_IMP 0177776 /* implemented */ + BITF(BA,16), + ENDBITS +}; + +/* RPCA 776722, cylinder address */ +static BITFIELD rp_ca_bits[] = { +#define RPCA_IMP 0000777 /* implemented */ + BITFFMT(CYL,9,%u), + ENDBITS +}; + +/* RPDA 776724, disk address (track/sector) */ +static BITFIELD rp_da_bits[] = { +#define RPDA_IMPL 0017777 /* implemented */ +#define RPDA_RW 0017417 /* bits here */ +#define RPDA_M_SECT 017 +#define RPDA_SECT RPDA_M_SECT /* sector */ + BITFFMT(SECT,4,%u), +#define RPDA_V_SOT 4 +#define RPDA_SOT (RPDA_M_SECT << RPDA_V_SOT) /* current sect on track */ + BITFFMT(SOT,4,%u), +#define RPDA_V_TRACK 8 +#define RPDA_M_TRACK 037 +#define RPDA_TRACK (RPDA_M_TRACK << RPDA_V_TRACK) /* track */ + 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 +}; + +/* RPM1 776726 maintenance 1, read-only, not implemented */ + +/* RPM2 776730 maintenance 2, read-only, not implemented */ + +/* RPM1 776732 maintenance 3, write-only, not implemented */ + +/* SUCA 776734 selected unit cylinder address, read-only */ +static BITFIELD rp_suca_bits[] = { + BITFFMT(CYL,9,%u), + ENDBITS +}; + +/* SILO 776736 silo memory, not implemented */ + +/* 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 01777 +#define RPWLOA_CYL2 0377 /* cyls locked (x2) */ + BITFFMT(CYL2,8,%u), +#define RPWLOA_V_DRV 8 +#define RPWLOA_M_DRV 3 +#define RPWLOA_DRV (RPWLOA_M_DRV << RPWLOA_V_DRV) /* drive(s) locked */ + BITFFMT(DRV,3,%u), +#define GET_WLOACYL(x) ((((x) & RPWLOA_CYL2) << 1) | 1) +#define GET_WLOADRV(x) (((x) & RPWLOA_DRV) >> RPWLOA_V_DRV) + BITNCF(4), +#define RPWLOA_ON 0100000 + BITFNAM(PROTECT,1,offon), + ENDBITS +}; + +/* Data buffer and device registers */ + +static RPCONTR* rpxb = NULL; /* xfer buffer */ +static int32 rpds = 0; /* drive status */ +static int32 rper = 0; /* error status */ +static int32 rpcs = 0; /* control/status */ +static int32 rpwc = 0; /* word count */ +static int32 rpba = 0; /* memory address */ +static int32 rpca = 0; /* cylinder address */ +static int32 rpda = 0; /* disk address */ +static int32 suca = 0; /* current cylinder address */ +static int32 wloa = 0; /* write lockout address */ +static int32 not_impl = 0; /* dummy register value */ + +/* Debug detail levels */ + +#define RRDEB_OPS 001 /* transactions */ +#define RRDEB_RRD 002 /* reg reads */ +#define RRDEB_RWR 004 /* reg writes */ +#define RRDEB_TRC 010 /* trace */ +#define RRDEB_INT 020 /* interrupts */ +#define RRDEB_DAT 0100 /* transfer data */ + +static DEBTAB rr_deb[] = { + { "OPS", RRDEB_OPS, "transactions" }, + { "RRD", RRDEB_RRD, "register reads" }, + { "RWR", RRDEB_RWR, "register writes" }, + { "INTERRUPT", RRDEB_INT, "interrupts" }, + { "TRACE", RRDEB_TRC, "trace" }, + { "DATA", RRDEB_DAT, "transfer data" }, + { NULL, 0 } +}; + +static struct { + const char* name; + int32* valp; + BITFIELD* bits; +} rr_regs[] = { + { "RPDS", &rpds, rp_ds_bits }, + { "RPER", &rper, rp_er_bits }, + { "RPCS", &rpcs, rp_cs_bits }, + { "RPWC", &rpwc, rp_wc_bits }, + { "RPBA", &rpba, rp_ba_bits }, + { "RPCA", &rpca, rp_ca_bits }, + { "RPDA", &rpda, rp_da_bits }, + { "RPM1", ¬_impl, NULL }, + { "RPM2", ¬_impl, NULL }, + { "RPM3", ¬_impl, NULL }, + { "SUCA", &suca, rp_suca_bits }, + { "SILO", ¬_impl, NULL } +}; + +/* Forward decls */ + +static t_stat rr_rd (int32 *data, int32 PA, int32 access); +static t_stat rr_wr (int32 data, int32 PA, int32 access); +static int32 rr_inta (void); +static t_stat rr_svc (UNIT *uptr); +static t_stat rr_reset (DEVICE *dptr); +static void rr_go (void); +static void rr_set_done (int32 error); +static void rr_clr_done (void); +static t_stat rr_boot (int32 unitno, DEVICE *dptr); +static t_stat rr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static t_stat rr_attach (UNIT *uptr, CONST char *cptr); +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_show_ctrl (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static const char *rr_description (DEVICE *dptr); + +/* RP11 data structures + + rr_reg RR register list + rr_unit RR unit list + rr_mod RR modifier list + rr_dev RR device descriptor +*/ + +static DIB rr_dib = { + IOBA_AUTO/*base address*/, RP_IOLN/*addresses used*/, + rr_rd, rr_wr, + 1/*# of vectors*/, IVCL(RR)/*locator*/, VEC_AUTO, + { rr_inta }, RP_IOLN/*addresses per device*/, +}; + +static REG rr_reg[] = { + /* registers */ + { ORDATADF(RPCS, rpcs, 16, "control/status", rp_cs_bits) }, + { ORDATADF(RPCA, rpca, 16, "cylinder address", rp_ca_bits) }, + { ORDATADF(RPDA, rpda, 16, "disk address", rp_da_bits) }, + { ORDATADF(RPBA, rpba, 16, "memory address", rp_ba_bits) }, + { ORDATADF(RPWC, rpwc, 16, "word count", rp_wc_bits) }, + { ORDATADF(RPDS, rpds, 16, "drive status", rp_ds_bits) }, + { ORDATADF(RPER, rper, 16, "error status", rp_er_bits) }, + { ORDATADF(SUCA, suca, 16, "current cylinder", rp_suca_bits) }, + { ORDATADF(WLOA, wloa, 16, "write lockout address", rp_wloa_bits) }, + + /* standard stuff */ + { FLDATAD (INT, IREQ(RR), INT_V_RR, "interrupt pending flag") }, + { FLDATAD (ERR, rpcs, CSR_V_ERR, "error flag (CSR<15>)") }, + { FLDATAD (DONE, rpcs, CSR_V_DONE, "device done flag (CSR<7>)") }, + { FLDATAD (IE, rpcs, CSR_V_IE, "interrupt enable flag (CSR<6>)") }, + { ORDATA (DEVADDR, rr_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, rr_dib.vec, 16), REG_HRO }, + { NULL } +}; + +static UNIT rr_unit[RP_NUMDR] = { + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) }, + { UDATA(rr_svc, + UNIT_FIX | UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | UNIT_RP03, + RP_SIZE_RP03) } +}; + +static MTAB rr_mod[] = { + { MTAB_VDV, 0, + "TYPE", NULL, + NULL, rr_show_ctrl, NULL, + "Display controller type" }, + { MTAB_VDV | MTAB_VALR, 0, + NULL, "PROTECT", + rr_set_wloa, NULL, NULL, + "Set write lockout mode/address" }, + { MTAB_VUN, 0, + "WRITEENABLED", "WRITEENABLED", + set_writelock, show_writelock, NULL, + "Write enable disk drive" }, + { MTAB_VUN, 1, + NULL, "LOCKED", + set_writelock, NULL, NULL, + "Write lock disk drive" }, + { MTAB_VUN, 0, + "TYPE", NULL, + NULL, rr_show_type, NULL, + "Display device type" }, + { MTAB_VUN, 0/*RP02*/, + NULL, RP_RP02, + rr_set_type, NULL, NULL, + "Set " RP_RP02 " disk type" }, + { MTAB_VUN, UNIT_RP03, + NULL, RP_RP03, + rr_set_type, NULL, NULL, + "Set " RP_RP03 " disk type"}, + { UNIT_NOAUTO, 0, + "autosize", "AUTOSIZE", + NULL, NULL, NULL, + "Set type based on file size at attach" }, + { UNIT_NOAUTO, UNIT_NOAUTO, + "noautosize", "NOAUTOSIZE", NULL, + NULL, NULL, + "Disable disk autosize on attach" }, + { MTAB_VUN | MTAB_VALR, 0, + "FORMAT", "FORMAT={AUTO|SIMH|VHD|RAW}", + sim_disk_set_fmt, sim_disk_show_fmt, NULL, + "Set/Display disk format" }, + { MTAB_VDV | MTAB_VALR, 010, + "ADDRESS", "ADDRESS", + set_addr, show_addr, NULL, + "Bus address" }, + { MTAB_VDV | MTAB_VALR, 0, + "VECTOR", "VECTOR", + set_vec, show_vec, NULL, + "Interrupt vector" }, + { 0 } +}; + +DEVICE rr_dev = { + "RR", rr_unit, rr_reg, rr_mod, RP_NUMDR, + 8/*address radix*/, 24/*address width*/, 1/*address increment*/, + 8/*data radix*/, RPWRDSZ/*data width*/, + 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, + 0/*debug control*/, rr_deb, + NULL/*msize()*/, NULL/*logical name*/, + rr_help, NULL/*attach_help()*/, NULL/*help_ctx*/, + rr_description, +}; + +/* I/O dispatch routine, I/O addresses 17776710 - 17776736 + + 17776710 RPDS read-only except for attention bits + 17776712 RPER read-only + 17776714 RPCS read/write + 17776716 RPWC read/write + 17776720 RPBA read/write + 17776722 RPCA read/write + 17776724 RPDA read/write + 17776726 RPM1 read-only, unimplemented + 17776730 RPM2 read-only, unimplemented + 17776732 RPM3 write-only, unimplemented + 17776734 SUCA read-only + 17776736 SILO read/write, unimplemented + +RP11-C actually responds to the range 17776700 - 17776736 with the first 4 word +locations unused. + +The original RP11 had the following differences: it responded to the address +range 17776710 - 17776746 (3 buffer registers RPB1-RPB3 followed RPM3, then 3 +unused 42-46). 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). The RP11 only supported the +RP02 disk drives, and so it only required 8 bits for cylinder addresses. The +RP03 bit in RPDS was always 0. But programmatically it was compatible with the +-C revision (except for the separate SUCA, which was not used in most software). + +RP11-E was just a newer version of RP11-C and supported both RP02 and RP03 disk +drives on the same controller. + +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. +*/ + +static t_stat rr_rd (int32 *data, int32 PA, int32 access) +{ + /* offset by base then decode <4:1> */ + int32 rn = ((PA - rr_dib.ba) >> 1) & 017; + UNIT* uptr; + + switch (rn) { + case 0: /* RPDS */ + case 1: /* RPER */ + case 2: /* RPCS */ + /* RPDS */ + uptr = rr_dev.units + GET_DRIVE(rpcs); /* selected unit */ + rpds &= RPDS_ATTN; /* attention bits */ + if (!(uptr->flags & UNIT_DIS)) { /* not disabled? */ + if (GET_DTYPE(uptr->flags)) + rpds |= RPDS_RP03; + if (uptr->flags & UNIT_ATT) { /* attached? */ + rpds |= RPDS_ONLN; + if (uptr->flags & UNIT_WPRT) /* write locked? */ + rpds |= RPDS_WLK; + if (uptr->SEEKING) /* still seeking? */ + rpds |= RPDS_SEEK; + else if (!sim_is_active(uptr)) /* idle? */ + rpds |= RPDS_RDY; + } + } + + /* RPER */ + rper &= RPER_REAL; + rper |= RPDS_DKER(rpds); + + /* RPCS */ + rpcs &= RPCS_REAL; + rpcs |= RPER_HARD(rper) | RPER_SOFT(rper); + + *data = *rr_regs[rn].valp; + break; + + case 3: /* RPWC */ + *data = rpwc; + break; + + case 4: /* RPBA */ + *data = rpba; + break; + + case 5: /* RPCA */ + *data = rpca; + break; + + case 6: /* RPDA */ + rpda &= RPDA_RW; + rpda |= (rand() % RP_NUMSC) << RPDA_V_SOT; /* a random sect */ + *data = rpda; + break; + + case 10: /* SUCA */ + *data = suca; + break; + + 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); + return SCPE_OK; +} + +#define RR_DATOB(r, d) (PA & 1 ? ((d) << 8) | ((r) & 0377) : ((r) & ~0377) | (d)) + +static t_stat rr_wr (int32 data, int32 PA, int32 access) +{ + /* offset by base then decode <4:1> */ + int32 rn = ((PA - rr_dib.ba) >> 1) & 017; + int32 n, old_val = *rr_regs[rn].valp; + + switch (rn) { + case 0: /* RPDS */ + if (access != WRITEB || !(PA & 1)) { + rpds &= ~(data & RPDS_ATTN); /* clr attention bits */ + if (!(rpds & RPDS_ATTN) && (rpcs & RPCS_AIE) + && (!(rpcs & CSR_IE) || !(rpcs & CSR_DONE))) { + sim_debug(RRDEB_INT, &rr_dev, "rr_wr(ATT:CLR_INT)\n"); + CLR_INT(RR); /* clr int request */ + } + } + break; + + case 1: /* RPER: read-only */ + break; + + case 2: /* RPCS */ + if (access == WRITEB) + data = RR_DATOB(rpcs, data); + if (!(data & (RPCS_AIE | CSR_IE))) { /* int disable? */ + sim_debug(RRDEB_INT, &rr_dev, "rr_wr(CSR:CLR_INT)\n"); + CLR_INT(RR); /* clr int request */ + } else if (((data & CSR_IE) + && (rpcs & (CSR_DONE | CSR_IE)) == CSR_DONE) || + ((data & RPCS_AIE) + && !(rpcs & RPCS_AIE) && (rpds & RPDS_ATTN))) { + sim_debug(RRDEB_INT, &rr_dev, "rr_wr(CSR:SET_INT)\n"); + SET_INT(RR); /* set int request */ + } + rpcs &= ~RPCS_RW; + rpcs |= data & RPCS_RW; + n = GET_DRIVE(rpcs); + if (n != GET_DRIVE(old_val)) { + UNIT* uptr = rr_dev.units + n; /* new selected unit */ + suca = uptr->CYL; + n = 1; + } else + n = 0; /* same old */ + if (!(rpcs & CSR_DONE)) { /* not ready? */ + if ((data & CSR_GO) || n) /* GO or de-selected? */ + rper |= RPER_PGE; + } else if (data & CSR_GO) /* new function? */ + rr_go(); + break; + + case 3: /* RPWC */ + if (access == WRITEB) + data = RR_DATOB(rpwc, data); + rpwc = data; + break; + + case 4: /* RPBA */ + if (access == WRITEB) + data = RR_DATOB(rpba, data); + rpba = data & RPBA_IMP; + break; + + case 5: /* RPCA */ + if (access == WRITEB) + data = RR_DATOB(rpca, data); + rpca = data & RPCA_IMP; + break; + + case 6: /* RPDA */ + if (access == WRITEB) + data = RR_DATOB(rpda, data); + rpda &= ~RPDA_RW; + rpda |= data & RPDA_RW; + break; + + case 10: /* SUCA: read-only */ + break; + + default: + return SCPE_OK; + } + sim_debug(RRDEB_RWR, &rr_dev, ">>RR write: %s=%#o\n", rr_regs[rn].name, data); + /* note that this is post-op; so e.g. it won't ever show the GO bit as 1 */ + sim_debug_bits(RRDEB_RWR, &rr_dev, rr_regs[rn].bits, old_val, *rr_regs[rn].valp, 1); + return SCPE_OK; +} + +/* Initiate new function */ + +static void rr_go (void) +{ + int32 i, cyl, head, sect, func, type; + t_bool rd, wr; + UNIT* uptr; + + assert(rpcs & CSR_DONE); + + func = GET_FUNC(rpcs); /* get function */ + if (func == RPCS_RESET) { /* control reset? */ + rpds = 0; + rper = 0; + rpcs = CSR_DONE; + rpwc = 0; + rpba = 0; + rpca = 0; + rpda = 0; + suca = rr_dev.units[0].CYL; + sim_debug(RRDEB_INT, &rr_dev, "rr_go(RESET:CLR_INT)\n"); + CLR_INT(RR); /* clr int request */ + return; + } + + rr_clr_done(); /* clear done */ + rper = 0; /* clear errors */ + rpcs &= ~(CSR_ERR | RPCS_HERR); /* clear summary */ + i = GET_DRIVE(rpcs); /* get drive no */ + uptr = rr_dev.units + i; /* selected unit */ + + if (!(uptr->flags & UNIT_ATT)) { /* not attached? */ + rr_set_done(RPER_PGE); + return; + } + + i = 0; /* errors detected */ + rd = func == RPCS_READ || func == RPCS_RD_NOSEEK || func == RPCS_WCHK; + wr = func == RPCS_WRITE || func == RPCS_WR_NOSEEK; + + if (wr && (uptr->flags & UNIT_WPRT)) /* write and locked? */ + i |= RPER_WPV; + if (uptr->SEEKING || sim_is_active(uptr)) /* still busy? */ + i |= RPER_PGE; + if (rpcs & RPCS_HDR) { /* format and ... */ + if (!(rpcs & RPCS_MODE)) /* ... not 18b? */ + i |= RPER_MODE; + else if (!(rd | wr) || /* ... or not R/W? or ... */ + (rd && -((int16) rpwc) != 3) || /* rd hdr: wc m.b. 3 */ + (wr && -((int16) rpwc) % 3)) { /* wr hdr: wc m.b. mult of 3 */ + i |= RPER_PGE; + } + } else if (rd | wr) { /* regular R/W and ... */ + if (rpcs & RPCS_MODE) /* ... 18b? */ + i |= RPER_MODE; +#if 0 /* per doc, rpwc must be even; but DOS/Batch uses odd wc xfers (?!) */ + else if (rpwc & 1) /* ... or odd wc? */ + i |= RPER_PGE; +#endif + } + sect = GET_SECT(rpda); + if (sect >= RP_NUMSC) + i |= RPER_NXS; + type = GET_DTYPE(uptr->flags); /* get drive type */ + if (func == RPCS_HOME) { + head = 0; + cyl = 0; + } else if (func == RPCS_RD_NOSEEK || func == RPCS_WR_NOSEEK) { + head = uptr->HEAD; + cyl = uptr->CYL; + } else { + head = GET_TRACK(rpda); + cyl = rpca; + if (head >= RP_NUMSF) + i |= RPER_NXT; + if (cyl >= drv_tab[type].cyl) + i |= RPER_NXC; + } + if ((wloa & RPWLOA_ON) && wr + && !(i & (RPER_WPV | RPER_NXC | RPER_NXT | RPER_NXS))) { + if (GET_DRIVE(rpcs) <= GET_WLOADRV(wloa)) /* unit protected? */ + i |= RPER_WPV; + else if (cyl <= GET_WLOACYL(wloa)) /* cyl protected? */ + i |= RPER_WPV; + } + if (i) { + rr_set_done(i); /* set done */ + return; + } + + /* seek time */ + if (func == RPCS_HOME) + i = drv_tab[type].seek_ave / 2; + else if (!(i = abs(cyl - uptr->CYL))) + i = drv_tab[type].seek_1 / 2; + else if (i <= 2) + i *= drv_tab[type].seek_1; + else if (i <= (3 * drv_tab[type].cyl) / 4) + i = drv_tab[type].seek_ave; + else + i = drv_tab[type].seek_max; + if (func == RPCS_SEEK || func == RPCS_HOME) { /* seek? */ + uptr->SEEKING = 1; /* start seeking */ + rr_set_done(0); /* set done */ + sim_activate(uptr, i / 10); /* schedule */ + } else + sim_activate(uptr, (i + RP_ROT_12) / 10); /* I/O takes longer */ + + uptr->CYL = cyl; /* put on cylinder */ + uptr->HEAD = head; /* save head too */ + uptr->FUNC = func; /* save func */ + return; +} + +/* Complete seek */ + +static t_stat rr_seek_done (UNIT *uptr, t_bool cancel) +{ + int32 n; + + assert(uptr->SEEKING); + assert(uptr->FUNC == RPCS_SEEK || uptr->FUNC == RPCS_HOME); + n = (int32)(uptr - rr_dev.units); /* get drv number */ + if (n == GET_DRIVE(rpcs)) + suca = cancel ? 0 : uptr->CYL; /* update cyl shown */ + uptr->SEEKING = 0; /* set seek done */ + assert((1 << n) | RPDS_ATTN); + rpds |= 1 << n; /* set attention */ + if (rpcs & RPCS_AIE) { /* att ints enabled? */ + sim_debug(RRDEB_INT, &rr_dev, "rr_seek_done(SET_INT)\n"); + SET_INT(RR); + } + return SCPE_OK; +} + +/* Service a unit + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and disk address for + the current command. + + Some registers must be revalidated because they could have been + modified in between go() and now. +*/ + +static t_stat rr_svc (UNIT *uptr) +{ + int32 func, cyl, head, sect, da, err, wc, n; + t_seccnt todo, done; + t_stat ioerr; + uint32 ma; + t_bool rd; + + if (uptr->SEEKING) /* seek? */ + return rr_seek_done(uptr, 0); + + func = uptr->FUNC; + assert(func && ~(rpcs & CSR_DONE)); + + if (!(uptr->flags & UNIT_ATT)) { /* attached? */ + rr_set_done(RPER_PGE); + return SCPE_UNATT; + } + sect = GET_SECT(rpda); /* get sect */ + if (sect >= RP_NUMSC) { /* bad sector? */ + rr_set_done(RPER_NXS); + return SCPE_OK; + } + + rd = func == RPCS_READ || func == RPCS_RD_NOSEEK || func == RPCS_WCHK; + wc = 0200000 - rpwc; /* get wd cnt */ + cyl = uptr->CYL; + head = uptr->HEAD; + n = GET_DTYPE(uptr->flags); /* get drive type */ + assert(cyl < drv_tab[n].cyl && head < RP_NUMSF); + da = GET_DA(cyl, head, sect); /* form full disk addr */ + assert(da < drv_tab[n].size); + n = drv_tab[n].size - da; /* sectors available */ + err = 0; /* errors detected */ + if (rpcs & RPCS_HDR) { /* header ops? */ + if (!(rpcs & RPCS_MODE)) + err |= RPER_MODE; /* must be in 18b mode */ + else if ((rd && wc != 3) || (!rd && wc % 3)) /* DEC-11-HRPCA-C-D 3.8 */ + err |= RPER_PGE; + else if (rd) /* a typo in doc??? */ + n = 3; /* can only read 3 wds */ + else + n *= 3; /* 3 wds per sector */ + } else { /* no: regular R/W */ + if (rpcs & RPCS_MODE) + err |= RPER_MODE; /* must be in PDP-11 mode */ +#if 0 /* per doc, wc must be even; but DOS/Batch uses odd wc xfers (?!) */ + else if (wc & 1) /* must be even */ + err |= RPER_PGE; +#endif + else + n *= RP_NUMWD; /* can do this many */ + } + if (err) { + rr_set_done(err); + return SCPE_OK; + } + if (wc > n) + wc = n; + assert(wc); + + /* A note on error handling: + * RP11 processes data words between drive and memory absolutely sequentially + * (not by the sector like the SIMH API provides). Therefore, controller + * errors should be asserted to follow that scenario. + * + * 1. When reading from disk, an I/O error must be deferred until all words + * (read from the disk so far) have been verified not to cause NXM. If any + * word did, then NXM gets reported, and the I/O error gets discarded + * (because the real controller would have stopped the operation right then + * and there, and would not have encountered the (later) I/O condition). + * + * 2. When writing, I/O errors take precedence, provided that words keep + * passing the address check in extraction from memory. But no NXM should + * be reported for any words that reside after the completed I/O boundary in + * case of a short write to disk. + * + * 3. Disk pack overrun is strictly a run-off of an otherwise successful + * completion, which has left a residual word counter non-zero, because had + * an earlier error stopped the disk operation, the overrun situation could + * not have been experienced (the end of pack would not have been reached). + */ + + ma = ((rpcs & RPCS_MEX) << (16 - RPCS_V_MEX)) | rpba; /* get mem addr */ + + if (rd) { /* read */ + if (rpcs & RPCS_HDR) { /* format? */ + /* Sector header is loaded in the 36-bit Buffer Register(BR): + 17 0-bits, 9-bit cyl, 5-bit track, a spare bit, 4-bit sect */ + rpxb[0] = 0; /* BR<35:20> */ + rpxb[1] = (cyl << 6) | (head << 1); /* BR<19:04> */ + rpxb[2] = sect; /* BR<03:00> */ + ioerr = 0; + done = 1; /* 1 sector done */ + } else { /* normal read */ + DEVICE* dptr = find_dev_from_unit(uptr); + todo = (wc + (RP_NUMWD - 1)) / RP_NUMWD; /* sectors to read */ + ioerr = sim_disk_rdsect(uptr, da, (uint8*) rpxb, &done, todo); + n = done * RP_NUMWD; /* words read */ + sim_disk_data_trace(uptr, (uint8*) rpxb, da, n * sizeof(*rpxb), "rr_read", + RRDEB_DAT & (dptr->dctrl | uptr->dctrl), RRDEB_OPS); + if (done >= todo) + ioerr = 0; /* good stuff */ + else if (ioerr) + wc = n; /* short, adj wc */ + else { + todo -= done; /* to clear ... */ + todo *= RP_NUMWD * sizeof(*rpxb); /* ... bytes */ + memset(rpxb + n, 0, todo); + } + } + if (func == RPCS_WCHK) { + uint32 a = ma; + for (n = 0; n < wc; ++n) { /* loop thru buf */ + RPCONTR data; + if (MAP_RDW(a, 1, &data)) { /* mem wd */ + err |= RPER_NXM; /* NXM? set flg */ + break; + } + a += 2; + if (ioerr) + continue; + if (data != rpxb[n]) /* match to disk? */ + err |= RPER_WCE; /* no, err */ + } + n %= wc; + } else if ((n = MAP_WRW(ma, wc, rpxb))) { /* store buf */ + err |= RPER_NXM; /* NXM? set flag */ + wc -= n; /* adj wd cnt */ + } + if (!n && ioerr) { /* all wrds ok but I/O? */ + err |= RPER_FMTE; /* report as FMTE */ + if (func == RPCS_WCHK) + err |= RPER_WCE; + } + } else { /* write */ + if ((n = MAP_RDW(ma, wc, rpxb))) /* get buf */ + wc -= n; /* adj wd cnt */ + if (wc && !(rpcs & RPCS_HDR)) { /* regular write? */ + DEVICE* dptr = find_dev_from_unit(uptr); + int32 m = (wc + (RP_NUMWD - 1)) & ~(RP_NUMWD - 1); /* clr to */ + memset(rpxb + wc, 0, (m - wc) * sizeof(*rpxb)); /* end of blk */ + sim_disk_data_trace(uptr, (uint8*) rpxb, da, m * sizeof(*rpxb), "rr_write", + RRDEB_DAT & (dptr->dctrl | uptr->dctrl), RRDEB_OPS); + todo = m / RP_NUMWD; /* sectors to write */ + ioerr = sim_disk_wrsect(uptr, da, (uint8*) rpxb, &done, todo); + if (done < todo) { + wc = done * RP_NUMWD; /* words written */ + err |= RPER_FMTE; /* report as FMTE */ + if (!ioerr) + ioerr = 1; /* just in case */ + } else if (n) + err |= RPER_NXM; /* NXM? set flg */ + else + ioerr = 0; /* good stuff */ + } else { + ioerr = 0; /* good stuff */ + done = wc / 3; + if (n) + err |= RPER_NXM; /* NXM? set flg */ + } + } + + if (wc) { /* any xfer? */ + rpwc += wc; + rpwc &= 0177777; + ma += wc << 1; + rpba = ma & RPBA_IMP; + rpcs &= ~RPCS_MEX; + rpcs |= (ma >> (16 - RPCS_V_MEX)) & RPCS_MEX; + + assert(done); + rd = func == RPCS_RD_NOSEEK || func == RPCS_WR_NOSEEK; + if (!rd || --done) { /* w/SEEK or 2+ sects? */ + da += done; /* update DA */ + n = GET_DTYPE(uptr->flags); /* drive type */ + assert(da <= drv_tab[n].size); + head = da / RP_NUMSC; /* new head (w/cyl) */ + cyl = head / RP_NUMSF; /* new cyl */ + if (cyl == drv_tab[n].cyl) { /* at the end? */ + cyl = drv_tab[n].cyl - 1; /* last cyl and ... */ + head = RP_NUMSF - 1; /* ... head keep on */ + } else + head %= RP_NUMSF; /* wrap up head */ + n = (int32)(uptr - rr_dev.units); /* get drv number */ + if (!rd) /* w/SEEK I/O? */ + uptr->HEAD = head; /* yes: select new head */ + else if (uptr->CYL != cyl || /* no: arm moved or */ + (rpwc && !(err | ioerr))) { /* boundary exceeded? */ + assert((1 << n) & RPDS_ATTN); + rpds |= 1 << n; /* set attention */ + if (rpcs & RPCS_AIE) { /* att ints enabled? */ + sim_debug(RRDEB_INT, &rr_dev, "rr_svc(SET_INT)\n"); + SET_INT(RR); /* request interrupt */ + } + } + uptr->CYL = cyl; /* update new cyl */ + if (n == GET_DRIVE(rpcs)) + suca = uptr->CYL; /* let it out */ + } + } else + assert(err); + + if (rpwc && !(err | ioerr)) + err |= RPER_EOP; /* disk pack overrun */ + rr_set_done(err); + + if (ioerr) { /* I/O error? */ + sim_perror("RR I/O error"); + return SCPE_IOERR; + } + return SCPE_OK; +} + +/* Interrupt state change routines + + rr_clr_done clear done + rr_set_done set done and possibly errors + rr_inta interrupt acknowledge +*/ + +static void rr_clr_done (void) +{ + rpcs &= ~CSR_DONE; /* clear done */ + if ((rpcs & CSR_IE) && (!(rpcs & RPCS_AIE) || !(rpds & RPDS_ATTN))) { + sim_debug(RRDEB_INT, &rr_dev, "rr_clr_done(CLR_INT)\n"); + CLR_INT(RR); /* clear int req */ + } + return; +} + +static void rr_set_done (int32 error) +{ + rper |= error; + rpcs |= CSR_DONE; /* set done */ + if (rpcs & CSR_IE) { /* int enable? */ + sim_debug(RRDEB_INT, &rr_dev, "rr_set_done(SET_INT)\n"); + SET_INT(RR); /* request int */ + } + return; +} + +static int32 rr_inta (void) +{ + sim_debug(RRDEB_INT, &rr_dev, "rr_inta()\n"); + assert(((rpcs & RPCS_AIE) && (rpds & RPDS_ATTN)) || + ((rpcs & CSR_IE) && (rpcs & CSR_DONE))); + rpcs &= ~RPCS_AIE; /* AIE is one-shot */ + return rr_dib.vec; /* return vector */ +} + +/* Device reset */ + +static t_stat rr_reset (DEVICE *dptr) +{ + int32 i; + + /* some sanity check first */ + assert(sizeof(rr_regs)/sizeof(rr_regs[0]) == RP_IOLN/2); + + /* clear everything now */ + rpds = 0; + rper = 0; + rpcs = CSR_DONE; + rpwc = 0; + rpba = 0; + rpca = 0; + rpda = 0; + suca = 0; + assert(dptr == &rr_dev); + sim_debug(RRDEB_INT, &rr_dev, "rr_reset(CLR_INT)\n"); + CLR_INT(RR); + for (i = 0; i < RP_NUMDR; ++i) { + UNIT* uptr = rr_dev.units + i; + sim_cancel(uptr); + uptr->CYL = 0; + uptr->FUNC = 0; + uptr->HEAD = 0; + uptr->SEEKING = 0; + } + if (rpxb == NULL) + rpxb = (RPCONTR*) calloc(RP_MAXFR, sizeof (*rpxb)); + if (rpxb == NULL) + return SCPE_MEM; + return auto_config(NULL, 0); +} + +/* Attach/detach routines */ + +static t_stat rr_attach (UNIT *uptr, CONST char *cptr) +{ + static const char* rr_types[] = { RP_RP03, RP_RP02, NULL }; + int32 type = GET_DTYPE(uptr->flags); + return sim_disk_attach_ex2(uptr, cptr, + RP_NUMWD * sizeof(*rpxb), sizeof (*rpxb), + TRUE, 0, drv_tab[type].name, + 0, 0, rr_types, 0); +} + +static t_stat rr_detach (UNIT *uptr) +{ + if (uptr->SEEKING) + rr_seek_done(uptr, 1/*cancel*/); + else if (sim_is_active(uptr)) + rr_set_done(RPER_TE); + sim_cancel(uptr); + uptr->CYL = 0; + uptr->FUNC = 0; + uptr->HEAD = 0; + return sim_disk_detach(uptr); +} + +/* Set / show drive type */ + +static t_stat rr_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if ((val & ~UNIT_RP03) || cptr) + return SCPE_ARG; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + uptr->capac = RP_SIZE(drv_tab[GET_DTYPE(val)].size); + uptr->flags &= ~UNIT_RP03; + uptr->flags |= val; + return SCPE_OK; +} + +static t_stat rr_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fputs(drv_tab[GET_DTYPE(uptr->flags)].name, st); + return SCPE_OK; +} + +/* Set WLOA */ + +static t_stat rr_set_wloa (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE* dptr = find_dev_from_unit(uptr); + if (!cptr || !*cptr) + return SCPE_ARG; + if (strcasecmp(cptr, "OFF") == 0) { + wloa &= ~RPWLOA_ON; + return SCPE_OK; + } + if (strncasecmp(cptr, "ON", 2) != 0) + return SCPE_ARG; + cptr += 2; + if (*cptr == ';') { + char* end; + long val; + errno = 0; + val = strtol(++cptr, &end, 0); + if (errno || !end || *end || end == cptr || (val & ~RPWLOA_IMPL)) + return SCPE_ARG; + wloa &= ~RPWLOA_IMPL; + wloa |= val; + } else if (!*cptr) + return SCPE_2FARG; + wloa |= RPWLOA_ON; + return SCPE_OK; +} + +/* Device bootstrap */ + +static t_stat rr_boot (int32 unitno, DEVICE *dptr) +{ + return SCPE_NOFNC; +} + +/* 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; + fputs( + /*567901234567890123456789012345678901234567890123456789012345678901234567890*/ + RP_DESCRIPTION "\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" + "(DEC-11-HRPCA-C-D).\n\n" + "In default configuration " RP_RP11 " responds to the range 17776700 - 17776736\n" + "with the first 4 word locations not occupied by any device registers (and\n" + "so 17776710 is the first used location). Some operating systems want you\n" + "to specify the extended range (e.g. RSTS/E), but some -- the relevant range\n" + "(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:\n\n" + " 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]) - 1; ++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" + " %5.1fMB %5.1fMB %5u.%1u\n", drv_tab[i].name, + drv_tab[i].cyl, drv_tab[i].spare, RP_NUMSF, RP_NUMSC, + RP_SIZE(total - spare) / .5e6, RP_SIZE(total) / .5e6, + (drv_tab[i].seek_ave + RP_ROT_12)/10, + (drv_tab[i].seek_ave + RP_ROT_12)%10); + } + fputs("\n" + "The implementation does not include any maintenance registers or disk\n" + "formatting operations yet supports the Write Lockout Address (LOA)\n" + "register, 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,\n" + "and 7x2=14 first cylinders of unit 2 will become write-locked).\n" + "The current setting can be obtained by examining the WLOA register in\n" + "the device:\n\n" + " sim> EXAMINE RR WLOA\n" + " WLOA: 100407 PROTECT=ON DRV=1 CYL2=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" + "Note that it does not clear the address but turns the feature off.\n", st); + fprint_set_help (st, dptr); + fprint_show_help(st, dptr); + fprintf(st, + "\nThe " RP_RP11 " is disabled in a Qbus system with more than 256KB of memory.\n"); + fprint_reg_help (st, dptr); + return SCPE_OK; +} + +static const char *rr_description (DEVICE *dptr) +{ + return RP_DESCRIPTION; +} + +/* Show / switch controller type */ + +static t_stat rr_show_ctrl (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fputs(RP_RP11, st); + return SCPE_OK; +} + +#elif !defined(UC15) +#error "RP11/-C/-E can only be used in PDP-11 configuration" +#endif /*VM_PDP11 && !UC15*/ diff --git a/PDP11/pdp11_sys.c b/PDP11/pdp11_sys.c index 2f9ee567..d5da6d9b 100644 --- a/PDP11/pdp11_sys.c +++ b/PDP11/pdp11_sys.c @@ -101,6 +101,7 @@ extern DEVICE rx_dev; extern DEVICE ry_dev; extern DEVICE mba_dev[]; extern DEVICE rp_dev; +extern DEVICE rr_dev; extern DEVICE rs_dev; extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; extern DEVICE tm_dev; @@ -182,6 +183,7 @@ DEVICE *sim_devices[] = { &rx_dev, &ry_dev, &rp_dev, + &rr_dev, &rs_dev, &rq_dev, &rqb_dev, diff --git a/Visual Studio Projects/PDP11.vcproj b/Visual Studio Projects/PDP11.vcproj index 41febf59..46ae15b2 100644 --- a/Visual Studio Projects/PDP11.vcproj +++ b/Visual Studio Projects/PDP11.vcproj @@ -325,6 +325,10 @@ RelativePath="..\PDP11\pdp11_rp.c" > + + diff --git a/Visual Studio Projects/PDP11.vcxproj b/Visual Studio Projects/PDP11.vcxproj index 58ffc4b3..5dce26f7 100755 --- a/Visual Studio Projects/PDP11.vcxproj +++ b/Visual Studio Projects/PDP11.vcxproj @@ -284,6 +284,7 @@ + diff --git a/Visual Studio Projects/PDP11.vcxproj.filters b/Visual Studio Projects/PDP11.vcxproj.filters index dcb8ec65..7af268cd 100755 --- a/Visual Studio Projects/PDP11.vcxproj.filters +++ b/Visual Studio Projects/PDP11.vcxproj.filters @@ -117,6 +117,9 @@ Source Files + + Source Files + Source Files diff --git a/makefile b/makefile index 39aba638..1a55f947 100644 --- a/makefile +++ b/makefile @@ -1493,7 +1493,7 @@ PDP11 = ${PDP11D}/pdp11_fp.c ${PDP11D}/pdp11_cpu.c ${PDP11D}/pdp11_dz.c \ ${PDP11D}/pdp11_vt.c ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_io_lib.c \ ${PDP11D}/pdp11_rom.c ${PDP11D}/pdp11_ch.c ${PDP11D}/pdp11_dh.c \ ${PDP11D}/pdp11_ng.c ${PDP11D}/pdp11_daz.c ${PDP11D}/pdp11_tv.c \ - ${PDP11D}/pdp11_mb.c \ + ${PDP11D}/pdp11_mb.c ${PDP11D}/pdp11_rr.c \ ${DISPLAYL} ${DISPLAYNG} ${DISPLAYVT} PDP11_OPT = -DVM_PDP11 -I ${PDP11D} ${NETWORK_OPT} ${DISPLAY_OPT}