root/src/dps8/dps8_disk.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. disk_show_nunits
  2. disk_set_nunits
  3. disk_show_type
  4. disk_set_type
  5. dsk_show_device_name
  6. dsk_set_device_name
  7. signal_disk_ready
  8. disk_set_ready
  9. unloadDisk
  10. loadDisk
  11. disk_reset
  12. disk_attach
  13. disk_init
  14. diskSeek64
  15. diskSeek512
  16. diskSeekSpecial
  17. diskRead
  18. diskWrite
  19. readStatusRegister
  20. diskRdCtrlReg
  21. read_configuration
  22. read_and_clear_statistics
  23. dsk_iom_cmd
  24. ipc_show_nunits
  25. ipc_set_nunits
  26. ipc_show_device_name
  27. ipc_set_device_name
  28. msp_show_nunits
  29. msp_set_nunits
  30. msp_show_device_name
  31. msp_set_device_name

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: 22079097-f62e-11ec-b987-80ee73e9b8e7
   6  *
   7  * ---------------------------------------------------------------------------
   8  *
   9  * Copyright (c) 2007-2013 Michael Mondy
  10  * Copyright (c) 2012-2016 Harry Reed
  11  * Copyright (c) 2013-2018 Charles Anthony
  12  * Copyright (c) 2021-2024 The DPS8M Development Team
  13  *
  14  * This software is made available under the terms of the ICU License.
  15  * See the LICENSE.md file at the top-level directory of this distribution.
  16  *
  17  * ---------------------------------------------------------------------------
  18  *
  19  * This source file may contain code comments that adapt, include, and/or
  20  * incorporate Multics program code and/or documentation distributed under
  21  * the Multics License.  In the event of any discrepancy between code
  22  * comments herein and the original Multics materials, the original Multics
  23  * materials should be considered authoritative unless otherwise noted.
  24  * For more details and historical background, see the LICENSE.md file at
  25  * the top-level directory of this distribution.
  26  *
  27  * ---------------------------------------------------------------------------
  28  */
  29 
  30 //-V::536
  31 
  32 // source/library_dir_dir/system_library_1/source/bound_volume_rldr_ut_.s.archive/rdisk_.pl1
  33 // source/library_dir_dir/system_library_1/source/bound_rcp_.s.archive/rcp_disk_.pl1
  34 
  35 #include <stdio.h>
  36 
  37 #include "dps8.h"
  38 #include "dps8_iom.h"
  39 #include "dps8_disk.h"
  40 #include "dps8_sys.h"
  41 #include "dps8_cable.h"
  42 #include "dps8_cpu.h"
  43 #include "dps8_faults.h"
  44 #include "dps8_scu.h"
  45 #include "sim_disk.h"
  46 #include "dps8_utils.h"
  47 
  48 #if defined(LOCKLESS)
  49 # include "threadz.h"
  50 #endif /* if defined(LOCKLESS) */
  51 
  52 #define DBG_CTR 1
  53 
  54 //
  55 // A possible disk data packing algorithm
  56 //
  57 // Currently sectors are 512 * word36. Word36 is 64bits; that 56% utilization,
  58 // A computationally efficient packing would improve disk throughput and storage
  59 // efficiency
  60 //
  61 //     word36 in[512]
  62 //     struct dsksec
  63 //       {
  64 //         uint32 low32[512];
  65 //         uint8 high4[512];
  66 //       } out;
  67 //     uint32 * map = (uint32 *) in;
  68 //     uint8 * highmap = (uint8 *) in;
  69 //
  70 //     for (uint i = 0; i < 512; i ++)
  71 //       {
  72 //         out . low32[i] = map[i * 2];
  73 //         out . high4[i] = (uint8) (map[i * 2 + 1]);
  74 //       }
  75 //
  76 // This 36/40 -- 90% utilization; at the cost of the scatter/gather and
  77 // the cost of emulated disk sectors size not a multiple of host disk
  78 // sector size.
  79 //
  80 
  81 // dau_type stat_mpc_.pl1
  82 //
  83 //  dcl  ddev_model (0:223) char (6) static options (constant)
  84 //          init ((84) (""), "  190A", (52) (""), "  190B", "   401", "  190B", (14) (""), "   451", (31) (""), "   402",
  85 //          (13) (""), "   500", "   501", "   500", "   501", "   500", "   501", "   500", "   501", (9) (""), "   509",
  86 //          "", "   509", "", "   509", "", "   509");
  87 
  88 //  84   0:83   ""
  89 //   1  84      "190A"
  90 //  52  85:135  ""
  91 //   1  137     "190B
  92 //   1  138     "401"
  93 //   1  139     "190B"
  94 //  14  140:153 ""
  95 //   1  154     "451"
  96 //  31  155:185 ""
  97 //   1  186     "402"
  98 //  13  175:199 ""
  99 //   1  200     "500"
 100 //   1  201     "501"
 101 //   1  202     "500"
 102 //   1  203     "501"
 103 //   1  204     "500"
 104 //   1  205     "501"
 105 //   1  206     "500"
 106 //   1  207     "501"
 107 //   9  208:216 ""
 108 //   1  217     "509"
 109 //   1  218     ""
 110 //   1  219     "509"
 111 //   1  220     ""
 112 //   1  221     "509"
 113 //   1  222     ""
 114 //   1  223     "509"
 115 
 116 // fs_dev.sect_per_dev:
 117 //           vfd       36/4000000          Bulk
 118 //           vfd       36/814*40*19        MSU0500
 119 //           vfd       36/814*40*19        MSU0450
 120 //           vfd       36/410*40*19        MSU0400
 121 //           vfd       36/410*31*19        DSU190
 122 //           vfd       36/202*18*20        DSU181
 123 //           vfd       36/840*64*20        MSU0501
 124 //           vfd       36/885*255          FIPS 3380
 125 //           vfd       36/1770*255         FIPS 3381
 126 
 127 /* table of # last sector number for each device */
 128 //dcl  last_sect_num (9) fixed bin (24) static options (constant) init
 129 //     (0, 618639, 616359, 309319, 239722, 71999, 1075199, 225674, 451349);
 130 /* table of # of last sector on device (includes T&D cylinders) */
 131 //dcl  last_physical_sect_num (9) fixed bin (24) static options (constant) init
 132 //     (0, 639919, 619399, 312359, 242249, 72359, 1077759, 225674, 451859);
 133 
 134 //          last     t&d last
 135 // D500     618639    639919
 136 // D451     616359    619399
 137 // D400     309319    312359
 138 // D190     239722    242249
 139 // D181      71999     72359
 140 // D501    1075199   1077759
 141 // 3380     225674    225674
 142 // 3381     451349    451859
 143 
 144 struct diskType_t diskTypes[] = {
 145   { "3381",  451350,  0, false, seek_512, 512,   0 }, // disk_init assumes 3381 is at index 0
 146   { "d500",  618640,  1, false, seek_64,   64, 200 },
 147   { "d451",  616360,  1, true,  seek_64,   64, 154 },
 148   { "d400",  309320,  1, true,  seek_64,   64,  84 }, // d400 is a d190 with "high-efficiency format (40 sectors/track)"
 149   { "d190",  239723,  1, true,  seek_64,   64,  84 }, // 190A 84, 190B 137
 150   { "d181",   72000,  1, true,  seek_64,   64,   0 }, // no idea what the dau idx is
 151   { "d501", 1075200,  1, false, seek_64,   64, 201 },
 152   { "3380",  225675,  0, false, seek_512, 512,   0 }, // 338x is never attached to a dau
 153 };
 154 #define N_DISK_TYPES (sizeof (diskTypes) / sizeof (struct diskType_t))
 155 
 156 static uint tAndDCapac[N_DISK_TYPES] = { 451860, 639920, 619400, 312360, 242250, 72360, 1077760, 225675 };
 157 
 158 #define M3381_SECTORS (1770*255)
 159 //#define M3381_SECTORS 6895616
 160 // records per subdev: 74930 (127 * 590)
 161 // number of sub-volumes: 3
 162 // records per dev: 3 * 74930 = 224790
 163 // cyl/sv: 590
 164 // cyl: 1770 (3*590)
 165 // rec/cyl 127
 166 // tracks/cyl 15
 167 // sector size: 512
 168 // sectors: 451858
 169 // data: 3367 MB, 3447808 KB, 6895616 sectors,
 170 //  3530555392 bytes, 98070983 records?
 171 
 172 #define N_DISK_UNITS 2 // default
 173 
 174 struct dsk_state dsk_states [N_DSK_UNITS_MAX];
 175 
 176 //-- // extern t_stat disk_svc(UNIT *up);
 177 
 178 // ./library_dir_dir/include/fs_dev_types.incl.alm
 179 //
 180 // From IBM GA27-1661-3_IBM_3880_Storage_Control_Description_May80, pg 4.4:
 181 //
 182 //  The Seek command transfers the six-byte seek address from the channel to
 183 //  the storage director....
 184 //
 185 //     Bytes 0-5: 0 0 C C 0 H
 186 //
 187 //       Model       Cmax   Hmax
 188 //       3330-1       410     18
 189 //       3330-11      814     18
 190 //       3340 (35MB)  348     11
 191 //       3340 (70MB)  697     11
 192 //       3344         697     11
 193 //       3350         559     29
 194 //
 195 //  Search Identifier Equal [CC HH R]
 196 //
 197 
 198 // ./library_dir_dir/system_library_1/source/bound_page_control.s.archive/disk_control.pl1
 199 //
 200 // dcl     devadd             fixed bin (18);              /* record number part of device address */
 201 //
 202 //  /* Compute physical sector address from input info.  Physical sector result
 203 //   accounts for unused sectors per cylinder. */
 204 //
 205 //        if pvte.is_sv then do;                  /* convert the subvolume devadd to the real devadd */
 206 //             record_offset = mod (devadd, pvte.records_per_cyl);
 207 //             devadd = ((devadd - record_offset) * pvte.num_of_svs) + pvte.record_factor + record_offset;
 208 //        end;
 209 //        sector = devadd * sect_per_rec (pvte.device_type);/* raw sector. */
 210 //        cylinder = divide (sector, pvtdi.usable_sect_per_cyl, 12, 0);
 211 //        sector = sector + cylinder * pvtdi.unused_sect_per_cyl;
 212 //        sector = sector + sect_off;                     /* sector offset, if any. */
 213 //
 214 
 215 // DB37rs DSS190 Disk Subsystem, pg. 27:
 216 //
 217 //   Seek instruction:
 218 //     Sector count limit, bits 0-11: These bits define the binary sector count.
 219 //     All zeros is a maximum count of 4096.
 220 //   Track indicator, bits 12-13:
 221 //     These bits indicate a complete track as good, defective, or alternate.
 222 //       00 = primary track - good
 223 //       01 = alternate track - good
 224 //       10 = defective track - alternate track assigned
 225 //       11 = defective track - no alternate track assigned
 226 //
 227 //   Sector address, bits 16-35
 228 //
 229 //      0                                                  35
 230 //      XXXX  XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX 0000
 231 //        BYTE 0         1           2           3           4
 232 //
 233 //  Seek        011100
 234 //  Read        010101
 235 //  Read ASCII  010011
 236 //  Write       011001
 237 //  Write ASCII 011010
 238 //  Write and compare
 239 //              011011
 240 //  Request status
 241 //              000000
 242 //  reset status
 243 //              100000
 244 //  bootload control store
 245 //              001000
 246 //  itr boot    001001
 247 //
 248 
 249 #if defined(POLTS_DISK_TESTING)
 250 static int nCmds = 0;
 251 #endif /* if defined(POLTS_DISK_TESTING) */
 252 
 253 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | \
 254                      UNIT_DISABLE | UNIT_IDLE | DKUF_F_STD )
 255 UNIT dsk_unit[N_DSK_UNITS_MAX] = {
 256 #if defined(NO_C_ELLIPSIS)
 257   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 258   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 259   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 260   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 261   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 262   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 263   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 264   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 265   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 266   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 267   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 268   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 269   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 270   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 271   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 272   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 273   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 274   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 275   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 276   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 277   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 278   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 279   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 280   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 281   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 282   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 283   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 284   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 285   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 286   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 287   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 288   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 289   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 290   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 291   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 292   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 293   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 294   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 295   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 296   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 297   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 298   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 299   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 300   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 301   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 302   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 303   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 304   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 305   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 306   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 307   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 308   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 309   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 310   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 311   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 312   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 313   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 314   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 315   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 316   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 317   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 318   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 319   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 320   { UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 321 #else
 322   [0 ... N_DSK_UNITS_MAX-1] = {
 323     UDATA (/* & disk_svc */ NULL, UNIT_FLAGS, M3381_SECTORS), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 324   }
 325 #endif
 326 };
 327 
 328 #define DSK_UNIT_IDX(uptr) ((uptr) - dsk_unit)
 329 
 330 static DEBTAB disk_dt[] =
 331   {
 332     { "TRACE",  DBG_TRACE,  NULL },
 333     { "NOTIFY", DBG_NOTIFY, NULL },
 334     { "INFO",   DBG_INFO,   NULL },
 335     { "ERR",    DBG_ERR,    NULL },
 336     { "WARN",   DBG_WARN,   NULL },
 337     { "DEBUG",  DBG_DEBUG,  NULL },
 338     { "ALL",    DBG_ALL,    NULL }, // Don't move as it messes up DBG message
 339     { NULL,     0,          NULL }
 340   };
 341 
 342 static t_stat disk_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val, UNUSED const void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 343   {
 344     sim_printf("Number of DISK units in system is %d\n", dsk_dev . numunits);
 345     return SCPE_OK;
 346   }
 347 
 348 static t_stat disk_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr, UNUSED void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 349   {
 350     if (! cptr)
 351       return SCPE_ARG;
 352     int n = atoi (cptr);
 353     if (n < 1 || n > N_DSK_UNITS_MAX)
 354       return SCPE_ARG;
 355     dsk_dev . numunits = (uint32) n;
 356     return SCPE_OK;
 357   }
 358 
 359 static t_stat disk_show_type (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val, UNUSED const void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 360   {
 361     int diskUnitIdx = (int) DSK_UNIT_IDX (uptr);
 362     if (diskUnitIdx < 0 || diskUnitIdx >= N_DSK_UNITS_MAX)
 363       {
 364         sim_printf ("error: Invalid unit number %ld\n", (long) diskUnitIdx);
 365         return SCPE_ARG;
 366       }
 367 
 368     sim_printf("type     : %s", diskTypes[dsk_states[diskUnitIdx].typeIdx].typename);
 369 
 370     return SCPE_OK;
 371   }
 372 
 373 static t_stat disk_set_type (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr, UNUSED void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 374   {
 375     int diskUnitIdx = (int) DSK_UNIT_IDX (uptr);
 376     if (diskUnitIdx < 0 || diskUnitIdx >= N_DSK_UNITS_MAX)
 377       {
 378         sim_printf ("error: Invalid unit number %ld\n", (long) diskUnitIdx);
 379         return SCPE_ARG;
 380       }
 381 
 382     uint i;
 383     for (i = 0; i < N_DISK_TYPES; i++)
 384       {
 385         if (strcasecmp (cptr, diskTypes[i].typename) == 0)
 386           break;
 387       }
 388     if (i >= N_DISK_TYPES)
 389      {
 390        sim_printf ("Disk type %s unrecognized, expected one of "
 391                    "%s %s %s %s %s %s %s %s\r\n",
 392                    cptr,
 393                    diskTypes[0].typename,
 394                    diskTypes[1].typename,
 395                    diskTypes[2].typename,
 396                    diskTypes[3].typename,
 397                    diskTypes[4].typename,
 398                    diskTypes[5].typename,
 399                    diskTypes[6].typename,
 400                    diskTypes[7].typename);
 401         return SCPE_ARG;
 402       }
 403     dsk_states[diskUnitIdx].typeIdx = i;
 404     dsk_unit[diskUnitIdx].capac = (t_addr) diskTypes[i].capac;
 405     dsk_states[diskUnitIdx].tAndDCapac = tAndDCapac[i];
 406     return SCPE_OK;
 407   }
 408 
 409 static t_stat dsk_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 410                                     UNUSED int val, UNUSED const void * desc)
 411   {
 412     int n = (int) DSK_UNIT_IDX (uptr);
 413     if (n < 0 || n >= N_DSK_UNITS_MAX)
 414       return SCPE_ARG;
 415     sim_printf("name     : %s", dsk_states[n].device_name);
 416     return SCPE_OK;
 417   }
 418 
 419 static t_stat dsk_set_device_name (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 420                                    const char * cptr, UNUSED void * desc)
 421   {
 422     int n = (int) DSK_UNIT_IDX (uptr);
 423     if (n < 0 || n >= N_DSK_UNITS_MAX)
 424       return SCPE_ARG;
 425     if (cptr)
 426       {
 427         strncpy (dsk_states[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
 428         dsk_states[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
 429       }
 430     else
 431       dsk_states[n].device_name[0] = 0;
 432     return SCPE_OK;
 433   }
 434 
 435 //
 436 // Looking at rcp_disk.pl1, it appears that the special interrupt for disks
 437 // only causes rcp to poll the device for status; there don't appear to
 438 // be any status specific bits to send here; signal ready is a misnomer;
 439 // it is just signal.
 440 
 441 t_stat signal_disk_ready (uint dsk_unit_idx) {
     /* [previous][next][first][last][top][bottom][index][help] */
 442   // Don't signal if the sim is not actually running....
 443   if (! sim_is_running)
 444     return SCPE_OK;
 445   // if substr (special_status_word, 20, 1) ^= "1"b | substr (special_status_word, 13, 6) ^= "00"b3
 446   // if substr (special_status_word, 34, 3) ^= "001"b
 447   // Note the 34,3 spans 34,35,36; therefore the bits are 1..36, not 0..35
 448   // 20,1 is bit 19
 449   // 13,6, is bits 12..17
 450   // status0 is 19..26
 451   // status1 is 28..35
 452   // so substr (w, 20, 1) is bit 0 of status0
 453   //    substr (w, 13, 6) is the low 6 bits of dev_no
 454   //    substr (w, 34, 3) is the low 3 bits of status 1
 455       //sim_printf ("%s %d %o\n", disk_filename, ro,  mt_unit[dsk_unit_idx] . flags);
 456       //sim_printf ("special int %d %o\n", dsk_unit_idx, mt_unit[dsk_unit_idx] . flags);
 457 
 458   uint ctlr_unit_idx = cables->dsk_to_ctlr[dsk_unit_idx].ctlr_unit_idx;
 459   enum ctlr_type_e ctlr_type = cables->dsk_to_ctlr[dsk_unit_idx].ctlr_type;
 460   if (ctlr_type != CTLR_T_MSP && ctlr_type != CTLR_T_IPC) {
 461     // If None, assume that the cabling hasn't happened yet.
 462     if (ctlr_type != CTLR_T_NONE) {
 463       sim_warn ("loadDisk lost\n");
 464       return SCPE_ARG;
 465     }
 466     return SCPE_OK;
 467   }
 468 
 469 
 470 
 471 
 472 
 473 
 474 
 475 
 476 
 477 
 478 
 479 
 480 
 481 
 482 
 483 
 484 
 485 
 486 
 487 
 488 
 489 
 490 
 491 
 492 
 493 
 494 
 495 
 496 
 497 
 498 
 499 
 500 
 501 
 502 
 503 
 504 
 505 
 506 
 507   for (uint ctlr_port_num = 0; ctlr_port_num < MAX_CTLR_PORTS; ctlr_port_num ++) {
 508     if (ctlr_type == CTLR_T_MSP) {
 509       if (cables->msp_to_iom[ctlr_unit_idx][ctlr_port_num].in_use) {
 510          uint iom_unit_idx = cables->msp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 511          uint chan_num = cables->msp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 512          uint dev_code = cables->dsk_to_ctlr[dsk_unit_idx].dev_code;
 513 
 514          send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0x40, 01 /* disk pack ready */);
 515          return SCPE_OK;
 516        }
 517      } else {
 518        if (cables->ipc_to_iom[ctlr_unit_idx][ctlr_port_num].in_use) {
 519          uint iom_unit_idx = cables->ipc_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 520          uint chan_num = cables->ipc_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 521          uint dev_code = cables->dsk_to_ctlr[dsk_unit_idx].dev_code;
 522 
 523          send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0x40, 01 /* disk pack ready */);
 524          return SCPE_OK;
 525        }
 526      }
 527    }
 528    return SCPE_ARG;
 529 
 530 }
 531 
 532 static t_stat disk_set_ready (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 533                               UNUSED const char * cptr,
 534                               UNUSED void * desc)
 535   {
 536     long disk_unit_idx = DSK_UNIT_IDX (uptr);
 537     if (disk_unit_idx >= (long) dsk_dev.numunits)
 538       {
 539         sim_warn ("%s: error: Invalid unit number %ld\n", __func__, (long) disk_unit_idx);
 540         return SCPE_ARG;
 541       }
 542     return signal_disk_ready ((uint) disk_unit_idx);
 543   }
 544 
 545 t_stat unloadDisk (uint dsk_unit_idx) {
     /* [previous][next][first][last][top][bottom][index][help] */
 546   if (dsk_unit [dsk_unit_idx] . flags & UNIT_ATT) {
 547     t_stat stat = sim_disk_detach (& dsk_unit [dsk_unit_idx]);
 548     if (stat != SCPE_OK) {
 549       sim_warn ("%s: sim_disk_detach returned %ld\n", __func__, (long) stat);
 550       return SCPE_ARG;
 551     }
 552   }
 553   return signal_disk_ready ((uint) dsk_unit_idx);
 554 }
 555 
 556 t_stat loadDisk (uint dsk_unit_idx, const char * disk_filename, bool ro) {
     /* [previous][next][first][last][top][bottom][index][help] */
 557   if (ro)
 558     dsk_unit[dsk_unit_idx].flags |= MTUF_WRP;
 559   else
 560     dsk_unit[dsk_unit_idx].flags &= ~ MTUF_WRP;
 561   t_stat stat = attach_unit (& dsk_unit [dsk_unit_idx], disk_filename);
 562   if (stat != SCPE_OK) {
 563     sim_printf ("%s: sim_disk_attach returned %d\n", __func__, stat);
 564     return stat;
 565   }
 566   return signal_disk_ready ((uint) dsk_unit_idx);
 567 }
 568 
 569 #define UNIT_WATCH UNIT_V_UF
 570 
 571 static MTAB disk_mod[] =
 572   {
 573 #if !defined(SPEED)
 574     { UNIT_WATCH, 1, "WATCH",   "WATCH",   0, 0, NULL, NULL },
 575     { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
 576 #endif /* if !defined(SPEED) */
 577     {
 578       MTAB_dev_value,                                   /* Mask               */
 579       0,                                                /* Match              */
 580       "NUNITS",                                         /* Print string       */
 581       "NUNITS",                                         /* Match string       */
 582       disk_set_nunits,                                  /* Validation routine */
 583       disk_show_nunits,                                 /* Display routine    */
 584       "Number of DISK units in the system",             /* Value descriptor   */
 585       NULL                                              /* Help               */
 586     },
 587     {
 588       MTAB_unit_value_show,                             /* Mask               */
 589       0,                                                /* Match              */
 590       "TYPE",                                           /* Print string       */
 591       "TYPE",                                           /* Match string       */
 592       disk_set_type,                                    /* Validation routine */
 593       disk_show_type,                                   /* Display routine    */
 594       "disk type",                                      /* Value descriptor   */
 595       "D500, D451, D400, D190, D181, D501, 3380, 3381", /* Help               */
 596     },
 597     {
 598       MTAB_XTD | MTAB_VUN | MTAB_VALR | MTAB_NC,        /* Mask               */
 599       0,                                                /* Match              */
 600       "NAME",                                           /* Print string       */
 601       "NAME",                                           /* Match string       */
 602       dsk_set_device_name,                              /* Validation routine */
 603       dsk_show_device_name,                             /* Display routine    */
 604       "Set the device name",                            /* Value descriptor   */
 605       NULL                                              /* Help               */
 606     },
 607     {
 608       MTAB_XTD | MTAB_VUN | MTAB_NMO | MTAB_VALR,       /* Mask               */
 609       0,                                                /* Match              */
 610       "READY",                                          /* Print string       */
 611       "READY",                                          /* Match string       */
 612       disk_set_ready,                                   /* Validation routine */
 613       NULL,                                             /* Display routine    */
 614       NULL,                                             /* Value descriptor   */
 615       NULL                                              /* Help string        */
 616     },
 617     MTAB_eol
 618   };
 619 
 620 static t_stat disk_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 621   {
 622     return SCPE_OK;
 623   }
 624 
 625 static t_stat disk_attach (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 626   {
 627     int diskUnitIdx = (int) DSK_UNIT_IDX (uptr);
 628     if (diskUnitIdx < 0 || diskUnitIdx >= N_DSK_UNITS_MAX)
 629       {
 630         sim_printf ("error: Invalid unit number %ld\n", (long) diskUnitIdx);
 631         return SCPE_ARG;
 632       }
 633 
 634     return loadDisk ((uint) diskUnitIdx, cptr, false);
 635   }
 636 
 637 // No disks known to Multics had more than 2^24 sectors...
 638 DEVICE dsk_dev = {
 639     "DISK",                /* Name                */
 640     dsk_unit,              /* Units               */
 641     NULL,                  /* Registers           */
 642     disk_mod,              /* Modifiers           */
 643     N_DISK_UNITS,          /* #Units              */
 644     10,                    /* Address radix       */
 645     24,                    /* Address width       */
 646     1,                     /* Address increment   */
 647     8,                     /* Data radix          */
 648     36,                    /* Data width          */
 649     NULL,                  /* Examine             */
 650     NULL,                  /* Deposit             */
 651     disk_reset,            /* Reset               */
 652     NULL,                  /* Boot                */
 653     disk_attach,           /* Attach              */
 654     NULL /*disk_detach*/,  /* Detach              */
 655     NULL,                  /* Context             */
 656     DEV_DEBUG,             /* Flags               */
 657     0,                     /* Debug control flags */
 658     disk_dt,               /* Debug flag names    */
 659     NULL,                  /* Memory size change  */
 660     NULL,                  /* Logical name        */
 661     NULL,                  /* Help                */
 662     NULL,                  /* Attach help         */
 663     NULL,                  /* Attach context      */
 664     NULL,                  /* Description         */
 665     NULL                   /* End                 */
 666 };
 667 
 668 /*
 669  * disk_init()
 670  */
 671 
 672 // Once-only initialization
 673 
 674 void disk_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 675   {
 676     // Sets diskTypeIdx to 0: 3381
 677     (void)memset (dsk_states, 0, sizeof (dsk_states));
 678 #if defined(LOCKLESS)
 679 # if defined(__FreeBSD__)
 680         pthread_mutexattr_t scu_attr;
 681         pthread_mutexattr_init (& scu_attr);
 682         pthread_mutexattr_settype (& scu_attr, PTHREAD_MUTEX_ADAPTIVE_NP);
 683 # endif /* if defined(__FreeBSD__) */
 684     for (uint i = 0; i < N_DSK_UNITS_MAX; i ++)
 685       {
 686 # if defined(__FreeBSD__)
 687         pthread_mutex_init (& dsk_states[i].dsk_lock, & scu_attr);
 688 # else
 689         pthread_mutex_init (& dsk_states[i].dsk_lock, NULL);
 690 # endif /* if defined(__FreeBSD__) */
 691       }
 692 #endif /* if defined(LOCKLESS) */
 693   }
 694 
 695 static iom_cmd_rc_t diskSeek64 (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 696   {
 697     iom_chan_data_t * p            = & iom_chan_data[iomUnitIdx][chan];
 698     struct dsk_state * disk_statep = & dsk_states[devUnitIdx];
 699 
 700     uint typeIdx                     = disk_statep->typeIdx;
 701     if (diskTypes[typeIdx].seekSize != seek_64)
 702       sim_warn ("%s: disk%u sent a SEEK_64 but is 512 sized\n", __func__, typeIdx);
 703 
 704     uint tally = p->DDCW_TALLY;
 705 
 706     // Seek specific processing
 707 
 708     if (tally != 1)
 709       {
 710         sim_printf ("disk seek dazed by tally %d != 1\n", tally);
 711         p->stati = 04510; // Cmd reject, invalid inst. seq.
 712         return IOM_CMD_ERROR;
 713       }
 714 
 715     word36 seekData[1];
 716     uint count;
 717     iom_indirect_data_service (iomUnitIdx, chan, seekData, &count, false);
 718     // POLTS claims that seek data doesn't count as an I/O xfer
 719     p->initiate = true;
 720     if (count != 1)
 721       sim_warn ("%s: count %d not 1\n", __func__, count);
 722 
 723 #if defined(POLTS_DISK_TESTING)
 724     if_sim_debug (DBG_TRACE, & dsk_dev) { sim_printf ("// Seek address %012"PRIo64"\n", seekData[0]); }
 725 #endif
 726 
 727 // disk_control.pl1:
 728 //   quentry.sector = bit (sector, 21);  /* Save the disk device address. */
 729 // suggests seeks are 21 bits. The largest sector number is D501 1077759; 4040777 in base 8;
 730 // 21 bits.
 731 
 732     seekData[0] &= MASK21;
 733     if (seekData[0] >= diskTypes[typeIdx].capac)
 734       {
 735         disk_statep->seekValid = false;
 736         p->stati               = 04304; // Invalid seek address
 737         return IOM_CMD_ERROR;
 738       }
 739     disk_statep->seekValid    = true;
 740     disk_statep->seekPosition = (uint) seekData[0];
 741     p->stati = 04000; // Channel ready
 742     return IOM_CMD_PROCEED;
 743   }
 744 
 745 static int diskSeek512 (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 746   {
 747     iom_chan_data_t * p            = & iom_chan_data[iomUnitIdx][chan];
 748     struct dsk_state * disk_statep = & dsk_states[devUnitIdx];
 749     uint typeIdx                   = disk_statep->typeIdx;
 750     if (diskTypes[typeIdx].seekSize != seek_512)
 751       sim_warn ("%s: disk%u sent a SEEK_512 but is 64 sized\n", __func__, typeIdx);
 752 
 753     uint tally = p->DDCW_TALLY;
 754 
 755     // Seek specific processing
 756 
 757     if (tally != 1)
 758       {
 759         sim_printf ("disk seek dazed by tally %d != 1\n", tally);
 760         //p->stati = 04510; // Cmd reject, invalid inst. seq.
 761         //p->chanStatus = chanStatIncorrectDCW;
 762         //return -1;
 763         tally = 1;
 764       }
 765 
 766     word36 seekData[1];
 767     uint count;
 768     iom_indirect_data_service (iomUnitIdx, chan, seekData, &count, false);
 769     // POLTS claims that seek data doesn't count as an I/O xfer
 770     p->initiate = true;
 771     if (count != 1)
 772       sim_warn ("%s: count %d not 1\n", __func__, count);
 773 
 774     seekData[0] &= MASK21;
 775     if (seekData[0] >= diskTypes[typeIdx].capac)
 776       {
 777         disk_statep->seekValid = false;
 778         p->stati = 04304; // Invalid seek address
 779         return -1;
 780       }
 781     disk_statep->seekValid    = true;
 782     disk_statep->seekPosition = (uint)seekData[0];
 783     p->stati                  = 04000; // Channel ready
 784     return 0;
 785   }
 786 
 787 static iom_cmd_rc_t diskSeekSpecial (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 788   {
 789     iom_chan_data_t * p            = & iom_chan_data[iomUnitIdx][chan];
 790     struct dsk_state * disk_statep = & dsk_states[devUnitIdx];
 791 
 792     uint typeIdx = disk_statep->typeIdx;
 793     //if (diskTypes[typeIdx].seekSize != seek_64)
 794       //sim_warn ("%s: disk%u sent a SEEK_64 but is 512 sized\n", __func__, typeIdx);
 795 
 796     uint tally = p->DDCW_TALLY;
 797 
 798     // Seek specific processing
 799 
 800     if (tally != 1)
 801       {
 802         sim_printf ("disk seek dazed by tally %d != 1\n", tally);
 803         //p->stati = 04510; // Cmd reject, invalid inst. seq.
 804         //p->chanStatus = chanStatIncorrectDCW;
 805         //return IOM_CMD_ERROR;
 806         tally = 1;
 807       }
 808 
 809     word36 seekData[1];
 810     uint count;
 811     iom_indirect_data_service (iomUnitIdx, chan, seekData, &count, false);
 812     // POLTS claims that seek data doesn't count as an I/O xfer
 813     p->initiate  = true;
 814     if (count   != 1)
 815       sim_warn ("%s: count %d not 1\n", __func__, count);
 816 
 817 #if defined(POLTS_DISK_TESTING)
 818     if_sim_debug (DBG_TRACE, & dsk_dev)
 819       {
 820         sim_printf ("// Seek address %012"PRIo64"\n", seekData[0]);
 821       }
 822 #endif
 823 
 824     seekData[0] &= MASK21;
 825     if (seekData[0] >= dsk_states[typeIdx].tAndDCapac)
 826       {
 827         p->stati               = 04304; // Invalid seek address
 828         disk_statep->seekValid = false;
 829         //p->chanStatus = chanStatIncomplete;
 830         return IOM_CMD_ERROR;
 831       }
 832     disk_statep->seekValid    = true;
 833     disk_statep->seekPosition = (uint) seekData[0];
 834     p->stati                  = 04000; // Channel ready
 835     return IOM_CMD_PROCEED;
 836   }
 837 
 838 static int diskRead (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 839   {
 840     iom_chan_data_t * p            = & iom_chan_data[iomUnitIdx][chan];
 841     UNIT * unitp                   = & dsk_unit[devUnitIdx];
 842     struct dsk_state * disk_statep = & dsk_states[devUnitIdx];
 843     uint typeIdx                   = disk_statep->typeIdx;
 844     uint sectorSizeWords           = diskTypes[typeIdx].sectorSizeWords;
 845     uint sectorSizeBytes           = ((36 * sectorSizeWords) / 8);
 846 
 847     if (! unitp->fileref) {
 848       p->stati = 04240; // device offline
 849 #if defined(POLTS_TESTING)
 850 if (chan == 014)
 851     if_sim_debug (DBG_TRACE, & dsk_dev) sim_printf ("// diskRead device offline\r\n");
 852 #endif
 853       return -1;
 854     }
 855     if (! disk_statep->seekValid) {
 856       p->stati               = 04510; // Invalid instruction sequence
 857       disk_statep->seekValid = false;
 858 #if defined(POLTS_TESTING)
 859 if (chan == 014)
 860     if_sim_debug (DBG_TRACE, & dsk_dev) sim_printf ("// diskRead seek invalid\r\n");
 861 #endif
 862       return IOM_CMD_ERROR;
 863     }
 864     uint tally = p->DDCW_TALLY;
 865     if (tally == 0)
 866       {
 867         tally = 4096;
 868       }
 869 
 870     int rc = fseek (unitp->fileref,
 871                 (long) ((long)disk_statep->seekPosition * (long)sectorSizeBytes),
 872                 SEEK_SET);
 873     if (rc)
 874       {
 875         sim_warn ("%s: fseek (read) returned %d, errno %d\n", __func__, rc, errno);
 876         p->stati = 04202; // attn, seek incomplete
 877 #if defined(POLTS_TESTING)
 878 if (chan == 014)
 879     if_sim_debug (DBG_TRACE, & dsk_dev) sim_printf ("// diskRead seek incomplete\r\n");
 880 #endif
 881         return -1;
 882       }
 883 
 884     // Convert from word36 format to packed72 format
 885 
 886     // round tally up to sector boundary
 887 
 888     // this math assumes tally is even.
 889 
 890     uint tallySectors = (tally + sectorSizeWords - 1) /
 891                          sectorSizeWords;
 892     uint tallyWords   = tallySectors * sectorSizeWords;
 893     //uint tallyBytes = tallySectors * sectorSizeBytes;
 894     uint p72ByteCnt   = (tallyWords * 36) / 8;
 895     uint8 diskBuffer[p72ByteCnt];
 896     (void)memset (diskBuffer, 0, sizeof (diskBuffer));
 897 
 898     (void)fflush (unitp->fileref);
 899     rc = (int) fread (diskBuffer, sectorSizeBytes,
 900                 tallySectors,
 901                 unitp->fileref);
 902 
 903     if (rc == 0) // EOF or error
 904       {
 905         if (ferror (unitp->fileref))
 906           {
 907             p->stati = 04202; // attn, seek incomplete
 908             //p->chanStatus = chanStatIncorrectDCW;
 909 #if defined(POLTS_TESTING)
 910 if (chan == 014)
 911     if_sim_debug (DBG_TRACE, & dsk_dev) sim_printf ("// diskRead seek incomplete2\r\n");
 912 #endif
 913             return -1;
 914           }
 915         // We ignore short reads-- we assume that they are reads
 916         // past the write highwater mark, and return zero data,
 917         // just as if the disk had been formatted with zeros.
 918       }
 919     disk_statep->seekPosition += tallySectors;
 920 
 921     uint wordsProcessed = 0;
 922     word36 buffer[tally];
 923     for (uint i = 0; i < tally; i ++)
 924       {
 925         word36 w;
 926         extractWord36FromBuffer (diskBuffer, p72ByteCnt, & wordsProcessed,
 927                                  & w);
 928         buffer[i] = w;
 929       }
 930     iom_indirect_data_service (iomUnitIdx, chan, buffer,
 931                             & wordsProcessed, true);
 932     p->stati = 04000;
 933 #if defined(POLTS_TESTING)
 934 if (chan == 014)
 935     if_sim_debug (DBG_TRACE, & dsk_dev) sim_printf ("// diskRead ok\r\n");
 936 #endif
 937     return 0;
 938   }
 939 
 940 static int diskWrite (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 941   {
 942     iom_chan_data_t * p            = & iom_chan_data[iomUnitIdx][chan];
 943     UNIT * unitp                   = & dsk_unit[devUnitIdx];
 944     struct dsk_state * disk_statep = & dsk_states[devUnitIdx];
 945     uint typeIdx                   = disk_statep->typeIdx;
 946     uint sectorSizeWords           = diskTypes[typeIdx].sectorSizeWords;
 947     uint sectorSizeBytes           = ((36 * sectorSizeWords) / 8);
 948 
 949     if (! unitp->fileref) {
 950       p->stati = 04240; // device offline
 951       return -1;
 952     }
 953 
 954     if (! disk_statep->seekValid) {
 955       p->stati               = 04510; // Invalid instruction sequence
 956       disk_statep->seekValid = false;
 957       return IOM_CMD_ERROR;
 958     }
 959 
 960     uint tally = p->DDCW_TALLY;
 961 
 962     if (tally == 0)
 963       {
 964         tally = 4096;
 965       }
 966 
 967     int rc = fseek (unitp->fileref,
 968                 (long) ((long)disk_statep->seekPosition * (long)sectorSizeBytes),
 969                 SEEK_SET);
 970     if (rc)
 971       {
 972         sim_printf ("fseek (read) returned %d, errno %d\n", rc, errno);
 973         p->stati = 04202; // attn, seek incomplete
 974         return -1;
 975       }
 976 
 977     // Convert from word36 format to packed72 format
 978 
 979     // round tally up to sector boundary
 980 
 981     // this math assumes tally is even.
 982 
 983     uint tallySectors = (tally + sectorSizeWords - 1) /
 984                          sectorSizeWords;
 985     uint tallyWords   = tallySectors * sectorSizeWords;
 986     uint p72ByteCnt   = (tallyWords * 36) / 8;
 987     uint8 diskBuffer[p72ByteCnt];
 988     (void)memset (diskBuffer, 0, sizeof (diskBuffer));
 989     uint wordsProcessed = 0;
 990 
 991     word36 buffer[tally];
 992     iom_indirect_data_service (iomUnitIdx, chan, buffer,
 993                             & wordsProcessed, false);
 994     wordsProcessed = 0;
 995     for (uint i = 0; i < tally; i ++)
 996       {
 997         insertWord36toBuffer (diskBuffer, p72ByteCnt, & wordsProcessed,
 998                               buffer[i]);
 999       }
1000 
1001     rc = (int) fwrite (diskBuffer, sectorSizeBytes,
1002                  tallySectors,
1003                  unitp->fileref);
1004     (void)fflush (unitp->fileref);
1005 
1006     if (rc != (int) tallySectors)
1007       {
1008         sim_printf ("fwrite returned %d, errno %d\n", rc, errno);
1009         p->stati      = 04202; // attn, seek incomplete
1010         p->chanStatus = chanStatIncorrectDCW;
1011         return -1;
1012       }
1013 
1014     disk_statep->seekPosition += tallySectors;
1015 
1016     p->stati = 04000;
1017     return 0;
1018   }
1019 
1020 static int readStatusRegister (uint devUnitIdx, uint iomUnitIdx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1021   {
1022     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1023     UNIT * unitp        = & dsk_unit[devUnitIdx];
1024 
1025     uint tally = p->DDCW_TALLY;
1026 
1027     if (tally != 4 && tally != 2)
1028       {
1029         sim_warn ("%s: RSR expected tally of 2 or 4, is %d\n",
1030                   __func__, tally);
1031       }
1032     if (tally == 0)
1033       {
1034         tally = 4096;
1035       }
1036 
1037 // XXX need status register data format
1038 // system_library_tools/source/bound_io_tools_.s.archive/analyze_detail_stat_.pl1  anal_fips_disk_().
1039 
1040 #if defined(TESTING)
1041     sim_warn ("Need status register data format\n");
1042 #endif
1043     word36 buffer[tally];
1044     (void)memset (buffer, 0, sizeof (buffer));
1045 #if defined(POLTS_DISK_TESTING)
1046 buffer[0] = nCmds;
1047 #endif
1048     uint wordsProcessed = tally;
1049     iom_indirect_data_service (iomUnitIdx, chan, buffer,
1050                             & wordsProcessed, true);
1051     p->charPos = 0;
1052     p->stati   = 04000;
1053     if (! unitp->fileref)
1054       p->stati = 04240; // device offline
1055     return 0;
1056   }
1057 
1058 static int diskRdCtrlReg (uint dev_unit_idx, uint iom_unit_idx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
1059   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1060   UNIT * unitp        = & dsk_unit[dev_unit_idx];
1061   p->charPos          = 0;
1062   p->stati            = 04000;
1063   if (! unitp->fileref)
1064     p->stati = 04240; // device offline
1065   return 0;
1066 }
1067 
1068 static int read_configuration (uint dev_unit_idx, uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1069   {
1070     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1071     UNIT * unitp        = & dsk_unit[dev_unit_idx];
1072 
1073     uint tally = p->DDCW_TALLY;
1074 
1075 // poll_mpc.pl1
1076 //
1077 // dcl  1 dau_buf aligned based (workp),   /* The IOI buffer segment */
1078 //        2 cf_idcw bit (36),              /* Read Configuration (24o) */
1079 //        2 cf_dcw bit (36),               /* Addr=dau_buf.data(0), tally=65 */
1080 //        2 st_idcw bit (36),              /* Read/Clear Statistics (16o) */
1081 //        2 st_dcw bit (36),               /* Address=dau_buf.data(130), tally=315 */
1082 //        2 data (0:759) bit (18) unal;    /* Config & statistics area */
1083 
1084 // XXX need status register data format
1085 // system_library_tools/source/bound_io_tools_.s.archive/analyze_detail_stat_.pl1  anal_fips_disk_().
1086 
1087 // poll_mpc.pl1
1088 //
1089 // dcl  1 dau_buf aligned based (workp),   /* The IOI buffer segment */
1090 //        2 cf_idcw bit (36),              /* Read Configuration (24o) */
1091 //        2 cf_dcw bit (36),               /* Addr=dau_buf.data(0), tally=65 */
1092 //        2 st_idcw bit (36),              /* Read/Clear Statistics (16o) */
1093 //        2 st_dcw bit (36),               /* Address=dau_buf.data(130), tally=315 */
1094 //        2 data (0:759) bit (18) unal;    /* Config & statistics area */
1095 
1096 //  dau_buf.data:
1097 //    0                                35
1098 //   +----------------+------------------+
1099 //   |    data(0)     |   data(1)        |
1100 //   +----------------+------------------+
1101 //   |    data(2)     |   data(3)        |
1102 //   +----------------+------------------+
1103 
1104 // dcl  dau_data (0:759) bit (16) unal;                        /* DAU config and stats */
1105 
1106 //
1107 //          do i = 0 to 759;
1108 //            substr (dau_data (i), 1, 8) = substr (dau_buf.data (i), 2, 8);
1109 //            substr (dau_data (i), 9, 8) = substr (dau_buf.data (i), 11, 8);
1110 //          end;
1111 //
1112 //   substr (dau_buf.data (i), 2, 8)
1113 //
1114 //    0 1 2 3 4 5 6 7 8     ....       17
1115 //   +-----------------------------------+
1116 //   | |X|X|X|X|X|X|X|X| |Y|Y|Y|Y|Y|Y|Y|Y|
1117 //   +-----------------------------------+
1118 //
1119 //   substr (dau_data (i), 1, 8)
1120 //
1121 //    0 1 2 3 4 5 6 7 8   ....     15
1122 //   +-------------------------------+
1123 //   |X|X|X|X|X|X|X|X|Y|Y|Y|Y|Y|Y|Y|Y|
1124 //   +-------------------------------+
1125 
1126 // dcl  1 dau_char based (image_ptr) unaligned,   /* Config data */
1127 //       2 type bit (8),                          /* = 12 HEX */
1128 //       2 hw_rev bit (8) unal,                   /* DAU rev */
1129 //       2 fw_maj_rev bit (8) unal,               /* firmware rev letter */
1130 //       2 fw_sub_rev bit (8) unal;               /* firmware rev number */
1131 //       2 dev (64),                              /* seq'ed by dev# */
1132 //                       /* all 4 bytes zero, if device NEVER configured */
1133 //         3 type fixed bin (8) uns unal,         /* device type */
1134 //         3 number fixed bin (8) uns unal,       /* device number, =FF if not configured */
1135 //         3 summary_status bit (8) unal,         /* device SS reg */
1136 //         3 port_number fixed bin (8) uns unal;  /* device DAU port */
1137 
1138 // We know that we are an MSP and not an IPC as this command is only issued
1139 // to MSPs.
1140 
1141     uint ctlr_unit_idx = get_ctlr_idx (iom_unit_idx, chan);
1142     struct ctlr_to_dev_s * dev_p;
1143     if (cables->iom_to_ctlr[iom_unit_idx][chan].ctlr_type == CTLR_T_IPC)
1144       dev_p = cables->ipc_to_dsk[ctlr_unit_idx];
1145     else
1146       dev_p = cables->msp_to_dsk[ctlr_unit_idx];
1147 
1148 // XXX Temp
1149     word36 buffer[tally];
1150     (void)memset (buffer, 0, sizeof (buffer));
1151     putbits36_9 (& buffer[0],  0, 0x12);
1152     putbits36_9 (& buffer[0],  9, 1);    // h/w revision
1153     putbits36_9 (& buffer[0], 18, '1');  // fw maj revision
1154     putbits36_9 (& buffer[0], 27, 'a');  // fw sub revision
1155 
1156     for (word9 dev_num = 0; dev_num < N_DEV_CODES; dev_num ++)
1157       {
1158          if (! dev_p[dev_num].in_use)
1159            continue;
1160          uint dsk_unit_idx = dev_p[dev_num].unit_idx;
1161          // word9 dau_type    = (word9) diskTypes[dsk_unit_idx].dau_type;
1162          // ubsan/asan
1163          word9 dau_type    = (word9) diskTypes[dsk_states[dsk_unit_idx].typeIdx].dau_type;
1164          putbits36_9 (& buffer[1+dev_num],  0, dau_type); // dev.type
1165          putbits36_9 (& buffer[1+dev_num],  9, dev_num);  // dev.number
1166          putbits36_9 (& buffer[1+dev_num], 18, 0);        // dev.summary_status // XXX
1167          putbits36_9 (& buffer[1+dev_num], 27, dev_num);  // dev.port_number
1168      }
1169 
1170     uint wordsProcessed = tally;
1171     iom_indirect_data_service (iom_unit_idx, chan, buffer,
1172                             & wordsProcessed, true);
1173     p->charPos = 0;
1174     p->stati   = 04000;
1175     if (! unitp->fileref)
1176       p->stati = 04240; // device offline
1177     return 0;
1178   }
1179 
1180 static int read_and_clear_statistics (uint dev_unit_idx, uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1181   {
1182 #if defined(TESTING)
1183     cpu_state_t * cpup  = _cpup;
1184 #endif
1185     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1186     UNIT * unitp        = & dsk_unit[dev_unit_idx];
1187 
1188     sim_debug (DBG_NOTIFY, & dsk_dev, "Read %d\n", dev_unit_idx);
1189 
1190     p->charPos = 0;
1191     p->stati   = 04000;
1192     if (! unitp->fileref)
1193       p->stati = 04240; // device offline
1194     return 0;
1195   }
1196 
1197 // source/library_dir_dir/system_library_1/source/bound_rcp_.s.archive/rcp_disk_.pl1
1198 //
1199 //  dcl set_standby_command    bit (6) internal static init ("72"b3);
1200 //  dcl request_status_command bit (6) internal static init ("00"b3);
1201 //  dcl read_command           bit (6) internal static init ("25"b3);
1202 //  dcl reset_status_command   bit (6) internal static init ("40"b3);
1203 
1204 iom_cmd_rc_t dsk_iom_cmd (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
1205 #if defined(POLTS_DISK_TESTING)
1206 nCmds ++;
1207 #endif
1208 #if defined(TESTING)
1209   cpu_state_t * cpup  = _cpup;
1210 #endif
1211   iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1212   uint ctlr_unit_idx  = get_ctlr_idx (iomUnitIdx, chan);
1213   uint devUnitIdx;
1214 
1215 #if defined(POLTS_DISK_TESTING)
1216 if (chan == 014)   {if_sim_debug (DBG_TRACE, & dsk_dev) { dumpDCW (p->DCW, 0); }}
1217 #endif
1218   if (cables->iom_to_ctlr[iomUnitIdx][chan].ctlr_type == CTLR_T_IPC)
1219     devUnitIdx = cables->ipc_to_dsk[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
1220   else if (cables->iom_to_ctlr[iomUnitIdx][chan].ctlr_type == CTLR_T_MSP)
1221     devUnitIdx = cables->msp_to_dsk[ctlr_unit_idx][p->IDCW_DEV_CODE].unit_idx;
1222   else {
1223     sim_warn ("%s: Can't find controller (%d)\n", __func__, cables->iom_to_ctlr[iomUnitIdx][chan].ctlr_type);
1224     return IOM_CMD_ERROR;
1225   }
1226 
1227   UNIT * unitp = & dsk_unit[devUnitIdx];
1228   struct dsk_state * disk_statep = & dsk_states[devUnitIdx];
1229 
1230   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1231 
1232 #if defined(LOCKLESS)
1233   lock_ptr (& dsk_states->dsk_lock); //-V619
1234 #endif
1235 
1236   // IDCW?
1237   if (p->DCW_18_20_CP == 7) {
1238     // IDCW
1239     disk_statep->io_mode = disk_no_mode;
1240     switch (p->IDCW_DEV_CMD) {
1241       case 000: // CMD 00 Request status
1242 #if defined(POLTS_DISK_TESTING)
1243 //if (chan == 014)
1244 #endif /* if defined(POLTS_DISK_TESTING) */
1245 #if !defined(POLTS_DISK_TESTING)
1246         if_sim_debug (DBG_TRACE, & dsk_dev) {
1247           sim_printf ("// Disk Request Status\r\n");
1248         }
1249 #endif /* if !defined(POLTS_DISK_TESTING) */
1250         //disk_statep->io_mode = disk_no_mode;
1251         p->stati = 04000;
1252         if (! unitp->fileref)
1253           p->stati = 04240; // device offline
1254         break;
1255 
1256       case 016: // CMD 16 Read and Clear Statistics -- Model 800
1257 #if defined(POLTS_DISK_TESTING)
1258 if (chan == 014)
1259 #endif /* if defined(POLTS_DISK_TESTING) */
1260         if_sim_debug (DBG_TRACE, & dsk_dev) {
1261           sim_printf ("// Disk Read And Clear Statistics\r\n");
1262         }
1263         disk_statep->io_mode = disk_rd_clr_stats;
1264         p->recordResidue --;
1265         p->stati             = 04000;
1266         if (! unitp->fileref)
1267           p->stati = 04240; // device offline
1268         break;
1269 
1270       case 022: // CMD 22 Read Status Register
1271 #if defined(POLTS_DISK_TESTING)
1272 if (chan == 014)
1273 #endif
1274         if_sim_debug (DBG_TRACE, & dsk_dev) {
1275           sim_printf ("// Disk Read Status Register\r\n");
1276         }
1277         disk_statep->io_mode = disk_rd_status_reg;
1278         p->recordResidue --;
1279         p->stati             = 04000;
1280         if (! unitp->fileref)
1281           p->stati = 04240; // device offline
1282         break;
1283 
1284       case 024: // CMD 24 Read configuration -- Model 800
1285 #if defined(POLTS_DISK_TESTING)
1286 if (chan == 014)
1287 #endif
1288         if_sim_debug (DBG_TRACE, & dsk_dev) {
1289           sim_printf ("// Disk Read Configuration\r\n");
1290         }
1291         disk_statep->io_mode = disk_rd_config;
1292         p->recordResidue --;
1293         p->stati             = 04000;
1294         if (! unitp->fileref)
1295           p->stati = 04240; // device offline
1296         break;
1297 
1298       case 025: // CMD 25 READ
1299 #if defined(POLTS_DISK_TESTING)
1300 if (chan == 014)
1301 #endif
1302         if_sim_debug (DBG_TRACE, & dsk_dev) {
1303           sim_printf ("// Disk Read\r\n");
1304         }
1305         disk_statep->io_mode = disk_rd;
1306         p->recordResidue --;
1307         p->stati             = 04000;
1308         if (! unitp->fileref)
1309           p->stati = 04240; // device offline
1310         break;
1311 
1312       case 026: // CMD 26 READ CONTROL REGISTER
1313 #if defined(POLTS_DISK_TESTING)
1314 if (chan == 014)
1315 #endif
1316         if_sim_debug (DBG_TRACE, & dsk_dev) {
1317           sim_printf ("// Disk Read Control Register\r\n");
1318         }
1319         disk_statep->io_mode = disk_rd_ctrl_reg;
1320         p->recordResidue --;
1321         p->stati             = 04000;
1322         if (! unitp->fileref)
1323           p->stati = 04240; // device offline
1324         break;
1325 
1326       case 030: // CMD 30 SEEK_512
1327 #if defined(POLTS_DISK_TESTING)
1328 if (chan == 014)
1329 #endif
1330         if_sim_debug (DBG_TRACE, & dsk_dev) {
1331           sim_printf ("// Disk Seek 512\r\n");
1332         }
1333         disk_statep->io_mode = disk_seek_512;
1334         p->recordResidue --;
1335         p->stati             = 04000;
1336         if (! unitp->fileref)
1337           p->stati = 04240; // device offline
1338         break;
1339 
1340       case 031: // CMD 31 WRITE
1341       case 033: // CMD 31 WRITE AND COMPARE
1342 #if defined(POLTS_DISK_TESTING)
1343 if (chan == 014)
1344 #endif
1345         if_sim_debug (DBG_TRACE, & dsk_dev) {
1346           sim_printf ("// Disk Write\r\n");
1347         }
1348         disk_statep->io_mode = disk_wr;
1349         p->recordResidue --;
1350         p->stati             = 04000;
1351         if (! unitp->fileref)
1352           p->stati = 04240; // device offline
1353         break;
1354 
1355       case 034: // CMD 34 SEEK_64
1356 #if defined(POLTS_DISK_TESTING)
1357 if (chan == 014)
1358 #endif
1359         if_sim_debug (DBG_TRACE, & dsk_dev) {
1360           sim_printf ("// Disk Seek 64\r\n");
1361         }
1362         disk_statep->io_mode = disk_seek_64;
1363         p->recordResidue --;
1364         p->stati             = 04000;
1365         if (! unitp->fileref)
1366           p->stati = 04240; // device offline
1367         break;
1368 
1369       case 036: // CMD 36 SPECIAL SEEK (T&D) // Make it work like SEEK_64 and
1370                                              // hope for the best
1371 #if defined(POLTS_DISK_TESTING)
1372 if (chan == 014)
1373 #endif
1374         if_sim_debug (DBG_TRACE, & dsk_dev) {
1375           sim_printf ("// Disk Special Seek\r\n");
1376         }
1377         disk_statep->io_mode = disk_special_seek;
1378         p->recordResidue --;
1379         p->stati             = 04000;
1380         if (! unitp->fileref)
1381           p->stati = 04240; // device offline
1382         break;
1383 
1384       case 040: // CMD 40 Reset status
1385 #if defined(POLTS_DISK_TESTING)
1386 if (chan == 014)
1387 #endif
1388         if_sim_debug (DBG_TRACE, & dsk_dev) {
1389           sim_printf ("// Disk Reset Status\r\n");
1390         }
1391         p->stati    = 04000;
1392         // XXX POLTS wants this; I don't understand why.
1393         p->recordResidue --;
1394         p->initiate = false; // According to POLTS
1395         p->isRead   = false;
1396         // T&D probing
1397         if (p->IDCW_DEV_CODE == 077) {
1398           p->stati = 04502; // invalid device code
1399 #if defined(LOCKLESS)
1400           unlock_ptr (& dsk_states->dsk_lock); //-V619
1401 #endif
1402           return IOM_CMD_DISCONNECT;
1403         }
1404         if (! unitp->fileref)
1405           p->stati = 04240; // device offline
1406         break;
1407 
1408       case 042: // CMD 42 RESTORE
1409 #if defined(POLTS_DISK_TESTING)
1410 if (chan == 014)
1411 #endif
1412         if_sim_debug (DBG_TRACE, & dsk_dev) {
1413           sim_printf ("// Disk Restore\r\n");
1414         }
1415         p->stati    = 04000;
1416         // XXX POLTS wants this; I don't understand why.
1417         p->recordResidue --;
1418         p->initiate = false; // According to POLTS
1419         if (! unitp->fileref)
1420           p->stati = 04240; // device offline
1421 
1422         disk_statep->seekValid = false;
1423 
1424         break;
1425 
1426       case 072: // CMD 72 SET STANDBY
1427 #if defined(POLTS_DISK_TESTING)
1428 if (chan == 014)
1429 #endif
1430         if_sim_debug (DBG_TRACE, & dsk_dev) {
1431           sim_printf ("// Disk Set Standby\r\n");
1432         }
1433         p->stati = 04000;
1434         if (! unitp->fileref)
1435           p->stati = 04240; // device offline
1436         break;
1437 
1438       default:
1439 #if defined(POLTS_DISK_TESTING)
1440 if (chan == 014)
1441 #endif
1442         if_sim_debug (DBG_TRACE, & dsk_dev) {
1443           sim_printf ("// Disk unknown command %o\r\n", p->IDCW_DEV_CMD);
1444         }
1445         p->stati      = 04501;
1446         p->chanStatus = chanStatIncorrectDCW;
1447         if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
1448           sim_warn ("%s: Unrecognized device command %02o\n", __func__, p->IDCW_DEV_CMD);
1449         rc =  IOM_CMD_ERROR;
1450         goto done;
1451     }
1452     goto done;
1453   } // IDCW
1454 
1455   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
1456   switch (disk_statep->io_mode) {
1457     case disk_no_mode:
1458 #if defined(POLTS_DISK_TESTING)
1459 if (chan == 014)
1460 #endif
1461       if_sim_debug (DBG_TRACE, & dsk_dev) {
1462         sim_printf ("// Disk IOT No Mode\r\n");
1463       }
1464 // It appears that in some cases Multics builds a dcw list from a generic
1465 // "single IDCW plus optional DDCW" template. That template sets the IDCW
1466 // channel command to "record", regardless of whether or not the
1467 // instruction needs an DDCW. In particular, disk_ctl pings the disk by
1468 // sending a "Reset status" with "record" and an apparently uninitialized
1469 // IOTD. The payload channel will send the IOTD because of the "record";
1470 // the Reset Status command left IO mode at "no mode" so we don't know what
1471 // to do with it. Since this appears benign, we will assume that the
1472 // original H/W ignored, and so shall we.
1473 
1474       //sim_warn ("%s: Unexpected IOTx\n", __func__);
1475       //rc = IOM_CMD_ERROR;
1476       goto done;
1477 
1478     case disk_rd_clr_stats: {
1479 #if defined(POLTS_DISK_TESTING)
1480 if (chan == 014)
1481 #endif
1482         if_sim_debug (DBG_TRACE, & dsk_dev) {
1483           sim_printf ("// Disk IOT Rd And Clr Stats\r\n");
1484         }
1485         int rc1 = read_and_clear_statistics (devUnitIdx, iomUnitIdx, chan);
1486         if (rc1) {
1487           rc = IOM_CMD_ERROR;
1488           goto done;
1489         }
1490       }
1491       break;
1492 
1493     case disk_rd_status_reg: {
1494 #if defined(POLTS_DISK_TESTING)
1495 if (chan == 014)
1496 #endif
1497         if_sim_debug (DBG_TRACE, & dsk_dev) {
1498           sim_printf ("// Disk IOT Rd Status Reg\r\n");
1499         }
1500         int rc1 = readStatusRegister (devUnitIdx, iomUnitIdx, chan);
1501         if (rc1) {
1502           rc = IOM_CMD_ERROR;
1503           goto done;
1504         }
1505       }
1506       break;
1507 
1508     case disk_rd_config: {
1509 #if defined(POLTS_DISK_TESTING)
1510 if (chan == 014)
1511 #endif
1512         if_sim_debug (DBG_TRACE, & dsk_dev) {
1513           sim_printf ("// Disk IOT Rd Config\r\n");
1514         }
1515         // XXX missing. see poll_mpc.pl1, poll_mpc_data.incl.pl1
1516         int rc1 = read_configuration (devUnitIdx, iomUnitIdx, chan);
1517         if (rc1) {
1518           rc = IOM_CMD_ERROR;
1519           goto done;
1520         }
1521       }
1522       break;
1523 
1524     case disk_rd: {
1525 #if defined(POLTS_DISK_TESTING)
1526 if (chan == 014)
1527 #endif
1528         if_sim_debug (DBG_TRACE, & dsk_dev) {
1529           sim_printf ("// Disk IOT Read\r\n");
1530         }
1531         int rc1 = diskRead (devUnitIdx, iomUnitIdx, chan);
1532         if (rc1) {
1533           rc = IOM_CMD_ERROR;
1534           goto done;
1535         }
1536       }
1537       break;
1538 
1539     case disk_rd_ctrl_reg: {
1540 #if defined(POLTS_DISK_TESTING)
1541 if (chan == 014)
1542 #endif
1543         if_sim_debug (DBG_TRACE, & dsk_dev) {
1544           sim_printf ("// Disk IOT Read Control Register\r\n");
1545         }
1546         int rc1 = diskRdCtrlReg (devUnitIdx, iomUnitIdx, chan);
1547         if (rc1) {
1548           rc = IOM_CMD_ERROR;
1549           goto done;
1550         }
1551       }
1552       break;
1553 
1554     case disk_seek_512: {
1555 #if defined(POLTS_DISK_TESTING)
1556 if (chan == 014)
1557 #endif
1558         if_sim_debug (DBG_TRACE, & dsk_dev) {
1559           sim_printf ("// Disk IOT Seek 512\r\n");
1560         }
1561         int rc1 = diskSeek512 (devUnitIdx, iomUnitIdx, chan);
1562         if (rc1) {
1563           rc = IOM_CMD_ERROR;
1564           goto done;
1565         }
1566       }
1567       break;
1568 
1569     case disk_wr: {
1570 #if defined(POLTS_DISK_TESTING)
1571 if (chan == 014)
1572 #endif
1573         if_sim_debug (DBG_TRACE, & dsk_dev) {
1574           sim_printf ("// Disk IOT Write\r\n");
1575         }
1576         int rc1 = diskWrite (devUnitIdx, iomUnitIdx, chan);
1577         if (rc1) {
1578           rc = IOM_CMD_ERROR;
1579           goto done;
1580         }
1581       }
1582       break;
1583 
1584     case disk_seek_64: {
1585 #if defined(POLTS_DISK_TESTING)
1586 if (chan == 014)
1587 #endif
1588         if_sim_debug (DBG_TRACE, & dsk_dev) {
1589           sim_printf ("// Disk IOT Seek 64\r\n");
1590         }
1591         int rc1 = diskSeek64 (devUnitIdx, iomUnitIdx, chan);
1592         if (rc1) {
1593           rc = IOM_CMD_ERROR;
1594           goto done;
1595         }
1596       }
1597       break;
1598 
1599     case disk_special_seek: { // Make it work like SEEK_64 and hope for the best
1600 #if defined(POLTS_DISK_TESTING)
1601 if (chan == 014)
1602 #endif
1603         if_sim_debug (DBG_TRACE, & dsk_dev) {
1604           sim_printf ("// Disk IOT special seek\r\n");
1605         }
1606         iom_cmd_rc_t rc1 = diskSeekSpecial (devUnitIdx, iomUnitIdx, chan);
1607         if (rc1) {
1608           rc = IOM_CMD_ERROR;
1609           goto done;
1610         }
1611       }
1612       break;
1613   }
1614 
1615 done:
1616 #if defined(LOCKLESS)
1617   unlock_ptr (& dsk_states->dsk_lock); //-V619
1618 #endif
1619   return rc;
1620 }
1621 
1622 //////////
1623 //////////
1624 //////////
1625 ///
1626 /// IPC
1627 ///
1628 
1629 #define IPC_UNIT_IDX(uptr) ((uptr) - ipc_unit)
1630 #define N_IPC_UNITS 1 // default
1631 
1632 static struct ipc_state
1633   {
1634     char device_name[MAX_DEV_NAME_LEN];
1635   } ipc_states[N_IPC_UNITS_MAX];
1636 
1637 UNIT ipc_unit[N_IPC_UNITS_MAX] = {
1638 #if defined(NO_C_ELLIPSIS)
1639   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1640   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1641   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1642   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1643   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1644   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1645   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1646   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1647   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1648   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1649   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1650   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1651   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1652   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1653   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1654   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
1655 #else
1656   [0 ... N_IPC_UNITS_MAX-1] = {
1657     UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
1658   }
1659 #endif
1660 };
1661 
1662 static t_stat ipc_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1663                                UNUSED int val, UNUSED const void * desc)
1664   {
1665     sim_printf("Number of IPC units in system is %d\n", ipc_dev.numunits);
1666     return SCPE_OK;
1667   }
1668 
1669 static t_stat ipc_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1670                               const char * cptr, UNUSED void * desc)
1671   {
1672     if (! cptr)
1673       return SCPE_ARG;
1674     int n = atoi (cptr);
1675     if (n < 0 || n > N_DSK_UNITS_MAX)
1676       return SCPE_ARG;
1677     ipc_dev.numunits = (uint32) n;
1678     return SCPE_OK;
1679   }
1680 
1681 static t_stat ipc_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1682                                     UNUSED int val, UNUSED const void * desc)
1683   {
1684     int n = (int) IPC_UNIT_IDX (uptr);
1685     if (n < 0 || n >= N_IPC_UNITS_MAX)
1686       return SCPE_ARG;
1687     if (ipc_states[n].device_name[0] != 0)
1688       sim_printf("name     : %s", ipc_states[n].device_name);
1689     else
1690       sim_printf("name     : default");
1691     return SCPE_OK;
1692   }
1693 
1694 static t_stat ipc_set_device_name (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1695                                    const char * cptr, UNUSED void * desc)
1696   {
1697     int n = (int) IPC_UNIT_IDX (uptr);
1698     if (n < 0 || n >= N_IPC_UNITS_MAX)
1699       return SCPE_ARG;
1700     if (cptr)
1701       {
1702         strncpy (ipc_states[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
1703         ipc_states[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
1704       }
1705     else
1706       ipc_states[n].device_name[0] = 0;
1707     return SCPE_OK;
1708   }
1709 
1710 static MTAB ipc_mod[] =
1711   {
1712     {
1713       MTAB_dev_value,                            /* Mask               */
1714       0,                                         /* Match              */
1715       "NUNITS",                                  /* Print string       */
1716       "NUNITS",                                  /* Match string       */
1717       ipc_set_nunits,                            /* Validation routine */
1718       ipc_show_nunits,                           /* Display routine    */
1719       "Number of DISK units in the system",      /* Value descriptor   */
1720       NULL                                       /* Help               */
1721     },
1722     {
1723       MTAB_XTD | MTAB_VUN | MTAB_VALR | MTAB_NC, /* Mask               */
1724       0,                                         /* Match              */
1725       "NAME",                                    /* Print string       */
1726       "NAME",                                    /* Match string       */
1727       ipc_set_device_name,                       /* Validation routine */
1728       ipc_show_device_name,                      /* Display routine    */
1729       "Set the device name",                     /* Value descriptor   */
1730       NULL                                       /* Help               */
1731     },
1732     MTAB_eol
1733   };
1734 
1735 DEVICE ipc_dev =
1736    {
1737     "IPC",                 /* Name                */
1738     ipc_unit,              /* Units               */
1739     NULL,                  /* Registers           */
1740     ipc_mod,               /* Modifiers           */
1741     N_IPC_UNITS,           /* #Units              */
1742     10,                    /* Address radix       */
1743     24,                    /* Address width       */
1744     1,                     /* Address increment   */
1745     8,                     /* Data radix          */
1746     36,                    /* Data width          */
1747     NULL,                  /* Examine             */
1748     NULL,                  /* Deposit             */
1749     NULL,                  /* Reset               */
1750     NULL,                  /* Boot                */
1751     NULL,                  /* Attach              */
1752     NULL /*disk_detach*/,  /* Detach              */
1753     NULL,                  /* Context             */
1754     0,                     /* Flags               */
1755     0,                     /* Debug control flags */
1756     NULL,                  /* Debug flag names    */
1757     NULL,                  /* Memory size change  */
1758     NULL,                  /* Logical name        */
1759     NULL,                  /* Help                */
1760     NULL,                  /* Attach help         */
1761     NULL,                  /* Attach context      */
1762     NULL,                  /* Description         */
1763     NULL                   /* End                 */
1764   };
1765 
1766 //////////
1767 //////////
1768 //////////
1769 ///
1770 /// MSP
1771 ///
1772 
1773 #define MSP_UNIT_IDX(uptr) ((uptr) - msp_unit)
1774 #define N_MSP_UNITS 1 // default
1775 
1776 struct msp_state_s msp_states [N_MSP_UNITS_MAX];
1777 
1778 UNIT msp_unit[N_MSP_UNITS_MAX] =
1779   {
1780 #if defined(NO_C_ELLIPSIS)
1781   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1782   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1783   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1784   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1785   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1786   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1787   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1788   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1789   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1790   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1791   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1792   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1793   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1794   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1795   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1796   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
1797 #else
1798   [0 ... N_MSP_UNITS_MAX-1] = {
1799     UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
1800   }
1801 #endif
1802 };
1803 
1804 static t_stat msp_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1805                                UNUSED int val, UNUSED const void * desc)
1806   {
1807     sim_printf("Number of MSP units in system is %d\n", msp_dev.numunits);
1808     return SCPE_OK;
1809   }
1810 
1811 static t_stat msp_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1812                               const char * cptr, UNUSED void * desc)
1813   {
1814     if (! cptr)
1815       return SCPE_ARG;
1816     int n = atoi (cptr);
1817     if (n < 0 || n > N_DSK_UNITS_MAX)
1818       return SCPE_ARG;
1819     msp_dev.numunits = (uint32) n;
1820     return SCPE_OK;
1821   }
1822 
1823 static t_stat msp_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1824                                     UNUSED int val, UNUSED const void * desc)
1825   {
1826     int n = (int) MSP_UNIT_IDX (uptr);
1827     if (n < 0 || n >= N_MSP_UNITS_MAX)
1828       return SCPE_ARG;
1829     if (msp_states[n].device_name[0] != 0)
1830       sim_printf("name     : %s", msp_states[n].device_name);
1831     else
1832       sim_printf("name     : default");
1833     return SCPE_OK;
1834   }
1835 
1836 static t_stat msp_set_device_name (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1837                                    const char * cptr, UNUSED void * desc)
1838   {
1839     int n = (int) MSP_UNIT_IDX (uptr);
1840     if (n < 0 || n >= N_MSP_UNITS_MAX)
1841       return SCPE_ARG;
1842     if (cptr)
1843       {
1844         strncpy (msp_states[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
1845         msp_states[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
1846       }
1847     else
1848       msp_states[n].device_name[0] = 0;
1849     return SCPE_OK;
1850   }
1851 
1852 static MTAB msp_mod[] =
1853   {
1854     {
1855       MTAB_dev_value,                        /* Mask               */
1856       0,                                     /* Match              */
1857       "NUNITS",                              /* Print string       */
1858       "NUNITS",                              /* Match string       */
1859       msp_set_nunits,                        /* Validation routine */
1860       msp_show_nunits,                       /* Display routine    */
1861       "Number of DISK units in the system",  /* Value descriptor   */
1862       NULL                                   /* Help               */
1863     },
1864     {
1865       MTAB_XTD | MTAB_VUN | \
1866       MTAB_VALR | MTAB_NC,                   /* Mask               */
1867       0,                                     /* Match              */
1868       "NAME",                                /* Print string       */
1869       "NAME",                                /* Match string       */
1870       msp_set_device_name,                   /* Validation routine */
1871       msp_show_device_name,                  /* Display routine    */
1872       "Set the device name",                 /* Value descriptor   */
1873       NULL                                   /* Help               */
1874     },
1875     MTAB_eol
1876   };
1877 
1878 DEVICE msp_dev =
1879    {
1880     "MSP",                 /* Name                */
1881     msp_unit,              /* Units               */
1882     NULL,                  /* Registers           */
1883     msp_mod,               /* Modifiers           */
1884     N_MSP_UNITS,           /* #Units              */
1885     10,                    /* Address radix       */
1886     24,                    /* Address width       */
1887     1,                     /* Address increment   */
1888     8,                     /* Data radix          */
1889     36,                    /* Data width          */
1890     NULL,                  /* Examine             */
1891     NULL,                  /* Deposit             */
1892     NULL,                  /* Reset               */
1893     NULL,                  /* Boot                */
1894     NULL,                  /* Attach              */
1895     NULL /*disk_detach*/,  /* Detach              */
1896     NULL,                  /* Context             */
1897     0,                     /* Flags               */
1898     0,                     /* Debug control flags */
1899     NULL,                  /* Debug flag names    */
1900     NULL,                  /* Memory size change  */
1901     NULL,                  /* Logical name        */
1902     NULL,                  /* Help                */
1903     NULL,                  /* Attach help         */
1904     NULL,                  /* Attach context      */
1905     NULL,                  /* Description         */
1906     NULL                   /* End                 */
1907   };

/* [previous][next][first][last][top][bottom][index][help] */