root/src/dps8/dps8_cable.c

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

DEFINITIONS

This source file includes following definitions.
  1. parseval
  2. getval
  3. name_match
  4. back_cable_iom_to_scu
  5. cable_scu_to_iom
  6. back_cable_cpu_to_scu
  7. cable_scu_to_cpu
  8. cable_scu
  9. cable_ctlr_to_iom
  10. cable_ctlr
  11. cable_iom
  12. cable_periph_to_ctlr
  13. cable_periph
  14. cable_mtp
  15. cable_ipc
  16. cable_msp
  17. cable_urp
  18. sys_cable
  19. cable_init
  20. sys_cable_graph
  21. sys_cable_show
  22. sys_cable_ripout
  23. sysCableInit

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 571b2a19-f62d-11ec-b8e7-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2013-2016 Charles Anthony
   9  * Copyright (c) 2026 Eric Swenson
  10  * Copyright (c) 2026 Jeffrey H. Johnson
  11  * Copyright (c) 2021-2026 The DPS8M Development Team
  12  *
  13  * This software is made available under the terms of the ICU License.
  14  * See the LICENSE.md file at the top-level directory of this distribution.
  15  *
  16  * ---------------------------------------------------------------------------
  17  */
  18 
  19 //  The cable command
  20 //
  21 //  This command is responsible for managing the interconnection of
  22 //  major subsystems: CPU, SCU, IOM; controllers: MTP, IPC, MSP, URP;
  23 //  peripherals:
  24 //
  25 //  The unit numbers for CPUs, IOMs, and SCUs (eg IOM3 is IOM unit 3) are
  26 //  scp unit numbers; these units can can individually configured to
  27 //  desired Multics unit numbers. However, it is somewhat easier to
  28 //  adopt an one-to-one practice; IOM0 == IOMA, etc.
  29 //
  30 //
  31 //   CABLE RIPOUT
  32 //      Remove all cables from the configuration.
  33 //
  34 //   CABLE SHOW
  35 //      Show the current cabling configuration.
  36 //
  37 //   CABLE DUMP
  38 //      Show the current cabling configuration in great detail.
  39 //
  40 //   CABLE SCUi j IOMk l
  41 //
  42 //      Connect SCU i port j to IOM k port l.
  43 //      "cable SCU0 0 IOM0 2"
  44 //
  45 //   CABLE SCUi j CPUk l
  46 //
  47 //      Connect SCU i port j to CPU k port l.
  48 //      "cable SCU0 7 CPU0 7"
  49 //
  50 //   CABLE IOMi j MTPk
  51 //   CABLE IOMi j MTPk l
  52 //
  53 //      Connect IOM i channel j to MTP k port l (l defaults to 0).
  54 //
  55 //   CABLE IOMi j MSPk
  56 //   CABLE IOMi j MSPk l
  57 //
  58 //      Connect IOM i channel j to MSP k port l (l defaults to 0).
  59 //
  60 //   CABLE IOMi j IPCk
  61 //   CABLE IOMi j IPCk l
  62 //
  63 //      Connect IOM i channel j to IPC k port l (l defaults to 0).
  64 //
  65 //   CABLE IOMi j OPCk
  66 //
  67 //      Connect IOM i channel j to OPC k.
  68 //
  69 //   CABLE IOMi j FNPk
  70 //
  71 //      Connect IOM i channel j to FNP k.
  72 //
  73 //   CABLE IOMi j ABSIk
  74 //
  75 //      Connect IOM i channel j to ABSI k.
  76 //
  77 //   CABLE IOMi j MGPk
  78 //
  79 //      Connect IOM i channel j to MGP k.
  80 //
  81 //   CABLE IOMi j NETk
  82 //
  83 //      Connect IOM i channel j to NET k.
  84 //
  85 //   CABLE IOMi j SKCk
  86 //
  87 //      Connect IOM i channel j to SKC k.
  88 //
  89 //   CABLE MTPi j TAPEk
  90 //
  91 //      Connect MTP i device code j to tape unit k.
  92 //
  93 //   CABLE IPCi j DISKk
  94 //
  95 //      Connect IPC i device code j to disk unit k.
  96 //
  97 //   CABLE MSPi j DISKk
  98 //
  99 //      Connect MSP i device code j to disk unit k.
 100 //
 101 //   CABLE URPi j RDRk
 102 //
 103 //      Connect URP i device code j to card reader unit k.
 104 //
 105 //   CABLE URPi j PUNk
 106 //
 107 //      Connect URP i device code j to card punch unit k.
 108 //
 109 //   CABLE URPi j PRTk
 110 //
 111 //      Connect URP i device code j to printer unit k.
 112 //
 113 
 114 #include <ctype.h>
 115 
 116 #include "dps8.h"
 117 #include "dps8_simh.h"
 118 #include "dps8_iom.h"
 119 #include "dps8_mt.h"
 120 #include "dps8_socket_dev.h"
 121 #include "dps8_sys.h"
 122 #include "dps8_cable.h"
 123 #include "dps8_cpu.h"
 124 #include "dps8_faults.h"
 125 #include "dps8_scu.h"
 126 #include "dps8_state.h"
 127 #include "dps8_console.h"
 128 #include "dps8_disk.h"
 129 #include "dps8_fnp2.h"
 130 #include "dps8_urp.h"
 131 #include "dps8_crdrdr.h"
 132 #include "dps8_crdpun.h"
 133 #include "dps8_prt.h"
 134 #include "dps8_utils.h"
 135 #if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined (CROSS_MINGW64) && !defined (CROSS_MINGW32)
 136 # include "dps8_absi.h"
 137 #endif /* if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined (CROSS_MINGW64) && !defined (CROSS_MINGW32) */
 138 #include "dps8_mgp.h"
 139 #include "dps8_net.h"
 140 #if defined(M_SHARED)
 141 # include <unistd.h>
 142 # include "shm.h"
 143 #endif
 144 
 145 #define DBG_CTR 1
 146 
 147 #if defined(FREE)
 148 # undef FREE
 149 #endif /* if defined(FREE) */
 150 #define FREE(p) do  \
 151   {                 \
 152     free((p));      \
 153     (p) = NULL;     \
 154   } while(0)
 155 
 156 struct cables_s * cables = NULL;
 157 
 158 char * ctlr_type_strs [/* enum ctlr_type_e */] =
 159   {
 160     "none",
 161      "MTP", "MSP", "IPC", "OPC",
 162      "URP", "FNP", "ABSI", "SKC", "MGP", "NET"
 163   };
 164 
 165 char * chan_type_strs [/* enum ctlr_type_e */] =
 166   {
 167     "CPI", "PSI", "Direct"
 168   };
 169 
 170 static t_stat sys_cable_graph (void);
 171 
 172 static int parseval (char * value)
     /* [previous][next][first][last][top][bottom][index][help] */
 173   {
 174     if (! value)
 175       return -1;
 176     if (strlen (value) == 1 && value[0] >= 'a' && value[0] <= 'z')
 177       return (int) (value[0] - 'a');
 178     if (strlen (value) == 1 && value[0] >= 'A' && value[0] <= 'Z')
 179       return (int) (value[0] - 'A');
 180     char * endptr;
 181     long l = strtol (value, & endptr, 0);
 182     if (* endptr || l < 0 || l > INT_MAX)
 183       {
 184         sim_printf ("error: CABLE: can't parse %s\r\n", value);
 185         return -1;
 186       }
 187     return (int) l;
 188   }
 189 
 190 static int getval (char * * save, char * text)
     /* [previous][next][first][last][top][bottom][index][help] */
 191   {
 192     char * value;
 193     value = strtok_r (NULL, ", ", save);
 194     if (! value)
 195       {
 196         sim_printf ("error: CABLE: can't parse %s\r\n", text);
 197         return -1;
 198       }
 199     return parseval (value);
 200   }
 201 
 202 // Match "FOO" with "FOOxxx" where xx is a decimal number or [A-Za-z]
 203 //  "IOM" : IOM0 IOMB iom15 IOMc
 204 // On match return value of number of mapped character (a = 0, b = 1, ...)
 205 // On fail, return -1;
 206 
 207 static bool name_match (const char * str, const char * pattern, uint * val)
     /* [previous][next][first][last][top][bottom][index][help] */
 208   {
 209     // Does str match pattern?
 210     size_t pat_len = strlen (pattern);
 211     if (strncasecmp (pattern, str, pat_len))
 212       return false;
 213 
 214     // Isolate the string past the pattern
 215     size_t rest = strlen (str) - pat_len;
 216     const char * p = str + pat_len;
 217 
 218     // Can't be empty
 219     if (! rest)
 220       return false; // no tag
 221 
 222     // [A-Za-z]? XXX Assume a-z contiguous; won't work in EBCDIC
 223     char * q;
 224     char * tags = "abcdefghijklmnopqrstuvwxyz";
 225     if (rest == 1 && (q = strchr (tags, tolower (*p))))
 226       {
 227         * val = (uint) (q - tags);
 228         return true;
 229       }
 230 
 231     // Starts with a digit?
 232     char * digits = "0123456789";
 233     q = strchr (digits, tolower (*p));
 234     if (! q)
 235       return false; // start not a digit
 236 
 237     long l = strtol (p, & q, 0);
 238     if (* q || l < 0 || l > INT_MAX)
 239       {
 240         sim_printf ("error: sys_cable: can't parse %s\r\n", str);
 241         return false;
 242       }
 243     * val =  (uint) l;
 244     return true;
 245   }
 246 
 247 // back cable SCUx port# IOMx port#
 248 
 249 static t_stat back_cable_iom_to_scu (int uncable, uint iom_unit_idx, uint iom_port_num, uint scu_unit_idx, uint scu_port_num)
     /* [previous][next][first][last][top][bottom][index][help] */
 250   {
 251     struct iom_to_scu_s * p = & cables->iom_to_scu[iom_unit_idx][iom_port_num];
 252     if (uncable)
 253       {
 254         p->in_use = false;
 255       }
 256     else
 257       {
 258         if (p->in_use)
 259           {
 260             sim_printf ("cable SCU: IOM%u port %u in use.\r\n", iom_unit_idx, iom_port_num);
 261              return SCPE_ARG;
 262           }
 263         p->in_use = true;
 264         p->scu_unit_idx = scu_unit_idx;
 265         p->scu_port_num = scu_port_num;
 266       }
 267     return SCPE_OK;
 268   }
 269 
 270 // cable SCUx IOMx
 271 
 272 static t_stat cable_scu_to_iom (int uncable, uint scu_unit_idx, uint scu_port_num, uint iom_unit_idx, uint iom_port_num)
     /* [previous][next][first][last][top][bottom][index][help] */
 273   {
 274     struct scu_to_iom_s * p = & cables->scu_to_iom[scu_unit_idx][scu_port_num];
 275     if (uncable)
 276       {
 277         if (! p->in_use)
 278           {
 279             sim_printf ("uncable SCU%u port %d: not cabled\r\n", scu_unit_idx, scu_port_num);
 280             return SCPE_ARG;
 281           }
 282 
 283         // Unplug the other end of the cable
 284         t_stat rc = back_cable_iom_to_scu (uncable, iom_unit_idx, iom_port_num,
 285                                   scu_unit_idx, scu_port_num);
 286         if (rc)
 287           {
 288             return rc;
 289           }
 290 
 291         p->in_use = false;
 292         scu[scu_unit_idx].ports[scu_port_num].type    = ADEV_NONE;
 293         scu[scu_unit_idx].ports[scu_port_num].dev_idx = 0;
 294         // XXX is this wrong? is is_exp supposed to be an accumulation of bits?
 295         scu[scu_unit_idx].ports[scu_port_num].is_exp  = false;
 296         //scu[scu_unit_idx].ports[scu_port_num].dev_port[scu_subport_num] = 0;
 297       }
 298     else
 299       {
 300         if (p->in_use)
 301           {
 302             sim_printf ("cable_scu: SCU %d port %d in use.\r\n", scu_unit_idx, scu_port_num);
 303             return SCPE_ARG;
 304           }
 305 
 306         // Plug the other end of the cable in
 307         t_stat rc = back_cable_iom_to_scu (uncable, iom_unit_idx, iom_port_num,
 308                                   scu_unit_idx, scu_port_num);
 309         if (rc)
 310           {
 311             return rc;
 312           }
 313 
 314         p->in_use = true;
 315         p->iom_unit_idx = iom_unit_idx;
 316         p->iom_port_num = (uint) iom_port_num;
 317 
 318         scu[scu_unit_idx].ports[scu_port_num].type        = ADEV_IOM;
 319         scu[scu_unit_idx].ports[scu_port_num].dev_idx     = (int) iom_unit_idx;
 320         scu[scu_unit_idx].ports[scu_port_num].dev_port[0] = (int) iom_port_num;
 321         // XXX is this wrong? is is_exp supposed to be an accumulation of bits?
 322         scu[scu_unit_idx].ports[scu_port_num].is_exp      = 0;
 323         //scu[scu_unit_idx].ports[scu_port_num].dev_port[scu_subport_num] = 0;
 324       }
 325     return SCPE_OK;
 326   }
 327 
 328 // back cable SCUx port# CPUx port#
 329 
 330 static t_stat back_cable_cpu_to_scu (int uncable, uint cpu_unit_idx, uint cpu_port_num,
     /* [previous][next][first][last][top][bottom][index][help] */
 331         uint scu_unit_idx, uint scu_port_num, uint scu_subport_num)
 332   {
 333     struct cpu_to_scu_s * p = & cables->cpu_to_scu[cpu_unit_idx][cpu_port_num];
 334     if (uncable)
 335       {
 336         p->in_use = false;
 337       }
 338     else
 339       {
 340         if (p->in_use)
 341           {
 342             sim_printf ("cable SCU: CPU%u port %u in use.\r\n", cpu_unit_idx, cpu_port_num);
 343              return SCPE_ARG;
 344           }
 345         p->in_use = true;
 346         p->scu_unit_idx    = scu_unit_idx;
 347         p->scu_port_num    = scu_port_num;
 348         p->scu_subport_num = scu_subport_num;
 349       }
 350     return SCPE_OK;
 351   }
 352 
 353 // cable SCUx CPUx
 354 
 355 static t_stat cable_scu_to_cpu (int uncable, uint scu_unit_idx, uint scu_port_num,
     /* [previous][next][first][last][top][bottom][index][help] */
 356         uint scu_subport_num, uint cpu_unit_idx, uint cpu_port_num, bool is_exp)
 357   {
 358     struct scu_to_cpu_s * p = & cables->scu_to_cpu[scu_unit_idx][scu_port_num][scu_subport_num];
 359     if (uncable)
 360       {
 361         if (! p->in_use)
 362           {
 363             sim_printf ("uncable SCU%u port %u subport %u: not cabled\r\n",
 364                     scu_unit_idx, scu_port_num, scu_subport_num);
 365             return SCPE_ARG;
 366           }
 367 
 368         // Unplug the other end of the cable
 369         t_stat rc = back_cable_cpu_to_scu (uncable, cpu_unit_idx, cpu_port_num,
 370                                   scu_unit_idx, scu_port_num, scu_subport_num);
 371         if (rc)
 372           {
 373             return rc;
 374           }
 375 
 376         p->in_use = false;
 377         scu[scu_unit_idx].ports[scu_port_num].type                      = ADEV_NONE;
 378         scu[scu_unit_idx].ports[scu_port_num].dev_idx                   = 0;
 379         // XXX is this wrong? is is_exp supposed to be an accumulation of bits?
 380         scu[scu_unit_idx].ports[scu_port_num].is_exp                    = false;
 381         scu[scu_unit_idx].ports[scu_port_num].dev_port[scu_subport_num] = 0;
 382       }
 383     else
 384       {
 385         if (p->in_use)
 386           {
 387             sim_printf ("cable_scu: SCU %u port %u subport %u in use.\r\n",
 388                     scu_unit_idx, scu_port_num, scu_subport_num);
 389             return SCPE_ARG;
 390           }
 391 
 392         // Plug the other end of the cable in
 393         t_stat rc = back_cable_cpu_to_scu (uncable, cpu_unit_idx, cpu_port_num,
 394                                   scu_unit_idx, scu_port_num, scu_subport_num);
 395         if (rc)
 396           {
 397             return rc;
 398           }
 399 
 400         p->in_use = true;
 401         p->cpu_unit_idx = cpu_unit_idx;
 402         p->cpu_port_num = (uint) cpu_port_num;
 403 
 404         scu[scu_unit_idx].ports[scu_port_num].type                      = ADEV_CPU;
 405         scu[scu_unit_idx].ports[scu_port_num].dev_idx                   = (int) cpu_unit_idx;
 406         scu[scu_unit_idx].ports[scu_port_num].dev_port[0]               = (int) cpu_port_num;
 407         // XXX is this wrong? is is_exp supposed to be an accumulation of bits?
 408         scu[scu_unit_idx].ports[scu_port_num].is_exp                    = is_exp;
 409         scu[scu_unit_idx].ports[scu_port_num].dev_port[scu_subport_num] = (int) cpu_port_num;
 410 
 411         cpus[cpu_unit_idx].scu_port[scu_unit_idx]                       = scu_port_num;
 412       }
 413     // Taking this out breaks the unit test segment loader.
 414     setup_scbank_map (_cpup);
 415     return SCPE_OK;
 416   }
 417 
 418 // cable SCUx port# IOMx port#
 419 // cable SCUx port# CPUx port#
 420 
 421 static t_stat cable_scu (int uncable, uint scu_unit_idx, char * * name_save)
     /* [previous][next][first][last][top][bottom][index][help] */
 422   {
 423     if (scu_unit_idx >= scu_dev.numunits)
 424       {
 425         sim_printf ("cable_scu: SCU unit number out of range <%d>\r\n",
 426                     scu_unit_idx);
 427         return SCPE_ARG;
 428       }
 429 
 430     int scu_port_num = getval (name_save, "SCU port number");
 431 
 432     // The scu port number may have subport data encoded; check range
 433     // after we have decided if the is a connection to an IOM or a CPU.
 434 
 435     //    if (scu_port_num < 0 || scu_port_num >= N_SCU_PORTS)
 436     //      {
 437     //        sim_printf ("cable_scu: SCU port number out of range <%d>\r\n",
 438     //                    scu_port_num);
 439     //        return SCPE_ARG;
 440     //      }
 441 
 442     // XXX combine into parse_match ()
 443     // extract 'IOMx' or 'CPUx'
 444     char * param = strtok_r (NULL, ", ", name_save);
 445     if (! param)
 446       {
 447         sim_printf ("cable_scu: can't parse IOM\r\n");
 448         return SCPE_ARG;
 449       }
 450     uint unit_idx;
 451 
 452     // SCUx IOMx
 453     if (name_match (param, "IOM", & unit_idx))
 454       {
 455         if (unit_idx >= N_IOM_UNITS_MAX)
 456           {
 457             sim_printf ("cable SCU: IOM unit number out of range <%d>\r\n", unit_idx);
 458             return SCPE_ARG;
 459           }
 460 
 461         if (scu_port_num < 0 || scu_port_num >= N_SCU_PORTS)
 462           {
 463             sim_printf ("cable_scu: SCU port number out of range <%d>\r\n",
 464                         scu_port_num);
 465             return SCPE_ARG;
 466           }
 467 
 468         // extract iom port number
 469         param = strtok_r (NULL, ", ", name_save);
 470         if (! param)
 471           {
 472             sim_printf ("cable SCU: can't parse IOM port number\r\n");
 473             return SCPE_ARG;
 474           }
 475         int iom_port_num = parseval (param);
 476 
 477         if (iom_port_num < 0 || iom_port_num >= N_IOM_PORTS)
 478           {
 479             sim_printf ("cable SCU: IOM port number out of range <%d>\r\n", iom_port_num);
 480             return SCPE_ARG;
 481           }
 482         return cable_scu_to_iom (uncable, scu_unit_idx, (uint) scu_port_num,
 483                                  unit_idx, (uint) iom_port_num);
 484       }
 485 
 486     // SCUx CPUx
 487     else if (name_match (param, "CPU", & unit_idx))
 488       {
 489         if (unit_idx >= N_CPU_UNITS_MAX)
 490           {
 491             sim_printf ("cable SCU: IOM unit number out of range <%d>\r\n", unit_idx);
 492             return SCPE_ARG;
 493           }
 494 
 495         // Encoding expansion port info into port number:
 496         //   [0-7]: port number
 497         //   [1-7][0-3]:  port number, sub port number.
 498         // This won't let me put an expansion port on port 0, but documents
 499         // say to put the CPUs on the high ports and the IOMs on the low, so
 500         // there is no reason to put an expander on port 0.
 501         //
 502 
 503         int scu_subport_num = 0;
 504         bool is_exp = false;
 505         int exp_port = scu_port_num / 10;
 506         if (exp_port)
 507           {
 508             scu_subport_num = scu_port_num % 10;
 509             if (scu_subport_num < 0 || scu_subport_num >= N_SCU_SUBPORTS)
 510               {
 511                 sim_printf ("cable SCU: subport number out of range <%d>\r\n",
 512                             scu_subport_num);
 513                 return SCPE_ARG;
 514               }
 515             scu_port_num /= 10;
 516             is_exp = true;
 517           }
 518         if (scu_port_num < 0 || scu_port_num >= N_SCU_PORTS)
 519           {
 520             sim_printf ("cable SCU: port number out of range <%d>\r\n",
 521                         scu_port_num);
 522             return SCPE_ARG;
 523           }
 524 
 525         // extract cpu port number
 526         param = strtok_r (NULL, ", ", name_save);
 527         if (! param)
 528           {
 529             sim_printf ("cable SCU: can't parse CPU port number\r\n");
 530             return SCPE_ARG;
 531           }
 532         int cpu_port_num = parseval (param);
 533 
 534         if (cpu_port_num < 0 || cpu_port_num >= N_CPU_PORTS)
 535           {
 536             sim_printf ("cable SCU: CPU port number out of range <%d>\r\n", cpu_port_num);
 537             return SCPE_ARG;
 538           }
 539         return cable_scu_to_cpu (uncable, scu_unit_idx, (uint) scu_port_num,
 540                                  (uint) scu_subport_num, unit_idx, (uint) cpu_port_num, is_exp);
 541       }
 542     else
 543       {
 544         sim_printf ("cable SCU: can't parse IOM or CPU\r\n");
 545         return SCPE_ARG;
 546       }
 547   }
 548 
 549 static t_stat cable_ctlr_to_iom (int uncable, struct ctlr_to_iom_s * there,
     /* [previous][next][first][last][top][bottom][index][help] */
 550                                  uint iom_unit_idx, uint chan_num)
 551   {
 552     if (uncable)
 553       {
 554         if (! there->in_use)
 555           {
 556             sim_printf ("error: UNCABLE: controller not cabled\r\n");
 557             return SCPE_ARG;
 558           }
 559         if (there->iom_unit_idx != iom_unit_idx ||
 560             there->chan_num != chan_num)
 561           {
 562             sim_printf ("error: UNCABLE: wrong controller\r\n");
 563             return SCPE_ARG;
 564           }
 565         there->in_use = false;
 566       }
 567    else
 568       {
 569         if (there->in_use)
 570           {
 571             sim_printf ("error: CABLE: controller in use\r\n");
 572             return SCPE_ARG;
 573           }
 574         there->in_use = true;
 575         there->iom_unit_idx = iom_unit_idx;
 576         there->chan_num     = chan_num;
 577       }
 578     return SCPE_OK;
 579   }
 580 
 581 static t_stat cable_ctlr (int uncable,
     /* [previous][next][first][last][top][bottom][index][help] */
 582                           uint iom_unit_idx, uint chan_num,
 583                           uint ctlr_unit_idx, uint port_num,
 584                           char * service,
 585                           DEVICE * devp,
 586                           struct ctlr_to_iom_s * there,
 587                           enum ctlr_type_e ctlr_type, enum chan_type_e chan_type,
 588                           UNIT * unitp, iom_cmd_t * iom_cmd)
 589   {
 590     if (ctlr_unit_idx >= devp->numunits)
 591       {
 592         sim_printf ("%s: unit index out of range <%d>\r\n",
 593                     service, ctlr_unit_idx);
 594         return SCPE_ARG;
 595       }
 596 
 597     struct iom_to_ctlr_s * p = & cables->iom_to_ctlr[iom_unit_idx][chan_num];
 598 
 599     if (uncable)
 600       {
 601         if (! p->in_use)
 602           {
 603             sim_printf ("%s: not cabled\r\n", service);
 604             return SCPE_ARG;
 605           }
 606 
 607         if (p->ctlr_unit_idx != ctlr_unit_idx)
 608           {
 609             sim_printf ("%s: Wrong IOM expected %d, found %d\r\n",
 610                         service, ctlr_unit_idx, p->ctlr_unit_idx);
 611             return SCPE_ARG;
 612           }
 613 
 614         // Unplug the other end of the cable
 615         t_stat rc = cable_ctlr_to_iom (uncable, there,
 616                                        iom_unit_idx, chan_num);
 617         if (rc)
 618           {
 619             return rc;
 620           }
 621         p->in_use  = false;
 622         p->iom_cmd = NULL;
 623       }
 624     else
 625       {
 626         if (p->in_use)
 627           {
 628             sim_printf ("%s: socket in use; unit number %d. (%o); "
 629                         "not cabling.\r\n", service, iom_unit_idx, iom_unit_idx);
 630             return SCPE_ARG;
 631           }
 632 
 633         // Plug the other end of the cable in
 634         t_stat rc = cable_ctlr_to_iom (uncable, there,
 635                                        iom_unit_idx, chan_num);
 636         if (rc)
 637           {
 638             return rc;
 639           }
 640         p->in_use        = true;
 641         p->ctlr_unit_idx = ctlr_unit_idx;
 642         p->port_num      = port_num;
 643         p->ctlr_type     = ctlr_type;
 644         p->chan_type     = chan_type;
 645         p->dev           = devp;
 646         p->board         = unitp;
 647         p->iom_cmd       = iom_cmd;
 648       }
 649 
 650     return SCPE_OK;
 651   }
 652 
 653 //    cable IOMx chan# MTPx [port#]  // tape controller
 654 //    cable IOMx chan# MSPx [port#]  // disk controller
 655 //    cable IOMx chah# IPCx [port#]  // FIPS disk controller
 656 //    cable IOMx chan# OPCx          // Operator console
 657 //    cable IOMx chan# FNPx          // FNP
 658 //    cable IOMx chan# ABSIx         // ABSI
 659 //    cable IOMx chan# MGPx          // MGP
 660 //    cable IOMx chan# NETx          // NET
 661 //    cable IOMx chan# SKCx          // Socket controller
 662 
 663 static t_stat cable_iom (int uncable, uint iom_unit_idx, char * * name_save)
     /* [previous][next][first][last][top][bottom][index][help] */
 664   {
 665     if (iom_unit_idx >= iom_dev.numunits)
 666       {
 667         sim_printf ("error: CABLE IOM: unit number out of range <%d>\r\n",
 668                     iom_unit_idx);
 669         return SCPE_ARG;
 670       }
 671 
 672     int chan_num = getval (name_save, "IOM channel number");
 673 
 674     if (chan_num < 0 || chan_num >= MAX_CHANNELS)
 675       {
 676         sim_printf ("error: CABLE IOM channel number out of range <%d>\r\n",
 677                     chan_num);
 678         return SCPE_ARG;
 679       }
 680 
 681     // extract controller type
 682     char * param = strtok_r (NULL, ", ", name_save);
 683     if (! param)
 684       {
 685         sim_printf ("error: CABLE IOM can't parse controller type\r\n");
 686         return SCPE_ARG;
 687       }
 688     uint unit_idx;
 689 
 690     // IOMx IPCx
 691     if (name_match (param, "IPC", & unit_idx))
 692       {
 693         if (unit_idx >= N_IPC_UNITS_MAX)
 694           {
 695             sim_printf ("error: CABLE IOM: IPC unit number out of range <%d>\r\n", unit_idx);
 696             return SCPE_ARG;
 697           }
 698 
 699         // extract IPC port number
 700         int ipc_port_num = 0;
 701         param = strtok_r (NULL, ", ", name_save);
 702         if (param)
 703           ipc_port_num = parseval (param);
 704 
 705         if (ipc_port_num < 0 || ipc_port_num >= MAX_CTLR_PORTS)
 706           {
 707             sim_printf ("error: CABLE IOM: IPC port number out of range <%d>\r\n", ipc_port_num);
 708             return SCPE_ARG;
 709           }
 710         return cable_ctlr (uncable,
 711                            iom_unit_idx, (uint) chan_num,
 712                            unit_idx, (uint) ipc_port_num,
 713                            "CABLE IOMx IPCx",
 714                            & ipc_dev,
 715                            & cables->ipc_to_iom[unit_idx][ipc_port_num],
 716                            CTLR_T_IPC, chan_type_PSI,
 717                            & ipc_unit [unit_idx], dsk_iom_cmd); // XXX mtp_iom_cmd?
 718       }
 719 
 720     // IOMx MSPx
 721     if (name_match (param, "MSP", & unit_idx))
 722       {
 723         if (unit_idx >= N_MSP_UNITS_MAX)
 724           {
 725             sim_printf ("error: CABLE IOM: MSP unit number out of range <%d>\r\n", unit_idx);
 726             return SCPE_ARG;
 727           }
 728 
 729         // extract MSP port number
 730         int msp_port_num = 0;
 731         param = strtok_r (NULL, ", ", name_save);
 732         if (param)
 733           msp_port_num = parseval (param);
 734 
 735         if (msp_port_num < 0 || msp_port_num >= MAX_CTLR_PORTS)
 736           {
 737             sim_printf ("error: CABLE IOM: MSP port number out of range <%d>\r\n", msp_port_num);
 738             return SCPE_ARG;
 739           }
 740         return cable_ctlr (uncable,
 741                            iom_unit_idx, (uint) chan_num,
 742                            unit_idx, (uint) msp_port_num,
 743                            "CABLE IOMx MSPx",
 744                            & msp_dev,
 745                            & cables->msp_to_iom[unit_idx][msp_port_num],
 746                            CTLR_T_MSP, chan_type_PSI,
 747                            & msp_unit [unit_idx], dsk_iom_cmd); // XXX mtp_iom_cmd?
 748       }
 749 
 750     // IOMx MTPx
 751     if (name_match (param, "MTP", & unit_idx))
 752       {
 753         if (unit_idx >= N_MTP_UNITS_MAX)
 754           {
 755             sim_printf ("error: CABLE IOM: MTP unit number out of range <%d>\r\n", unit_idx);
 756             return SCPE_ARG;
 757           }
 758 
 759         // extract MTP port number
 760         int mtp_port_num = 0;
 761         param = strtok_r (NULL, ", ", name_save);
 762         if (param)
 763           mtp_port_num = parseval (param);
 764 
 765         if (mtp_port_num < 0 || mtp_port_num >= MAX_CTLR_PORTS)
 766           {
 767             sim_printf ("error: CABLE IOM: MTP port number out of range <%d>\r\n", mtp_port_num);
 768             return SCPE_ARG;
 769           }
 770         return cable_ctlr (uncable,
 771                            iom_unit_idx, (uint) chan_num,
 772                            unit_idx, (uint) mtp_port_num,
 773                            "CABLE IOMx MTPx",
 774                            & mtp_dev,
 775                            & cables->mtp_to_iom[unit_idx][mtp_port_num],
 776                            CTLR_T_MTP, chan_type_PSI,
 777                            & mtp_unit [unit_idx], mt_iom_cmd); // XXX mtp_iom_cmd?
 778       }
 779 
 780     // IOMx URPx
 781     if (name_match (param, "URP", & unit_idx))
 782       {
 783         if (unit_idx >= N_URP_UNITS_MAX)
 784           {
 785             sim_printf ("error: CABLE IOM: URP unit number out of range <%d>\r\n", unit_idx);
 786             return SCPE_ARG;
 787           }
 788 
 789         // extract URP port number
 790         int urp_port_num = 0;
 791         param = strtok_r (NULL, ", ", name_save);
 792         if (param)
 793           urp_port_num = parseval (param);
 794 
 795         if (urp_port_num < 0 || urp_port_num >= MAX_CTLR_PORTS)
 796           {
 797             sim_printf ("error: CABLE IOM: URP port number out of range <%d>\r\n", urp_port_num);
 798             return SCPE_ARG;
 799           }
 800 
 801         return cable_ctlr (uncable,
 802                            iom_unit_idx, (uint) chan_num,
 803                            unit_idx, (uint) urp_port_num,
 804                            "CABLE IOMx URPx",
 805                            & urp_dev,
 806                            & cables->urp_to_iom[unit_idx][urp_port_num],
 807                            CTLR_T_URP, chan_type_PSI,
 808                            & urp_unit [unit_idx], urp_iom_cmd);
 809       }
 810 
 811     // IOMx OPCx
 812     if (name_match (param, "OPC", & unit_idx))
 813       {
 814         if (unit_idx >= N_OPC_UNITS_MAX)
 815           {
 816             sim_printf ("error: CABLE IOM: OPC unit number out of range <%d>\r\n", unit_idx);
 817             return SCPE_ARG;
 818           }
 819 
 820         uint opc_port_num = 0;
 821         return cable_ctlr (uncable,
 822                            iom_unit_idx, (uint) chan_num,
 823                            unit_idx, opc_port_num,
 824                            "CABLE IOMx OPCx",
 825                            & opc_dev,
 826                            & cables->opc_to_iom[unit_idx][opc_port_num],
 827                            CTLR_T_OPC, chan_type_CPI,
 828                            & opc_unit [unit_idx], opc_iom_cmd);
 829       }
 830 
 831     // IOMx FNPx
 832     if (name_match (param, "FNP", & unit_idx))
 833       {
 834         if (unit_idx >= N_FNP_UNITS_MAX)
 835           {
 836             sim_printf ("error: CABLE IOM: FNP unit number out of range <%d>\r\n", unit_idx);
 837             return SCPE_ARG;
 838           }
 839 
 840         uint fnp_port_num = 0;
 841         return cable_ctlr (uncable,
 842                            iom_unit_idx, (uint) chan_num,
 843                            unit_idx, fnp_port_num,
 844                            "CABLE IOMx FNPx",
 845                            & fnp_dev,
 846                            & cables->fnp_to_iom[unit_idx][fnp_port_num],
 847                            CTLR_T_FNP, chan_type_direct,
 848                            & fnp_unit [unit_idx], fnp_iom_cmd);
 849       }
 850 
 851 #if defined(WITH_ABSI_DEV)
 852 # if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined (CROSS_MINGW64) && !defined(CROSS_MINGW32)
 853     // IOMx ABSIx
 854     if (name_match (param, "ABSI", & unit_idx))
 855       {
 856         if (unit_idx >= N_ABSI_UNITS_MAX)
 857           {
 858             sim_printf ("error: CABLE IOM: ABSI unit number out of range <%d>\r\n", unit_idx);
 859             return SCPE_ARG;
 860           }
 861 
 862         uint absi_port_num = 0;
 863         return cable_ctlr (uncable,
 864                            iom_unit_idx, (uint) chan_num,
 865                            unit_idx, absi_port_num,
 866                            "CABLE IOMx ABSIx",
 867                            & absi_dev,
 868                            & cables->absi_to_iom[unit_idx][absi_port_num],
 869                            CTLR_T_ABSI, chan_type_direct,
 870                            & absi_unit [unit_idx], absi_iom_cmd);
 871       }
 872 # endif /* if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined (CROSS_MINGW64) && !defined(CROSS_MINGW32) */
 873 #endif /* if defined(WITH_ABSI_DEV) */
 874 
 875 #if defined(WITH_MGP_DEV)
 876     // IOMx MGPx
 877     if (name_match (param, "MGP", & unit_idx))
 878       {
 879         if (unit_idx >= N_MGP_UNITS_MAX)
 880           {
 881             sim_printf ("error: CABLE IOM: MGP unit number out of range <%d>\r\n", unit_idx);
 882             return SCPE_ARG;
 883           }
 884 
 885         uint mgp_port_num = 0;
 886         return cable_ctlr (uncable,
 887                            iom_unit_idx, (uint) chan_num,
 888                            unit_idx, mgp_port_num,
 889                            "CABLE IOMx MGPx",
 890                            & mgp_dev,
 891                            & cables->mgp_to_iom[unit_idx][mgp_port_num],
 892                            CTLR_T_MGP, chan_type_direct,
 893                            & mgp_unit [unit_idx], mgp_iom_cmd);
 894       }
 895 #endif /* if defined(WITH_MGP_DEV) */
 896 
 897 #if defined(WITH_NET_DEV)
 898     // IOMx NETx
 899     if (name_match (param, "NET", & unit_idx))
 900       {
 901         if (unit_idx >= N_NET_UNITS_MAX)
 902           {
 903             sim_printf ("error: CABLE IOM: NET unit number out of range <%d>\r\n", unit_idx);
 904             return SCPE_ARG;
 905           }
 906 
 907         uint net_port_num = 0;
 908         return cable_ctlr (uncable,
 909                            iom_unit_idx, (uint) chan_num,
 910                            unit_idx, net_port_num,
 911                            "CABLE IOMx NETx",
 912                            & net_dev,
 913                            & cables->net_to_iom[unit_idx][net_port_num],
 914                            CTLR_T_NET, chan_type_direct,
 915                            & net_unit [unit_idx], net_iom_cmd);
 916       }
 917 #endif /* if defined(WITH_NET_DEV) */
 918 
 919 #if defined(WITH_SOCKET_DEV)
 920 # if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW64) && !defined(CROSS_MINGW32)
 921     // IOMx SKCx
 922     if (name_match (param, "SKC", & unit_idx))
 923       {
 924         if (unit_idx >= N_SKC_UNITS_MAX)
 925           {
 926             sim_printf ("error: CABLE IOM: SKC unit number out of range <%d>\r\n", unit_idx);
 927             return SCPE_ARG;
 928           }
 929 
 930         uint skc_port_num = 0;
 931         return cable_ctlr (uncable,
 932                            iom_unit_idx, (uint) chan_num,
 933                            unit_idx, skc_port_num,
 934                            "CABLE IOMx SKCx",
 935                            & skc_dev,
 936                            & cables->sk_to_iom[unit_idx][skc_port_num],
 937                            CTLR_T_SKC, chan_type_direct,
 938                            & sk_unit [unit_idx], skc_iom_cmd);
 939       }
 940 # endif /* if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW64) && !defined(CROSS_MINGW32) */
 941 #endif /* if defined(WITH_SOCKET_DEV) */
 942 
 943     sim_printf ("cable IOM: can't parse controller type\r\n");
 944     return SCPE_ARG;
 945   }
 946 
 947 static t_stat cable_periph_to_ctlr (int uncable,
     /* [previous][next][first][last][top][bottom][index][help] */
 948                                     uint ctlr_unit_idx, uint dev_code,
 949                                     enum ctlr_type_e ctlr_type,
 950                                     struct dev_to_ctlr_s * there,
 951                                     UNUSED iom_cmd_t * iom_cmd)
 952   {
 953     if (uncable)
 954       {
 955         if (! there->in_use)
 956           {
 957             sim_printf ("error: UNCABLE: device not cabled\r\n");
 958             return SCPE_ARG;
 959           }
 960         if (there->ctlr_unit_idx != ctlr_unit_idx ||
 961             there->dev_code != dev_code)
 962           {
 963             sim_printf ("error: UNCABLE: wrong controller\r\n");
 964             return SCPE_ARG;
 965           }
 966         there->in_use = false;
 967       }
 968    else
 969       {
 970         if (there->in_use)
 971           {
 972             sim_printf ("error: CABLE: device in use\r\n");
 973             return SCPE_ARG;
 974           }
 975         there->in_use        = true;
 976         there->ctlr_unit_idx = ctlr_unit_idx;
 977         there->dev_code      = dev_code;
 978         there->ctlr_type     = ctlr_type;
 979       }
 980     return SCPE_OK;
 981   }
 982 
 983 static t_stat cable_periph (int uncable,
     /* [previous][next][first][last][top][bottom][index][help] */
 984                             uint ctlr_unit_idx,
 985                             uint dev_code,
 986                             enum ctlr_type_e ctlr_type,
 987                             struct ctlr_to_dev_s * here,
 988                             uint unit_idx,
 989                             iom_cmd_t * iom_cmd,
 990                             struct dev_to_ctlr_s * there,
 991                             char * service)
 992   {
 993     if (uncable)
 994       {
 995         if (! here->in_use)
 996           {
 997             sim_printf ("%s: socket not in use\r\n", service);
 998             return SCPE_ARG;
 999           }
1000         // Unplug the other end of the cable
1001         t_stat rc = cable_periph_to_ctlr (uncable,
1002                                           ctlr_unit_idx, dev_code, ctlr_type,
1003                                           there,
1004                                           iom_cmd);
1005         if (rc)
1006           {
1007             return rc;
1008           }
1009 
1010         here->in_use  = false;
1011         here->iom_cmd = NULL;
1012       }
1013     else
1014       {
1015         if (here->in_use)
1016           {
1017             sim_printf ("%s: controller socket in use; unit number %u. dev_code %oo\r\n",
1018                         service, ctlr_unit_idx, dev_code);
1019             return SCPE_ARG;
1020           }
1021 
1022         // Plug the other end of the cable in
1023         t_stat rc = cable_periph_to_ctlr (uncable,
1024                                           ctlr_unit_idx, dev_code, ctlr_type,
1025                                           there,
1026                                           iom_cmd);
1027         if (rc)
1028           {
1029             return rc;
1030           }
1031 
1032         here->in_use   = true;
1033         here->unit_idx = unit_idx;
1034         here->iom_cmd  = iom_cmd;
1035       }
1036 
1037     return SCPE_OK;
1038   }
1039 
1040 // cable MTPx dev_code TAPEx
1041 
1042 static t_stat cable_mtp (int uncable, uint ctlr_unit_idx, char * * name_save)
     /* [previous][next][first][last][top][bottom][index][help] */
