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

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