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

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