1043   {
1044     if (ctlr_unit_idx >= mtp_dev.numunits)
1045       {
1046         sim_printf ("error: CABLE MTP: controller unit number out of range <%d>\r\n",
1047                     ctlr_unit_idx);
1048         return SCPE_ARG;
1049       }
1050 
1051     int dev_code = getval (name_save, "MTP device code");
1052 
1053     if (dev_code < 0 || dev_code >= MAX_CHANNELS)
1054       {
1055         sim_printf ("error: CABLE MTP device code out of range <%d>\r\n",
1056                     dev_code);
1057         return SCPE_ARG;
1058       }
1059 
1060     // extract tape index
1061     char * param = strtok_r (NULL, ", ", name_save);
1062     if (! param)
1063       {
1064         sim_printf ("error: CABLE IOM can't parse device name\r\n");
1065         return SCPE_ARG;
1066       }
1067     uint mt_unit_idx;
1068 
1069     // MPCx TAPEx
1070     if (name_match (param, "TAPE", & mt_unit_idx))
1071       {
1072         if (mt_unit_idx >= N_MT_UNITS_MAX)
1073           {
1074             sim_printf ("error: CABLE IOM: TAPE unit number out of range <%d>\r\n", mt_unit_idx);
1075             return SCPE_ARG;
1076           }
1077 
1078         return cable_periph (uncable,
1079                              ctlr_unit_idx,
1080                              (uint) dev_code,
1081                              CTLR_T_MTP,
1082                              & cables->mtp_to_tape[ctlr_unit_idx][dev_code],
1083                              mt_unit_idx,
1084                              mt_iom_cmd,
1085                              & cables->tape_to_mtp[mt_unit_idx],
1086                              "CABLE MTPx TAPEx");
1087       }
1088 
1089     sim_printf ("cable MTP: can't parse device name\r\n");
1090     return SCPE_ARG;
1091   }
1092 
1093 // cable IPCx dev_code DISKx
1094 
1095 static t_stat cable_ipc (int uncable, uint ctlr_unit_idx, char * * name_save)
     /* [previous][next][first][last][top][bottom][index][help] */
