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

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