1096   {
1097     if (ctlr_unit_idx >= ipc_dev.numunits)
1098       {
1099         sim_printf ("error: CABLE IPC: controller unit number out of range <%d>\r\n",
1100                     ctlr_unit_idx);
1101         return SCPE_ARG;
1102       }
1103 
1104     int dev_code = getval (name_save, "IPC device code");
1105 
1106     if (dev_code < 0 || dev_code >= MAX_CHANNELS)
1107       {
1108         sim_printf ("error: CABLE IPC device code out of range <%d>\r\n",
1109                     dev_code);
1110         return SCPE_ARG;
1111       }
1112 
1113     // extract tape index
1114     char * param = strtok_r (NULL, ", ", name_save);
1115     if (! param)
1116       {
1117         sim_printf ("error: CABLE IOM can't parse device name\r\n");
1118         return SCPE_ARG;
1119       }
1120     uint dsk_unit_idx;
1121 
1122     // MPCx DISKx
1123     if (name_match (param, "DISK", & dsk_unit_idx))
1124       {
1125         if (dsk_unit_idx >= N_DSK_UNITS_MAX)
1126           {
1127             sim_printf ("error: CABLE IOM: DISK unit number out of range <%d>\r\n", dsk_unit_idx);
1128             return SCPE_ARG;
1129           }
1130 
1131         return cable_periph (uncable,
1132                              ctlr_unit_idx,
1133                              (uint) dev_code,
1134                              CTLR_T_IPC,
1135                              & cables->ipc_to_dsk[ctlr_unit_idx][dev_code],
1136                              dsk_unit_idx,
1137                              dsk_iom_cmd, // XXX
1138                              & cables->dsk_to_ctlr[dsk_unit_idx],
1139                              "CABLE IPCx DISKx");
1140       }
1141 
1142     sim_printf ("cable IPC: can't parse device name\r\n");
1143     return SCPE_ARG;
1144   }
1145 
1146 // cable MSPx dev_code DISKx
1147 
1148 static t_stat cable_msp (int uncable, uint ctlr_unit_idx, char * * name_save)
     /* [previous][next][first][last][top][bottom][index][help] */
1149   {
1150     if (ctlr_unit_idx >= msp_dev.numunits)
1151       {
1152         sim_printf ("error: CABLE MSP: controller unit number out of range <%d>\r\n",
1153                     ctlr_unit_idx);
1154         return SCPE_ARG;
1155       }
1156 
1157     int dev_code = getval (name_save, "MSP device code");
1158 
1159     if (dev_code < 0 || dev_code >= MAX_CHANNELS)
1160       {
1161         sim_printf ("error: CABLE MSP device code out of range <%d>\r\n",
1162                     dev_code);
1163         return SCPE_ARG;
1164       }
1165 
1166     // extract tape index
1167     char * param = strtok_r (NULL, ", ", name_save);
1168     if (! param)
1169       {
1170         sim_printf ("error: CABLE IOM can't parse device name\r\n");
1171         return SCPE_ARG;
1172       }
1173     uint dsk_unit_idx;
1174 
1175     // MPCx DISKx
1176     if (name_match (param, "DISK", & dsk_unit_idx))
1177       {
1178         if (dsk_unit_idx >= N_DSK_UNITS_MAX)
1179           {
1180             sim_printf ("error: CABLE IOM: DISK unit number out of range <%d>\r\n", dsk_unit_idx);
1181             return SCPE_ARG;
1182           }
1183 
1184         return cable_periph (uncable,
1185                              ctlr_unit_idx,
1186                              (uint) dev_code,
1187                              CTLR_T_MSP,
1188                              & cables->msp_to_dsk[ctlr_unit_idx][dev_code],
1189                              dsk_unit_idx,
1190                              dsk_iom_cmd, // XXX
1191                              & cables->dsk_to_ctlr[dsk_unit_idx],
1192                              "CABLE MSPx DISKx");
1193       }
1194 
1195     sim_printf ("cable MSP: can't parse device name\r\n");
1196     return SCPE_ARG;
1197   }
1198 
1199 // cable URPx dev_code [RDRx PUNx PRTx]
1200 
1201 static t_stat cable_urp (int uncable, uint ctlr_unit_idx, char * * name_save)
     /* [previous][next][first][last][top][bottom][index][help] */
1202   {
1203     if (ctlr_unit_idx >= urp_dev.numunits)
1204       {
1205         sim_printf ("error: CABLE URP: controller unit number out of range <%d>\r\n",
1206                     ctlr_unit_idx);
1207         return SCPE_ARG;
1208       }
1209 
1210     int dev_code = getval (name_save, "URP device code");
1211 
1212     if (dev_code < 0 || dev_code >= MAX_CHANNELS)
1213       {
1214         sim_printf ("error: CABLE URP device code out of range <%d>\r\n",
1215                     dev_code);
1216         return SCPE_ARG;
1217       }
1218 
1219     // extract tape index
1220     char * param = strtok_r (NULL, ", ", name_save);
1221     if (! param)
1222       {
1223         sim_printf ("error: CABLE IOM can't parse device name\r\n");
1224         return SCPE_ARG;
1225       }
1226     uint unit_idx;
1227 
1228     // URPx RDRx
1229     if (name_match (param, "RDR", & unit_idx))
1230       {
1231         if (unit_idx >= N_RDR_UNITS_MAX)
1232           {
1233             sim_printf ("error: CABLE IOM: DISK unit number out of range <%d>\r\n", unit_idx);
1234             return SCPE_ARG;
1235           }
1236 
1237         return cable_periph (uncable,
1238                              ctlr_unit_idx,
1239                              (uint) dev_code,
1240                              CTLR_T_URP,
1241                              & cables->urp_to_urd[ctlr_unit_idx][dev_code],
1242                              unit_idx,
1243                              rdr_iom_cmd, // XXX
1244                              & cables->rdr_to_urp[unit_idx],
1245                              "CABLE URPx RDRx");
1246       }
1247 
1248     // URPx PUNx
1249     if (name_match (param, "PUN", & unit_idx))
1250       {
1251         if (unit_idx >= N_PUN_UNITS_MAX)
1252           {
1253             sim_printf ("error: CABLE IOM: DISK unit number out of range <%d>\r\n", unit_idx);
1254             return SCPE_ARG;
1255           }
1256 
1257         return cable_periph (uncable,
1258                              ctlr_unit_idx,
1259                              (uint) dev_code,
1260                              CTLR_T_URP,
1261                              & cables->urp_to_urd[ctlr_unit_idx][dev_code],
1262                              unit_idx,
1263                              pun_iom_cmd, // XXX
1264                              & cables->pun_to_urp[unit_idx],
1265                              "CABLE URPx PUNx");
1266       }
1267 
1268     // URPx PRTx
1269     if (name_match (param, "PRT", & unit_idx))
1270       {
1271         if (unit_idx >= N_PRT_UNITS_MAX)
1272           {
1273             sim_printf ("error: CABLE IOM: DISK unit number out of range <%d>\r\n", unit_idx);
1274             return SCPE_ARG;
1275           }
1276 
1277         return cable_periph (uncable,
1278                              ctlr_unit_idx,
1279                              (uint) dev_code,
1280                              CTLR_T_URP,
1281                              & cables->urp_to_urd[ctlr_unit_idx][dev_code],
1282                              unit_idx,
1283                              prt_iom_cmd, // XXX
1284                              & cables->prt_to_urp[unit_idx],
1285                              "CABLE URPx PRTx");
1286       }
1287 
1288     sim_printf ("cable URP: can't parse device name\r\n");
1289     return SCPE_ARG;
1290   }
1291 
1292 t_stat sys_cable (int32 arg, const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
1293   {
1294     char * copy = strdup (buf);
1295     if (!copy)
1296       {
1297         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1298                        __func__, __FILE__, __LINE__);
1299 #if defined(USE_BACKTRACE)
1300 # if defined(SIGUSR2)
1301         (void)raise(SIGUSR2);
1302         /*NOTREACHED*/ /* unreachable */
1303 # endif /* if defined(SIGUSR2) */
1304 #endif /* if defined(USE_BACKTRACE) */
1305         abort();
1306       }
1307     t_stat rc = SCPE_ARG;
1308 
1309     // process statement
1310 
1311     // extract first word
1312     char * name_save = NULL;
1313     char * name;
1314     name = strtok_r (copy, ", \t", & name_save);
1315     if (! name)
1316       {
1317         //sim_debug (DBG_ERR, & sys_dev, "sys_cable: could not parse name\r\n");
1318         sim_printf ("error: CABLE: sys_cable could not parse name\r\n");
1319         goto exit;
1320       }
1321 
1322     uint unit_num;
1323     if (strcasecmp (name, "RIPOUT") == 0)
1324       rc = sys_cable_ripout (0, NULL);
1325     else if (strcasecmp (name, "SHOW") == 0)
1326       rc = sys_cable_show (0, NULL);
1327     else if (strcasecmp (name, "DUMP") == 0)
1328       rc = sys_cable_show (1, NULL);
1329     else if (strcasecmp (name, "GRAPH") == 0)
1330       rc = sys_cable_graph ();
1331     else if (name_match (name, "SCU", & unit_num))
1332       rc = cable_scu (arg, unit_num, & name_save);
1333     else if (name_match (name, "IOM", & unit_num))
1334       rc = cable_iom (arg, unit_num, & name_save);
1335     else if (name_match (name, "MTP", & unit_num))
1336       rc = cable_mtp (arg, unit_num, & name_save);
1337     else if (name_match (name, "IPC", & unit_num))
1338       rc = cable_ipc (arg, unit_num, & name_save);
1339     else if (name_match (name, "MSP", & unit_num))
1340       rc = cable_msp (arg, unit_num, & name_save);
1341     else if (name_match (name, "URP", & unit_num))
1342       rc = cable_urp (arg, unit_num, & name_save);
1343     else
1344       {
1345         sim_printf ("error: CABLE: Invalid name <%s>\r\n", name);
1346         goto exit;
1347       }
1348     if (name_save && strlen (name_save))
1349       {
1350         sim_printf ("CABLE ignored '%s'\r\n", name_save);
1351       }
1352 exit:
1353     FREE (copy);
1354     return rc;
1355   }
1356 
1357 static void cable_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1358   {
1359     // sets cablesFromIomToDev[iomUnitIdx].devices[chanNum][dev_code].type
1360     //  to DEVT_NONE and in_use to false
1361 
1362     (void)memset (cables, 0, sizeof (struct cables_s));
1363   }
1364 
1365 #define all(i,n) \
1366   for (uint i = 0; i < n; i ++)
1367 
1368 static t_stat
1369 sys_cable_graph (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1370 {
1371         // Find used CPUs
1372         bool cpus_used[N_CPU_UNITS_MAX];
1373         (void)memset (cpus_used, 0, sizeof (cpus_used));
1374 
1375         all (u, N_CPU_UNITS_MAX) all (prt, N_CPU_PORTS)
1376         {
1377                 struct cpu_to_scu_s *p = &cables->cpu_to_scu[u][prt];
1378                 if (p->in_use)
1379                         cpus_used[u] = true;
1380         }
1381 
1382         // Find used SCUs
1383         bool scus_used[N_SCU_UNITS_MAX];
1384         (void)memset (scus_used, 0, sizeof (scus_used));
1385 
1386         all (u, N_SCU_UNITS_MAX) all (prt, N_SCU_PORTS)
1387         {
1388                 struct scu_to_iom_s *p = &cables->scu_to_iom[u][prt];
1389                 if (p->in_use)
1390                         scus_used[u] = true;
1391         }
1392         all (u, N_CPU_UNITS_MAX) all (prt, N_CPU_PORTS)
1393         {
1394                 struct cpu_to_scu_s *p = &cables->cpu_to_scu[u][prt];
1395                 if (p->in_use)
1396                         scus_used[p->scu_unit_idx] = true;
1397         }
1398 
1399         // Find used IOMs
1400         bool ioms_used[N_IOM_UNITS_MAX];
1401         (void)memset (ioms_used, 0, sizeof (ioms_used));
1402 
1403         all (u, N_SCU_UNITS_MAX) all (prt, N_SCU_PORTS)
1404         {
1405                 struct scu_to_iom_s *p = &cables->scu_to_iom[u][prt];
1406                 if (p->in_use)
1407                         ioms_used[p->iom_unit_idx] = true;
1408         }
1409 
1410         // Create graph
1411         sim_printf ("graph {\r\n");
1412         sim_printf ("    rankdir=TD;\r\n");
1413 
1414         // Rank CPUs
1415         sim_printf ("    { rank=same; ");
1416         for (int i = 0; i < N_CPU_UNITS_MAX; i++)
1417          if (cpus_used[i])
1418           sim_printf (" CPU%c [shape=diamond,  \
1419                                color=lightgreen, \
1420                                style=filled];",
1421                           i + 'A');
1422         sim_printf ("}\r\n");
1423 
1424         // Rank SCUs
1425         sim_printf ("    { rank=same; ");
1426         for (int i = 0; i < N_SCU_UNITS_MAX; i++)
1427          if (scus_used[i])
1428           sim_printf (" SCU%c [shape=doubleoctagon, \
1429                                color=deepskyblue4,  \
1430                                style=filled];",
1431                           i + 'A');
1432         sim_printf ("}\r\n");
1433 
1434         // Rank IOMs
1435         sim_printf ("    { rank=same; ");
1436         for (int i = 0; i < N_IOM_UNITS_MAX; i++)
1437          if (ioms_used[i])
1438           sim_printf (" IOM%c [shape=doublecircle, \
1439                                color=cadetblue4,   \
1440                                style=filled];",
1441                           i + 'A');
1442         sim_printf ("}\r\n");
1443 
1444 #define R_CTLR_IOM(big, small, shape, color)                                 \
1445         sim_printf ("    { rank=same; ");                                    \
1446         all (u, N_ ## big ## _UNITS_MAX) all (prt, MAX_CTLR_PORTS)           \
1447         {                                                                    \
1448                 struct ctlr_to_iom_s *p = &cables->small ## _to_iom[u][prt]; \
1449                 if (p->in_use)                                               \
1450                 sim_printf (" %s%d [shape=%s, color=%s, style=filled];",     \
1451                                 #big, u, #shape, #color);                    \
1452         }                                                                    \
1453         sim_printf ("}\r\n");
1454 
1455         R_CTLR_IOM (MTP,  mtp,  oval,    firebrick1)
1456         R_CTLR_IOM (MSP,  msp,  oval,    firebrick2)
1457         R_CTLR_IOM (IPC,  ipc,  oval,    firebrick3)
1458         R_CTLR_IOM (FNP,  fnp,  egg,     snow2)
1459         R_CTLR_IOM (URP,  urp,  polygon, gold4)
1460         R_CTLR_IOM (DIA,  dia,  oval,    orange)
1461 #if defined(WITH_ABSI_DEV)
1462 # if !defined(__MINGW64__)
1463         R_CTLR_IOM (ABSI, absi, oval,    teal)
1464 # endif /* if !defined(__MINGW64__) */
1465 #endif /* if defined(WITH_ABSI_DEV) */
1466 #if defined(WITH_MGP_DEV)
1467         R_CTLR_IOM (MGP, mgp, oval,    teal)
1468 #endif /* if defined(WITH_MGP_DEV) */
1469 #if defined(WITH_NET_DEV)
1470         R_CTLR_IOM (NET, net, oval,    teal)
1471 #endif /* if defined(WITH_NET_DEV) */
1472         R_CTLR_IOM (OPC,  opc,  oval,    hotpink)
1473 
1474 #define R_DEV_CTLR(from_big, from_small, to_label,                       \
1475                       to_big, to_small, shape, color)                    \
1476         sim_printf ("    { rank=same; ");                                \
1477         all (u, N_ ## to_big ## _UNITS_MAX)                              \
1478         {                                                                \
1479                 struct dev_to_ctlr_s *p =                                \
1480                     &cables->to_small ## _to_ ## from_small[u];          \
1481                 if (p->in_use)                                           \
1482                 sim_printf (" %s%d [shape=%s, style=filled, color=%s];", \
1483                                 #to_label, u, #shape, #color);           \
1484         }                                                                \
1485         sim_printf ("}\r\n");
1486 
1487         R_DEV_CTLR (MTP,  mtp,  TAPE, MT,  tape, oval,     aquamarine3);
1488         R_DEV_CTLR (CTLR, ctlr, DISK, DSK, dsk,  cylinder, bisque3);
1489         R_DEV_CTLR (URP,  urp,  RDR,  RDR, rdr,  septagon, mediumpurple1);
1490         R_DEV_CTLR (URP,  urp,  PUN,  PUN, pun,  pentagon, maroon3);
1491         R_DEV_CTLR (URP,  urp,  PRT,  PRT, prt,  octagon,  yellowgreen);
1492 
1493         // Generate CPU/SCU cables
1494         all (u, N_CPU_UNITS_MAX) all (prt, N_CPU_PORTS)
1495         {
1496                 struct cpu_to_scu_s *p = &cables->cpu_to_scu[u][prt];
1497                 if (p->in_use)
1498                         sim_printf ("    CPU%c -- SCU%c;\r\n", u + 'A',
1499                                     p->scu_unit_idx + 'A');
1500         }
1501 
1502         // Generate SCI/IOM cables
1503         all (u, N_SCU_UNITS_MAX) all (prt, N_SCU_PORTS)
1504         {
1505                 struct scu_to_iom_s *p = &cables->scu_to_iom[u][prt];
1506                 if (p->in_use)
1507                         sim_printf ("    SCU%c -- IOM%c;\r\n", u + 'A',
1508                                     p->iom_unit_idx + 'A');
1509         }
1510 
1511         // Generate IOM to controller cables
1512         all (u, N_IOM_UNITS_MAX) all (c, MAX_CHANNELS)
1513         {
1514                 struct iom_to_ctlr_s *p = &cables->iom_to_ctlr[u][c];
1515                 if (p->in_use)
1516                         sim_printf ("    IOM%c -- %s%d;\r\n", u + 'A',
1517                                     ctlr_type_strs[p->ctlr_type],
1518                                     p->ctlr_unit_idx);
1519         }
1520 
1521         // Generate controller to device cables
1522 #define G_DEV_CTLR(from_big, from_small, to_label, to_big, to_small) \
1523         all (u, N_ ## to_big ## _UNITS_MAX)                          \
1524         {                                                            \
1525                 struct dev_to_ctlr_s *p =                            \
1526                     &cables->to_small ## _to_ ## from_small[u];      \
1527                 if (p->in_use)                                       \
1528                 sim_printf ("    %s%d -- %s%d;\r\n",                 \
1529                             ctlr_type_strs[p->ctlr_type],            \
1530                             p->ctlr_unit_idx, #to_label, u);         \
1531         }
1532 
1533         G_DEV_CTLR (MTP,  mtp,  TAPE, MT,  tape);
1534         G_DEV_CTLR (CTLR, ctlr, DISK, DSK, dsk);
1535         G_DEV_CTLR (URP,  urp,  RDR,  RDR, rdr);
1536         G_DEV_CTLR (URP,  urp,  PUN,  PUN, pun);
1537         G_DEV_CTLR (URP,  urp,  PRT,  PRT, prt);
1538 
1539         sim_printf ("}\r\n");
1540         return SCPE_OK;
1541 }
1542 
1543 t_stat sys_cable_show (int32 dump, UNUSED const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
1544   {
1545     sim_printf ("SCU <--> IOM\r\n");
1546     sim_printf ("   SCU port --> IOM port\r\n");
1547     all (u, N_SCU_UNITS_MAX)
1548       all (prt, N_SCU_PORTS)
1549         {
1550           struct scu_to_iom_s * p = & cables->scu_to_iom[u][prt];
1551           if (p->in_use)
1552             sim_printf (" %4u %4u    %4u %4u\r\n", u, prt, p->iom_unit_idx, p->iom_port_num);
1553         }
1554 
1555     if (dump)
1556       {
1557         sim_printf ("   IOM port --> SCU port\r\n");
1558         all (u, N_IOM_UNITS_MAX)
1559           all (prt, N_IOM_PORTS)
1560             {
1561               struct iom_to_scu_s * p = & cables->iom_to_scu[u][prt];
1562               if (p->in_use)
1563                 sim_printf (" %4u %4u    %4u %4u\r\n", u, prt, p->scu_unit_idx, p->scu_port_num);
1564             }
1565       }
1566     sim_printf ("\r\n");
1567 
1568     sim_printf ("SCU <--> CPU\r\n");
1569     sim_printf ("   SCU port --> CPU port\r\n");
1570     all (u, N_SCU_UNITS_MAX)
1571       all (prt, N_SCU_PORTS)
1572         all (sp, N_SCU_SUBPORTS)
1573           {
1574             struct scu_to_cpu_s * p = & cables->scu_to_cpu[u][prt][sp];
1575             if (p->in_use)
1576               sim_printf (" %4u %4u    %4u %4u\r\n", u, prt, p->cpu_unit_idx, p->cpu_port_num);
1577           }
1578 
1579     if (dump)
1580       {
1581         sim_printf ("   CPU port --> SCU port subport\r\n");
1582         all (u, N_CPU_UNITS_MAX)
1583           all (prt, N_CPU_PORTS)
1584             {
1585               struct cpu_to_scu_s * p = & cables->cpu_to_scu[u][prt];
1586               if (p->in_use)
1587                 sim_printf (" %4u %4u    %4u %4u  %4u\r\n",
1588                         u, prt, p->scu_unit_idx, p->scu_port_num, p->scu_subport_num);
1589             }
1590         }
1591     sim_printf ("\r\n");
1592 
1593     sim_printf ("IOM <--> controller\r\n");
1594     sim_printf ("                 ctlr       ctlr  chan\r\n");
1595     sim_printf ("   IOM chan -->  idx  port  type  type      device      board    command\r\n");
1596     all (u, N_IOM_UNITS_MAX)
1597       all (c, MAX_CHANNELS)
1598         {
1599           struct iom_to_ctlr_s * p = & cables->iom_to_ctlr[u][c];
1600           if (p->in_use)
1601             sim_printf (" %4u %4u     %4u  %4u %-6s  %-6s %10p %10p %10p\r\n",
1602                     u, c, p->ctlr_unit_idx, p->port_num, ctlr_type_strs[p->ctlr_type],
1603                     chan_type_strs[p->chan_type], (void *) p->dev,
1604                     (void *) p->board, (void *) p->iom_cmd);
1605         }
1606 
1607     if (dump)
1608       {
1609 #define CTLR_IOM(big,small)                                                                \
1610     sim_printf ("  %-4s port --> IOM channel\r\n", #big);                                  \
1611     all (u, N_ ## big ## _UNITS_MAX)                                                       \
1612       all (prt, MAX_CTLR_PORTS)                                                            \
1613         {                                                                                  \
1614           struct ctlr_to_iom_s * p = & cables->small ## _to_iom[u][prt];                   \
1615           if (p->in_use)                                                                   \
1616             sim_printf (" %4u %4u    %4u %4u\r\n", u, prt, p->iom_unit_idx, p->chan_num);  \
1617         }
1618         CTLR_IOM (MTP, mtp)
1619         CTLR_IOM (MSP, msp)
1620         CTLR_IOM (IPC, ipc)
1621         CTLR_IOM (URP, urp)
1622         CTLR_IOM (FNP, fnp)
1623         CTLR_IOM (DIA, dia)
1624 #if defined(WITH_ABSI_DEV)
1625 # if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1626         CTLR_IOM (ABSI, absi)
1627 # endif /* if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
1628 #endif /* if defined(WITH_ABSI_DEV) */
1629 #if defined(WITH_MGP_DEV)
1630         CTLR_IOM (MGP, mgp)
1631 #endif /* if defined(WITH_MGP_DEV) */
1632 #if defined(WITH_NET_DEV)
1633         CTLR_IOM (NET, net)
1634 #endif /* if defined(WITH_NET_DEV) */
1635 #if defined(WITH_SOCKET_DEV)
1636 # if !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1637         CTLR_IOM (SKC, sk)
1638 # endif /* if !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
1639 #endif /* if defined(WITH_SOCKET_DEV) */
1640         CTLR_IOM (OPC, opc)
1641       }
1642     sim_printf ("\r\n");
1643 
1644     sim_printf ("controller <--> device\r\n");
1645 
1646 #define CTLR_DEV(from_big,from_small, to_label, to_big, to_small)                                   \
1647     sim_printf ("  %-4s dev_code --> %-4s   command\r\n", #from_big, #to_label);                    \
1648     all (u, N_ ## from_big ## _UNITS_MAX)                                                           \
1649       all (prt, N_DEV_CODES)                                                                        \
1650         {                                                                                           \
1651           struct ctlr_to_dev_s * p = & cables->from_small ## _to_ ## to_small[u][prt];              \
1652           if (p->in_use)                                                                            \
1653             sim_printf (" %4u  %4u        %4u %10p\r\n", u, prt, p->unit_idx, (void *) p->iom_cmd); \
1654         }
1655 #define DEV_CTLR(from_big,from_small, to_label, to_big, to_small)               \
1656     sim_printf ("  %-4s --> %-4s dev_code type\r\n", #to_label, #from_big);     \
1657     all (u, N_ ## to_big ## _UNITS_MAX)                                         \
1658       {                                                                         \
1659         struct dev_to_ctlr_s * p = & cables->to_small ## _to_ ## from_small[u]; \
1660         if (p->in_use)                                                          \
1661           sim_printf (" %4u    %4u   %4u    %5s\r\n", u, p->ctlr_unit_idx,      \
1662                   p->dev_code, ctlr_type_strs[p->ctlr_type]);                   \
1663       }
1664     CTLR_DEV (MTP, mtp, TAPE, MT, tape);
1665     if (dump) //-V581
1666       {
1667         DEV_CTLR (MTP, mtp, TAPE, MT, tape);
1668       }
1669     CTLR_DEV (IPC, ipc, DISK, DSK, dsk);
1670     CTLR_DEV (MSP, msp, DISK, DSK, dsk);
1671     if (dump) //-V581
1672       {
1673         DEV_CTLR (CTLR, ctlr, DISK, DSK, dsk);
1674       }
1675     CTLR_DEV (URP, urp, URP, URP, urd);
1676     if (dump) //-V581
1677       {
1678         DEV_CTLR (URP, urp, RDR, RDR, rdr);
1679       }
1680     if (dump) //-V581
1681       {
1682         DEV_CTLR (URP, urp, PUN, PUN, pun);
1683       }
1684     if (dump) //-V581
1685       {
1686         DEV_CTLR (URP, urp, PRT, PRT, prt);
1687       }
1688 
1689     return SCPE_OK;
1690   }
1691 
1692 t_stat sys_cable_ripout (UNUSED int32 arg, UNUSED const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
1693   {
1694     cable_init ();
1695     scu_init ();
1696     return SCPE_OK;
1697   }
1698 
1699 void sysCableInit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1700   {
1701 
1702 
1703 
1704 
1705 
1706 
1707 
1708 
1709 
1710 
1711 
1712 
1713 
1714 
1715 
1716 
1717     cables = & system_state->cables;
1718 
1719     // Initialize data structures
1720     cable_init ();
1721   }

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