root/src/dps8/dps8_iom.c

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

DEFINITIONS

This source file includes following definitions.
  1. iom_core_read
  2. iom_core_read2
  3. iom_core_write
  4. iom_core_write2
  5. iom_core_read_lock
  6. iom_core_write_unlock
  7. iom_action
  8. iom_show_mbx
  9. iom_show_units
  10. iom_set_units
  11. iom_show_config
  12. iom_set_config
  13. iom_reset_unit
  14. iom_unit_reset_idx
  15. iom_reset
  16. init_memory_iom
  17. boot_svc
  18. iom_boot
  19. mbxLoc
  20. status_service
  21. build_AUXPTW_address
  22. build_DDSPTW_address
  23. fetch_DDSPTW
  24. build_IDSPTW_address
  25. fetch_IDSPTW
  26. build_LPWPTW_address
  27. fetch_LPWPTW
  28. iom_direct_data_service
  29. iom_indirect_data_service
  30. update_chan_mode
  31. write_LPW
  32. iom_advance_lpw
  33. iom_continue_channel
  34. dumpDCW
  35. dumpLPW
  36. fetch_and_parse_LPW
  37. unpack_DCW
  38. pack_DCW
  39. pack_LPW
  40. fetch_and_parse_PCW
  41. fetch_and_parse_DCW
  42. send_general_interrupt
  43. iom_fault
  44. iom_list_service
  45. doPayloadChannel
  46. doConnectChan
  47. send_marker_interrupt
  48. send_special_interrupt
  49. send_terminate_interrupt
  50. iom_interrupt
  51. chan_thread_main
  52. iom_thread_main
  53. iom_init
  54. do_boot
  55. iomProcess
  56. iomChar

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: 89f10936-f62e-11ec-b310-80ee73e9b8e7
   6  *
   7  * ---------------------------------------------------------------------------
   8  *
   9  * Copyright (c) 2007-2013 Michael Mondy
  10  * Copyright (c) 2012-2016 Harry Reed
  11  * Copyright (c) 2013-2022 Charles Anthony
  12  * Copyright (c) 2026 Eric Swenson
  13  * Copyright (c) 2026 Jeffrey H. Johnson
  14  * Copyright (c) 2021-2026 The DPS8M Development Team
  15  *
  16  * This software is made available under the terms of the ICU License.
  17  * See the LICENSE.md file at the top-level directory of this distribution.
  18  *
  19  * ---------------------------------------------------------------------------
  20  *
  21  * This source file may contain code comments that adapt, include, and/or
  22  * incorporate Multics program code and/or documentation distributed under
  23  * the Multics License.  In the event of any discrepancy between code
  24  * comments herein and the original Multics materials, the original Multics
  25  * materials should be considered authoritative unless otherwise noted.
  26  * For more details and historical background, see the LICENSE.md file at
  27  * the top-level directory of this distribution.
  28  *
  29  * ---------------------------------------------------------------------------
  30  */
  31 
  32 //
  33 // IOM rework # 7
  34 //
  35 
  36 // Implementation notes:
  37 //
  38 // The IOM can be configured to operate in GCOS, Extended GCOS, and Paged modes.
  39 // Only the Paged mode is supported.
  40 //
  41 // Backup list service not implemented
  42 // Wraparound channel not implemented
  43 // Snapshot channel not implemented
  44 // Scratchpad Access channel not implemented
  45 
  46 // Direct data service
  47 
  48 // Pg: B20 "Address Extension bits will be used for core selection when the
  49 //          "the IOM is [...] in Paged mode for a non-paged channel.
  50 
  51 // Definitions:
  52 //  unit number -- ambiguous: May indicate either the Multics
  53 //                 designation for IOMA, IOMB, etc. or the scp
  54 //                 UNIT table offset.
  55 //    unit_idx --  always refers to the scp unit table offset.
  56 //
  57 //
  58 
  59 // Functional layout:
  60 
  61 // CPU emulator:
  62 //   CIOC command -> SCU cable routing to IOM emulator "iom_connect"
  63 //
  64 // IOM emulator:
  65 //   iom_connect performs the connect channel operations of fetching
  66 //   the PCW, parsing out the channel number and initial DCW.
  67 //
  68 //   The channel number and initial DCW are passed to doPayloadChannel
  69 //
  70 //   Using the cable tables, doPayloadChannel determines the handler and
  71 //   unit_idx for that channel number and calls
  72 //     iom_unit[channel_number].iom_cmd (unit_idx, DCW)
  73 //   It then walks the channel DCW list, passing DCWs to the handler.
  74 //
  75 
  76 //        make_idcw IDCW_name,
  77 //                  Command,
  78 //                  Device,
  79 //                  {record,multirecord,character,nondata},
  80 //                  {proceed,terminate,marker},
  81 //                  [ChanData,]
  82 //                  [Extension,]
  83 //                  [mask,]
  84 //
  85 //   record      0
  86 //   nondata     2
  87 //   multirecord 6
  88 //   character   8
  89 //
  90 //   terminate   0
  91 //   proceed     2
  92 //   marker      3
  93 //
  94 // Observed channel command dialects
  95 //
  96 // IOM boot
  97 // CIOC 0
  98 // Connect channel LPW 000000040000 (case c)
  99 //   DCW pointer 000000
 100 //     RES              0
 101 //     REL              0
 102 //     AE               0
 103 //     NC 21            1
 104 //     TAL 22           0
 105 //     REL              0
 106 //     TALLY         0000
 107 //
 108 // PCW 000000720201 012000000000
 109 //   Chan info     0012
 110 //   Addr ext        00
 111 //   111              7
 112 //   M                0
 113 //   Chan info  22046447
 114 //   Pg Tbl Ptr  000000
 115 //   PTP              0
 116 //   PGE              0
 117 //   AUX              0
 118 //
 119 // MTP Tape channel LPW 000004020002 (case b)
 120 //   DCW pointer 000004
 121 //     RES              0
 122 //     REL              0
 123 //     AE               0
 124 //     NC 21            0
 125 //     TAL 22           1
 126 //     REL              0
 127 //     TALLY         0002
 128 //
 129 // DCW list dump:
 130 //   IDCW 000000720201
 131 //     cmd             00 Request Status
 132 //     dev code        00
 133 //     ctrl             2 (proceed)
 134 //     chancmd          2 (nondata)
 135 //   IDCW 050000700000
 136 //     cmd             05 Rd Bin Rcrd
 137 //     dev code        00
 138 //     ctrl             0 (terminate)
 139 //     chancmd          0 (record)
 140 //   IOTD (0) 000030000000
 141 //     tally       0000
 142 //     addr            30
 143 //     cp               0
 144 //   IOTD (0) 400000000000000000000
 145 //     tally       0000
 146 //     addr            00
 147 //     cp               0
 148 //   IOTD (0) 400000000000000000000
 149 //     tally       0000
 150 //     addr            00
 151 //     cp               0
 152 //
 153 // Tape commands done:
 154 //   Tape Request Status
 155 //   Tape Read Binary Record
 156 //   IOTD (0) 000030000000
 157 //     tally       0000
 158 //     addr            30
 159 //     cp               0
 160 //   Tape IOT Read
 161 //     Tape 0 reads record 1
 162 //   ctrl == 0 in chan 10 (12) DCW
 163 
 164 // Bootload check tape controller firmware loaded test
 165 //
 166 // CIOC 138
 167 // Connect channel LPW 000000040000 (case c)
 168 //   DCW pointer 000000
 169 //     RES              0
 170 //     REL              0
 171 //     AE               0
 172 //     NC 21            1
 173 //     TAL 22           0
 174 //     REL              0
 175 //     TALLY         0000
 176 //
 177 // PCW 000000720201 012000000000
 178 //   Chan info     0012
 179 //   Addr ext        00
 180 //   111              7
 181 //   M                0
 182 //   Chan info    22046447
 183 //   Pg Tbl Ptr  000000
 184 //   PTP              0
 185 //   PGE              0
 186 //   AUX              0
 187 //
 188 // MTP Tape channel LPW 000540007777 (case a)
 189 //   DCW pointer 000540
 190 //     RES              0
 191 //     REL              0
 192 //     AE               0
 193 //     NC 21            0
 194 //     TAL 22           0
 195 //     REL              0
 196 //     TALLY         7777
 197 //
 198 // DCW list dump:
 199 //   IDCW 000000720201
 200 //     cmd             00 Request Status
 201 //     dev code        00
 202 //     ctrl             2 (proceed)
 203 //     chancmd          2 (nondata)
 204 //   IDCW 510000700200
 205 //     cmd             51 Reset Dev Stat/Alert
 206 //     dev code        00
 207 //     ctrl             0 (terminate)
 208 //     chancmd          2 (nondata)
 209 //   IDCW 000000700200
 210 //     cmd             00 Request Status
 211 //     dev code        00
 212 //     ctrl             0 (terminate)
 213 //     chancmd          2 (nondata)
 214 //   IOTD (0) 200000000000
 215 //     tally       0000
 216 //     addr            200000
 217 //     cp               0
 218 //
 219 // Tape commands done:
 220 //   Tape Request Status
 221 //   Tape Reset Device Status
 222 //   ctrl == 0 in chan 10 (12) DCW
 223 
 224 // Bootload tape read
 225 //
 226 // CIOC 7999826
 227 // Connect channel LPW 000000040000 (case c)
 228 //   DCW pointer 000000
 229 //     RES              0
 230 //     REL              0
 231 //     AE               0
 232 //     NC 21            1
 233 //     TAL 22           0
 234 //     REL              0
 235 //     TALLY         0000
 236 //
 237 // PCW 000000720201 012000000000
 238 //   Chan info     0012
 239 //   Addr ext        00
 240 //   111              7
 241 //   M                0
 242 //   Chan info    22046447
 243 //   Pg Tbl Ptr  000000
 244 //   PTP              0
 245 //   PGE              0
 246 //   AUX              0
 247 //
 248 // MTP Tape channel LPW 000621007777 (case a)
 249 //   DCW pointer 000621
 250 //     RES              0
 251 //     REL              0
 252 //     AE               0
 253 //     NC 21            0
 254 //     TAL 22           0
 255 //     REL              0
 256 //     TALLY         7777
 257 //
 258 // DCW list dump:
 259 //   IDCW 000000720201
 260 //     cmd             00 Request Status
 261 //     dev code        00
 262 //     ctrl             2 (proceed)
 263 //     chancmd          2 (nondata)
 264 //   IDCW 050000700000
 265 //     cmd             05 Rd Bin Rcrd
 266 //     dev code        00
 267 //     ctrl             0 (terminate)
 268 //     chancmd          0 (record)
 269 //   IOTD (0) 060001002020
 270 //     tally       2020
 271 //     addr            60001
 272 //     cp               0
 273 //   IOTD (0) 024000000000
 274 //     tally       0000
 275 //     addr            24000
 276 //     cp               0
 277 //
 278 // Tape commands done:
 279 //   Tape Request Status
 280 //   Tape Read Binary Record
 281 //   IOTD (0) 060001002020
 282 //     tally       2020
 283 //     addr            60001
 284 //     cp               0
 285 //   Tape IOT Read
 286 //   ctrl == 0 in chan 10 (12) DCW
 287 
 288 // Bootload searching for console
 289 //
 290 // CIOC 8191828
 291 // Connect channel LPW 037312020000 (case b)
 292 //   DCW pointer 037312
 293 //     RES              0
 294 //     REL              0
 295 //     AE               0
 296 //     NC 21            0
 297 //     TAL 22           1
 298 //     REL              0
 299 //     TALLY         0000
 300 //
 301 // PCW 510000700200 010000000000
 302 //   Chan info     0010
 303 //   Addr ext        00
 304 //   111              7
 305 //   M                0
 306 //   Chan info 22046447
 307 //   Pg Tbl Ptr  000000
 308 //   PTP              0
 309 //   PGE              0
 310 //   AUX              0
 311 //
 312 
 313 // Bootload finding console
 314 //
 315 // CIOC 8324312
 316 // Connect channel LPW 037312020000 (case b)
 317 //   DCW pointer 037312
 318 //     RES              0
 319 //     REL              0
 320 //     AE               0
 321 //     NC 21            0
 322 //     TAL 22           1
 323 //     REL              0
 324 //     TALLY         0000
 325 //
 326 // PCW 510000700200 036000000000
 327 //   Chan info     0036
 328 //   Addr ext        00
 329 //   111              7
 330 //   M                0
 331 //   Chan info 22046447
 332 //   Pg Tbl Ptr  000000
 333 //   PTP              0
 334 //   PGE              0
 335 //   AUX              0
 336 //
 337 // CONSOLE: ALERT
 338 
 339 // Bootload console read ID
 340 //
 341 // Connect channel LPW 037312020000 (case b)
 342 //   DCW pointer 037312
 343 //     RES              0
 344 //     REL              0
 345 //     AE               0
 346 //     NC 21            0
 347 //     TAL 22           1
 348 //     REL              0
 349 //     TALLY         0000
 350 //
 351 // PCW 570000700200 036000000000
 352 //   Chan info     0036
 353 //   Addr ext        00
 354 //   111              7
 355 //   M                0
 356 //   Chan info 22046447
 357 //   Pg Tbl Ptr  000000
 358 //   PTP              0
 359 //   PGE              0
 360 //   AUX              0
 361 //
 362 
 363 // Bootload console write ASCII
 364 //
 365 // Connect channel LPW 037312020000 (case b)
 366 //   DCW pointer 037312
 367 //     RES              0
 368 //     REL              0
 369 //     AE               0
 370 //     NC 21            0
 371 //     TAL 22           1
 372 //     REL              0
 373 //     TALLY         0000
 374 //
 375 // PCW 330000700000 036000000000
 376 //   Chan info     0036
 377 //   Addr ext        00
 378 //   111              7
 379 //   M                0
 380 //   Chan info 22046447
 381 //   Pg Tbl Ptr  000000
 382 //   PTP              0
 383 //   PGE              0
 384 //   AUX              0
 385 //
 386 // IOTD (0) 033624000022
 387 //   tally       0022
 388 //   addr            33624
 389 //   cp               0
 390 // Console channel LPW 033357007777 (case a)
 391 //   DCW pointer 033357
 392 //     RES              0
 393 //     REL              0
 394 //     AE               0
 395 //     NC 21            0
 396 //     TAL 22           0
 397 //     REL              0
 398 //     TALLY         7777
 399 //
 400 // DCW list dump:
 401 //   IDCW 330000700000
 402 //     cmd             33 Write ASCII
 403 //     dev code        00
 404 //     ctrl             0 (terminate)
 405 //     chancmd          0 (record)
 406 //   IOTD (0) 033624000022
 407 //     tally       0022
 408 //     addr            33624
 409 //     cp               0
 410 //   IDCW 510000700000
 411 //     cmd             51 Reset Dev Stat/Alert
 412 //     dev code        00
 413 //     ctrl             0 (terminate)
 414 //     chancmd          0 (record)
 415 
 416 //
 417 //  T&D Read block 3
 418 //    Connect Channel LPW 006312040001 (case c)
 419 //      DCW pointer 006312
 420 //      RES              0
 421 //      REL              0
 422 //      AE               0
 423 //      NC 21            1
 424 //      TAL 22           0
 425 //      REL              0
 426 //      TALLY         0001
 427 //    PCW 050000700001 012000000000
 428 //      Chan info     0500
 429 //      Addr ext        00
 430 //      111              7
 431 //      M                0
 432 //      Chan info    00001
 433 //      Chan            12
 434 //      Pg Tbl Ptr  000000
 435 //      PTP              0
 436 //      PGE              0
 437 //      AUX              0
 438 //    IDCW 050000700001
 439 //      cmd             05 (Read Binary Record)
 440 //      dev             00
 441 //      ctrl             0
 442 //      chancmd          0
 443 //
 444 //    Payload channel
 445 //      IOTD 010000000000
 446 //        tally       0000
 447 //        addr      010000
 448 //
 449 
 450 // Table 3.2.1 Updating of LPW address and tally and indication of PTRO and TRO
 451 //
 452 //  case   LPW 21   LPW 22  TALLY  UPDATE   INDICATE    INDICATE
 453 //         NC       TALLY   VALUE  ADDR &   PTRO TO     TRO
 454 //                                 TALLY    CONN. CH.   FAULT
 455 //    a.     0       0       any    yes       no         no
 456 //    b.     0       1       > 2    yes       no         no
 457 //                            1     yes       yes        no
 458 //                            0     no        no         yes
 459 //    c.     1       x       any    no        yes        no
 460 //
 461 // Case a. is GCOS common Peripheral Interface payloads channels;
 462 // a tally of 0 is not a fault since the tally field will not be
 463 // used for that purpose.
 464 //
 465 // Case b. is the configuration expected of standard operation with real-time
 466 // applications and/or direct data channels.
 467 //
 468 // Case c. will be used normally only with the connect channel servicing CPI
 469 // type payload channels. [CAC: "INDICATE PTRO TO CONN. CH." means that the
 470 // connect channel should not call list service to obtain additional PCW's]
 471 //
 472 // If case a. is used with the connect channels, a "System Fault: Tally
 473 // Control Error, Connect Channel" will be generated. Case c. will be
 474 // used in the bootload program.
 475 
 476 // LPW bits
 477 //
 478 //  0-17  DCW pointer
 479 //  18    RES - Restrict IDCW's [and] changing AE flag
 480 //  19    REL - IOM controlled image of REL bit
 481 //  20    AE - Address extension flag
 482 //  21    NC
 483 //  22    TAL
 484 //  23    REL
 485 //  24-35 TALLY
 486 //
 487 
 488 // LPWX (sometimes called LPWE)
 489 //
 490 //  0-8   Lower bound
 491 //  9-17  Size
 492 //  18-35 Pointer to DCW of most recent instruction
 493 //
 494 
 495 // PCW bits
 496 //
 497 // word 1
 498 //   0-11 Channel information
 499 //  12-17 Address extension
 500 //  18-20 111
 501 //  21    M
 502 //  22-35 Channel Information
 503 //
 504 // word 2
 505 //  36-38 SBZ
 506 //  39-44 Channel Number
 507 //  45-62 Page table ptr
 508 //  63    PTP
 509 //  64    PGE
 510 //  65    AUX
 511 //  66-21 not used
 512 
 513 // IDCW
 514 //
 515 //   0-11 Channel information
 516 //  12-17 Address extension
 517 //  18-20 111
 518 //  21    EC
 519 //  22-35 Channel Information
 520 //
 521 
 522 // TDCW
 523 //
 524 // Paged, PCW 64 = 0
 525 //
 526 //   0-17 DCW pointer
 527 //  18-20 Not 111
 528 //  21    0
 529 //  22    1
 530 //  23    0
 531 //  24-32 SBZ
 532 //  33    EC
 533 //  34    RES
 534 //  35    REL
 535 //
 536 // Paged, PCW 64 = 1
 537 //
 538 //   0-17 DCW pointer
 539 //  18-20 Not 111
 540 //  21    0
 541 //  22    1
 542 //  23    0
 543 //  24-30 SBZ
 544 //  31    SEG
 545 //  32    PDTA
 546 //  33    PDCW
 547 //  34    RES
 548 //  35    REL
 549 //
 550 
 551 #include "dps8.h"
 552 #include "dps8_cpu.h"
 553 #include "dps8_rt.h"
 554 #include "dps8_priv.h"
 555 #include "dps8_sys.h"
 556 #include "dps8_faults.h"
 557 #include "dps8_scu.h"
 558 #include "dps8_iom.h"
 559 #include "dps8_cable.h"
 560 #include "dps8_console.h"
 561 #include "dps8_fnp2.h"
 562 #include "dps8_utils.h"
 563 #if defined(LOCKLESS)
 564 # include "threadz.h"
 565 #endif
 566 
 567 #define DBG_CTR 1
 568 
 569 // Nomenclature
 570 //
 571 //  IDX index   refers to emulator unit
 572 //  NUM         refers to the number that the unit is configured as ("IOMA,
 573 //              IOMB,..."). Encoded in the low to bits of configSwMultiplexBaseAddress
 574 
 575 // Default
 576 #define N_IOM_UNITS 1
 577 
 578 #define IOM_UNIT_IDX(uptr) ((uptr) - iom_unit)
 579 
 580 #define IOM_SYSTEM_FAULT_CHAN 1U
 581 #define IOM_CONNECT_CHAN 2U
 582 #define IOM_SPECIAL_STATUS_CHAN 6U
 583 
 584 iom_chan_data_t iom_chan_data[N_IOM_UNITS_MAX][MAX_CHANNELS];
 585 
 586 typedef enum iom_status_t
 587   {
 588     iomStatNormal         = 0,
 589     iomStatLPWTRO         = 1,
 590     iomStat2TDCW          = 2,
 591     iomStatBoundaryError  = 3,
 592     iomStatAERestricted   = 4,
 593     iomStatIDCWRestricted = 5,
 594     iomStatCPDiscrepancy  = 6,
 595     iomStatParityErr      = 7
 596   } iom_status_t;
 597 
 598 enum config_sw_OS_t
 599   {
 600     CONFIG_SW_STD_GCOS,
 601     CONFIG_SW_EXT_GCOS,
 602     CONFIG_SW_MULTICS  // "Paged"
 603   };
 604 
 605 enum config_sw_model_t
 606   {
 607     CONFIG_SW_MODEL_IOM,
 608     CONFIG_SW_MODEL_IMU
 609   };
 610 
 611 // Boot device: CARD/TAPE;
 612 enum config_sw_bootload_device_e { CONFIG_SW_BLCT_CARD, CONFIG_SW_BLCT_TAPE };
 613 
 614 typedef struct
 615   {
 616     // Configuration switches
 617 
 618     // Interrupt multiplex base address: 12 toggles
 619     word12 configSwIomBaseAddress;
 620 
 621     // Mailbox base aka IOM base address: 9 toggles
 622     // Note: The IOM number is encoded in the lower two bits
 623 
 624     // AM81, pg 60 shows an image of a Level 68 IOM configuration panel
 625     // The switches are arranged and labeled
 626     //
 627     //  12   13   14   15   16   17   18   --   --  --  IOM
 628     //                                                  NUMBER
 629     //   X    X    X    X    X    X    X                X     X
 630     //
 631 
 632     word9 configSwMultiplexBaseAddress;
 633 
 634     enum config_sw_model_t config_sw_model; // IOM or IMU
 635 
 636     // OS: Three position switch: GCOS, EXT GCOS, Multics
 637     enum config_sw_OS_t config_sw_OS; // = CONFIG_SW_MULTICS;
 638 
 639     // Bootload device: Toggle switch CARD/TAPE
 640     enum config_sw_bootload_device_e configSwBootloadCardTape; // = CONFIG_SW_BLCT_TAPE;
 641 
 642     // Bootload tape IOM channel: 6 toggles
 643     word6 configSwBootloadMagtapeChan; // = 0;
 644 
 645     // Bootload cardreader IOM channel: 6 toggles
 646     word6 configSwBootloadCardrdrChan; // = 1;
 647 
 648     // Bootload: pushbutton
 649 
 650     // Sysinit: pushbutton
 651 
 652     // Bootload SCU port: 3 toggle AKA "ZERO BASE S.C. PORT NO"
 653     // "the port number of the SC through which which connects are to
 654     // be sent to the IOM
 655     word3 configSwBootloadPort; // = 0;
 656 
 657     // 8 Ports: CPU/IOM connectivity
 658     // Port configuration: 3 toggles/port
 659     // Which SCU number is this port attached to
 660     uint configSwPortAddress[N_IOM_PORTS]; // = { 0, 1, 2, 3, 4, 5, 6, 7 };
 661 
 662     // Port interlace: 1 toggle/port
 663     uint configSwPortInterface[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 664 
 665     // Port enable: 1 toggle/port
 666     uint configSwPortEnable[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 667 
 668     // Port system initialize enable: 1 toggle/port // XXX What is this
 669     uint configSwPortSysinitEnable[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 670 
 671     // Port half-size: 1 toggle/port // XXX what is this
 672     uint configSwPortHalfsize[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 673     // Port store size: 1 8 pos. rotary/port
 674     uint configSwPortStoresize[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 675 
 676     // other switches:
 677     //   alarm disable
 678     //   test/normal
 679     iom_status_t iomStatus;
 680 
 681     uint invokingScuUnitIdx; // the unit number of the SCU that did the connect.
 682 
 683     coreLockState_t  iomCoreLockState;
 684 
 685   } iom_unit_data_t;
 686 
 687 static iom_unit_data_t iom_unit_data[N_IOM_UNITS_MAX];
 688 
 689 typedef enum iomSysFaults_t
 690   {
 691     // List from 4.5.1; descr from AN87, 3-9
 692     iomNoFlt         = 000,
 693     iomIllChanFlt    = 001, // PCW to chan with chan number >= 40
 694                             // or channel without scratchpad installed
 695     iomIllSrvReqFlt  = 002, // A channel requested a service request code
 696                             // of zero, a channel number of zero, or
 697                             // a channel number >= 40
 698     // =003,                // Parity error scratchpad
 699     iomBndryVioFlt   = 003, // pg B36
 700     iom256KFlt       = 004, // 256K overflow -- address decremented to zero,
 701                             // but not tally
 702     iomLPWTRPConnFlt = 005, // tally was zero for an update LPW (LPW bit
 703                             // 21==0) when the LPW was fetched for the
 704                             // connect channel
 705     iomNotPCWFlt     = 006, // DCW for conn channel had bits 18..20 != 111b
 706     iomCP1Flt        = 007, // DCW was TDCW or had bits 18..20 == 111b
 707     // = 010,               // CP/CS-BAD-DATA DCW fetch for a 9 bit channel
 708                             // contained an illegal character position
 709     // = 011,               // NO-MEM-RES No response to an interrupt from
 710                             // a system controller within 16.5 usec.
 711     // = 012,               // PRTY-ERR-MEM Parity error on the read
 712                             // data when accessing a system controller.
 713     iomIllTalCChnFlt = 013, // LPW bits 21-22 == 00 when LPW was fetched
 714                             // for the connect channel
 715     // = 014,               // PTP-Fault: PTP-Flag= zero or PTP address
 716                             // overflow.
 717     iomPTWFlagFault  = 015, // PTW-Flag-Fault: Page Present flag zero, or
 718                             // Write Control Bit 0, or Housekeeping bit set,
 719     // = 016,               // ILL-LPW-STD LPW had bit 20 on in GCOS mode
 720     iomNoPortFault   = 017, // NO-PRT-SEL No port selected during attempt
 721                             // to access memory.
 722   } iomSysFaults_t;
 723 
 724 typedef enum iomFaultServiceRequest
 725   {
 726     // Combined SR/M/D
 727     iomFsrFirstList =  (1 << 2) | 2,
 728     iomFsrList =       (1 << 2) | 0,
 729     iomFsrStatus =     (2 << 2) | 0,
 730     iomFsrIntr =       (3 << 2) | 0,
 731     iomFsrSPIndLoad =  (4 << 2) | 0,
 732     iomFsrDPIndLoad =  (4 << 2) | 1,
 733     iomFsrSPIndStore = (5 << 2) | 0,
 734     iomFsrDPIndStore = (5 << 2) | 1,
 735     iomFsrSPDirLoad =  (6 << 2) | 0,
 736     iomFsrDPDirLoad =  (6 << 2) | 1,
 737     iomFsrSPDirStore = (7 << 2) | 0,
 738     iomFsrDPDirStore = (7 << 2) | 1
 739   } iomFaultServiceRequest;
 740 
 741 #if defined(IO_THREADZ)
 742 __thread uint this_iom_idx;
 743 __thread uint this_chan_num;
 744 #endif
 745 
 746 #if defined(TESTING)
 747 static char * cmdNames [] =
 748   {
 749     "Request Status",        //  0
 750     "",                      //  1
 751     "Rd Ctrlr Mem",          //  2
 752     "Rd 9 Rcrd",             //  3
 753     "",                      //  4
 754     "Rd Bin Rcrd",           //  5
 755     "Initiate Rd Data Xfer", //  6
 756     "",                      //  7
 757     "",                      // 10
 758     "",                      // 11
 759     "",                      // 12
 760     "Wr 9 Rcrd",             // 13
 761     "",                      // 14
 762     "Wr Bin Rcrd",           // 15
 763     "Initiate Wr Data Xfer", // 16
 764     "",                      // 17
 765     "Release Ctlr",          // 20
 766     "",                      // 21
 767     "RdStatReg",             // 22
 768     "",                      // 23
 769     "",                      // 24
 770     "Read",                  // 25
 771     "",                      // 26
 772     "",                      // 27
 773     "Seek 512",              // 30
 774     "",                      // 31
 775     "MTP Wr Mem",            // 32
 776     "Write ASCII",           // 33
 777     "",                      // 34
 778     "",                      // 35
 779     "",                      // 36
 780     "",                      // 37
 781     "Reset Status",          // 40
 782     "Set 6250 CPI",          // 41
 783     "Restore",               // 42
 784     "",                      // 43
 785     "Forward Skip Rcrd",     // 44
 786     "Forward Skip File",     // 45
 787     "Backward Skip Rcrd",    // 46
 788     "Backspace File",        // 47
 789     "Request Status",        // 50
 790     "Reset Dev Stat/Alert",  // 51
 791     "",                      // 52
 792     "",                      // 53
 793     "",                      // 54
 794     "Write EOF",             // 55
 795     "",                      // 56
 796     "Survey Devs/ReadID",    // 57
 797     "Set 800 BPI",           // 60
 798     "Set 556 BPI",           // 61
 799     "",                      // 62
 800     "Set File Permit",       // 63
 801     "Set 200 BPI",           // 64
 802     "Set 1600 CPI",          // 65
 803     "",                      // 66
 804     "",                      // 67
 805     "Rewind",                // 70
 806     "",                      // 71
 807     "Rewind/Unload",         // 72
 808     "",                      // 73
 809     "",                      // 74
 810     "",                      // 75
 811     "",                      // 76
 812     "",                      // 77
 813   };
 814 #endif
 815 
 816 void iom_core_read (UNUSED uint iom_unit_idx, word24 addr, word36 *data, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 817   {
 818 #if defined(LOCKLESS)
 819 # if !defined(SUNLINT)
 820     word36 v;
 821     LOAD_ACQ_CORE_WORD(v, addr);
 822     * data = v & DMASK;
 823 # endif /* if !defined(SUNLINT) */
 824 #else
 825     * data = M[addr] & DMASK;
 826 #endif
 827   }
 828 
 829 void iom_core_read2 (UNUSED uint iom_unit_idx, word24 addr, word36 *even, word36 *odd, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 830   {
 831 #if defined(LOCKLESS)
 832 # if !defined(SUNLINT)
 833     word36 v;
 834     LOAD_ACQ_CORE_WORD(v, addr);
 835     * even = v & DMASK;
 836 # endif /* if !defined(SUNLINT) */
 837     addr++;
 838 # if !defined(SUNLINT)
 839     LOAD_ACQ_CORE_WORD(v, addr);
 840     * odd = v & DMASK;
 841 # endif /* if !defined(SUNLINT) */
 842 #else
 843     * even = M[addr ++] & DMASK;
 844     * odd =  M[addr]    & DMASK;
 845 #endif
 846   }
 847 
 848 void iom_core_write (uint iom_unit_idx, word24 addr, word36 data, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 849   {
 850 #if defined(LOCKLESS)
 851     LOCK_CORE_WORD(addr, & iom_unit_data[iom_unit_idx].iomCoreLockState);
 852 # if !defined(SUNLINT)
 853     STORE_REL_CORE_WORD(addr, data);
 854 # endif /* if !defined(SUNLINT) */
 855 #else
 856     M[addr] = data & DMASK;
 857 #endif
 858   }
 859 
 860 void iom_core_write2 (UNUSED uint iom_unit_idx, word24 addr, word36 even, word36 odd, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 861   {
 862 #if defined(LOCKLESS)
 863     LOCK_CORE_WORD(addr, & iom_unit_data[iom_unit_idx].iomCoreLockState);
 864 # if !defined(SUNLINT)
 865     STORE_REL_CORE_WORD(addr, even);
 866 # endif /* if !defined(SUNLINT) */
 867     addr++;
 868     LOCK_CORE_WORD(addr, & iom_unit_data[iom_unit_idx].iomCoreLockState);
 869 # if !defined(SUNLINT)
 870     STORE_REL_CORE_WORD(addr, odd);
 871 # endif /* if !defined(SUNLINT) */
 872 #else
 873     M[addr ++] = even;
 874     M[addr] =    odd;
 875 #endif
 876   }
 877 
 878 void iom_core_read_lock (UNUSED uint iom_unit_idx, word24 addr, word36 *data, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 879   {
 880 #if defined(LOCKLESS)
 881     LOCK_CORE_WORD(addr, & iom_unit_data[iom_unit_idx].iomCoreLockState);
 882 # if !defined(SUNLINT)
 883     word36 v;
 884     LOAD_ACQ_CORE_WORD(v, addr);
 885     * data = v & DMASK;
 886 # endif /* if !defined(SUNLINT) */
 887 #else
 888     * data = M[addr] & DMASK;
 889 #endif
 890   }
 891 
 892 void iom_core_write_unlock (UNUSED uint iom_unit_idx, word24 addr, word36 data, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 893   {
 894 #if defined(LOCKLESS)
 895 # if !defined(SUNLINT)
 896     STORE_REL_CORE_WORD(addr, data);
 897 # endif /* if !defined(SUNLINT) */
 898 #else
 899     M[addr] = data & DMASK;
 900 #endif
 901   }
 902 
 903 static t_stat iom_action (UNIT *up)
     /* [previous][next][first][last][top][bottom][index][help] */
 904   {
 905     // Recover the stash parameters
 906     uint scu_unit_idx = (uint) (up -> u3);
 907     uint iom_unit_idx = (uint) (up -> u4);
 908     iom_interrupt (scu_unit_idx, iom_unit_idx);
 909     return SCPE_OK;
 910   }
 911 
 912 static UNIT iom_unit[N_IOM_UNITS_MAX] = {
 913 #if defined(NO_C_ELLIPSIS)
 914   { UDATA (iom_action, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 915   { UDATA (iom_action, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 916   { UDATA (iom_action, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 917   { UDATA (iom_action, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 918 #else
 919   [0 ... N_IOM_UNITS_MAX - 1] = {
 920     UDATA (iom_action, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 921   }
 922 #endif
 923 };
 924 
 925 static t_stat iom_show_mbx (UNUSED FILE * st,
     /* [previous][next][first][last][top][bottom][index][help] */
 926                             UNUSED UNIT * uptr, UNUSED int val,
 927                             UNUSED const void * desc)
 928   {
 929     return SCPE_OK;
 930   }
 931 
 932 static t_stat iom_show_units (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val, UNUSED const void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 933   {
 934     sim_printf ("Number of IOM units in system is %d\r\n", iom_dev.numunits);
 935     return SCPE_OK;
 936   }
 937 
 938 static t_stat iom_set_units (UNUSED UNIT * uptr, UNUSED int value, const char * cptr, UNUSED void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
 939   {
 940     if (! cptr)
 941       return SCPE_ARG;
 942     int n = atoi (cptr);
 943     if (n < 1 || n > N_IOM_UNITS_MAX)
 944       return SCPE_ARG;
 945     // This was true for early Multics releases.
 946     //if (n > 2)
 947       //sim_printf ("Warning: Multics supports 2 IOMs maximum\r\n");
 948     iom_dev.numunits = (unsigned) n;
 949     return SCPE_OK;
 950   }
 951 
 952 static t_stat iom_show_config (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
 953                              UNUSED const void * desc)
 954   {
 955     uint iom_unit_idx = (uint) IOM_UNIT_IDX (uptr);
 956     if (iom_unit_idx >= iom_dev.numunits)
 957       {
 958         sim_printf ("error: Invalid unit number %lu\r\n", (unsigned long) iom_unit_idx);
 959         return SCPE_ARG;
 960       }
 961 
 962     sim_printf ("IOM unit number %lu\r\n", (unsigned long) iom_unit_idx);
 963     iom_unit_data_t * p = iom_unit_data + iom_unit_idx;
 964 
 965     char * os = "<out of range>";
 966     switch (p -> config_sw_OS)
 967       {
 968         case CONFIG_SW_STD_GCOS:
 969           os = "Standard GCOS";
 970           break;
 971         case CONFIG_SW_EXT_GCOS:
 972           os = "Extended GCOS";
 973           break;
 974         case CONFIG_SW_MULTICS:
 975           os = "Multics";
 976           break;
 977       }
 978     char * blct = "<out of range>";
 979     switch (p -> configSwBootloadCardTape)
 980       {
 981         case CONFIG_SW_BLCT_CARD:
 982           blct = "CARD";
 983           break;
 984         case CONFIG_SW_BLCT_TAPE:
 985           blct = "TAPE";
 986           break;
 987       }
 988 
 989     sim_printf ("Allowed Operating System: %s\r\n", os);
 990     sim_printf ("IOM Base Address:         %03o(8)\r\n", p -> configSwIomBaseAddress);
 991     sim_printf ("Multiplex Base Address:   %04o(8)\r\n", p -> configSwMultiplexBaseAddress);
 992     sim_printf ("Bootload Card/Tape:       %s\r\n", blct);
 993     sim_printf ("Bootload Tape Channel:    %02o(8)\r\n", p -> configSwBootloadMagtapeChan);
 994     sim_printf ("Bootload Card Channel:    %02o(8)\r\n", p -> configSwBootloadCardrdrChan);
 995     sim_printf ("Bootload Port:            %02o(8)\r\n", p -> configSwBootloadPort);
 996     sim_printf ("Port Address:            ");
 997     int i;
 998     for (i = 0; i < N_IOM_PORTS; i ++)
 999       sim_printf (" %03o", p -> configSwPortAddress[i]);
1000     sim_printf ("\r\n");
1001     sim_printf ("Port Interlace:          ");
1002     for (i = 0; i < N_IOM_PORTS; i ++)
1003       sim_printf (" %3o", p -> configSwPortInterface[i]);
1004     sim_printf ("\r\n");
1005     sim_printf ("Port Enable:             ");
1006     for (i = 0; i < N_IOM_PORTS; i ++)
1007       sim_printf (" %3o", p -> configSwPortEnable[i]);
1008     sim_printf ("\r\n");
1009     sim_printf ("Port Sysinit Enable:     ");
1010     for (i = 0; i < N_IOM_PORTS; i ++)
1011       sim_printf (" %3o", p -> configSwPortSysinitEnable[i]);
1012     sim_printf ("\r\n");
1013     sim_printf ("Port Halfsize:           ");
1014     for (i = 0; i < N_IOM_PORTS; i ++)
1015       sim_printf (" %3o", p -> configSwPortHalfsize[i]);
1016     sim_printf ("\r\n");
1017     sim_printf ("Port Storesize:          ");
1018     for (i = 0; i < N_IOM_PORTS; i ++)
1019       sim_printf (" %3o", p -> configSwPortStoresize[i]);
1020     sim_printf ("\r\n");
1021 
1022     return SCPE_OK;
1023   }
1024 
1025 //
1026 // set iom0 config=<blah> [;<blah>]
1027 //
1028 //    blah = iom_base=n
1029 //           multiplex_base=n
1030 //           os=gcos | gcosext | multics
1031 //           boot=card | tape
1032 //           tapechan=n
1033 //           cardchan=n
1034 //           scuport=n
1035 //           port=n   // set port number for below commands
1036 //             addr=n
1037 //             interlace=n
1038 //             enable=n
1039 //             initenable=n
1040 //             halfsize=n
1041 //             storesize=n
1042 //          bootskip=n // Hack: forward skip n records after reading boot record
1043 
1044 static config_value_list_t cfg_model_list[] =
1045   {
1046     { "iom", CONFIG_SW_MODEL_IOM },
1047     { "imu", CONFIG_SW_MODEL_IMU }
1048   };
1049 
1050 static config_value_list_t cfg_os_list[] =
1051   {
1052     { "gcos",    CONFIG_SW_STD_GCOS },
1053     { "gcosext", CONFIG_SW_EXT_GCOS },
1054     { "multics", CONFIG_SW_MULTICS  },
1055     { NULL,      0 }
1056   };
1057 
1058 static config_value_list_t cfg_boot_list[] =
1059   {
1060     { "card", CONFIG_SW_BLCT_CARD },
1061     { "tape", CONFIG_SW_BLCT_TAPE },
1062     { NULL,   0 }
1063   };
1064 
1065 static config_value_list_t cfg_base_list[] =
1066   {
1067     { "multics",  014 },
1068     { "multics1", 014 }, // boot iom
1069     { "multics2", 020 },
1070     { "multics3", 024 },
1071     { "multics4", 030 },
1072     { NULL, 0 }
1073   };
1074 
1075 static config_value_list_t cfg_size_list[] =
1076   {
1077     { "32",    0 },
1078     { "64",    1 },
1079     { "128",   2 },
1080     { "256",   3 },
1081     { "512",   4 },
1082     { "1024",  5 },
1083     { "2048",  6 },
1084     { "4096",  7 },
1085     { "32K",   0 },
1086     { "64K",   1 },
1087     { "128K",  2 },
1088     { "256K",  3 },
1089     { "512K",  4 },
1090     { "1024K", 5 },
1091     { "2048K", 6 },
1092     { "4096K", 7 },
1093     { "1M",    5 },
1094     { "2M",    6 },
1095     { "4M",    7 },
1096     { NULL,    0 }
1097   };
1098 
1099 static config_list_t iom_config_list[] =
1100   {
1101     { "model",          1, 0,               cfg_model_list },
1102     { "os",             1, 0,               cfg_os_list    },
1103     { "boot",           1, 0,               cfg_boot_list  },
1104     { "iom_base",       0, 07777,           cfg_base_list  },
1105     { "multiplex_base", 0, 0777,            NULL           },
1106     { "tapechan",       0, 077,             NULL           },
1107     { "cardchan",       0, 077,             NULL           },
1108     { "scuport",        0, 07,              NULL           },
1109     { "port",           0, N_IOM_PORTS - 1, NULL           },
1110     { "addr",           0, 7,               NULL           },
1111     { "interlace",      0, 1,               NULL           },
1112     { "enable",         0, 1,               NULL           },
1113     { "initenable",     0, 1,               NULL           },
1114     { "halfsize",       0, 1,               NULL           },
1115     { "store_size",     0, 7,               cfg_size_list  },
1116     { NULL,             0, 0,               NULL           }
1117   };
1118 
1119 static t_stat iom_set_config (UNIT * uptr, UNUSED int value, const char * cptr, UNUSED void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
1120   {
1121     uint iom_unit_idx = (uint) IOM_UNIT_IDX (uptr);
1122     if (iom_unit_idx >= iom_dev.numunits)
1123       {
1124         sim_printf ("error: %s: Invalid unit number %ld\r\n", __func__, (long) iom_unit_idx);
1125         return SCPE_ARG;
1126       }
1127 
1128     iom_unit_data_t * p = iom_unit_data + iom_unit_idx;
1129 
1130     static uint port_num = 0;
1131 
1132     config_state_t cfg_state = { NULL, NULL };
1133 
1134     for (;;)
1135       {
1136         int64_t v;
1137         int rc = cfg_parse (__func__, cptr, iom_config_list, & cfg_state, & v);
1138 
1139         if (rc == -1) // done
1140           break;
1141 
1142         if (rc == -2) // error
1143           {
1144             cfg_parse_done (& cfg_state);
1145             continue;
1146           }
1147         const char * name = iom_config_list[rc].name;
1148 
1149         if (strcmp (name, "model") == 0)
1150           {
1151             p -> config_sw_model = (enum config_sw_model_t) v;
1152             continue;
1153           }
1154 
1155         if (strcmp (name, "os") == 0)
1156           {
1157             p -> config_sw_OS = (enum config_sw_OS_t) v;
1158             continue;
1159           }
1160 
1161         if (strcmp (name, "boot") == 0)
1162           {
1163             p -> configSwBootloadCardTape = (enum config_sw_bootload_device_e) v;
1164             continue;
1165           }
1166 
1167         if (strcmp (name, "iom_base") == 0)
1168           {
1169             p -> configSwIomBaseAddress = (word12) v;
1170             continue;
1171           }
1172 
1173         if (strcmp (name, "multiplex_base") == 0)
1174           {
1175             // The IOM number is in the low 2 bits
1176             // The address is in the high 7 bits which are mapped
1177             // to bits 12 to 18 of a 24 bit address
1178             //
1179 //  0  1  2  3  4  5  6  7  8
1180 //  x  x  x  x  x  x  x  y  y
1181 //
1182 //  Address
1183 //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 29 20 21 22 23 24
1184 //  0  0  0  0  0  0  0  0  0  0  0  0  x  x  x  x  x  x  x  0  0  0  0  0  0
1185 //
1186 // IOM number
1187 //
1188 //  0  1
1189 //  y  y
1190 
1191             p -> configSwMultiplexBaseAddress = (word9) v;
1192             continue;
1193           }
1194 
1195         if (strcmp (name, "tapechan") == 0)
1196           {
1197             p -> configSwBootloadMagtapeChan = (word6) v;
1198             continue;
1199           }
1200 
1201         if (strcmp (name, "cardchan") == 0)
1202           {
1203             p -> configSwBootloadCardrdrChan = (word6) v;
1204             continue;
1205           }
1206 
1207         if (strcmp (name, "scuport") == 0)
1208           {
1209             p -> configSwBootloadPort = (word3) v;
1210             continue;
1211           }
1212 
1213         if (strcmp (name, "port") == 0)
1214           {
1215             port_num = (uint) v;
1216             continue;
1217           }
1218 
1219         if (strcmp (name, "addr") == 0)
1220           {
1221             p -> configSwPortAddress[port_num] = (uint) v;
1222             continue;
1223           }
1224 
1225         if (strcmp (name, "interlace") == 0)
1226           {
1227             p -> configSwPortInterface[port_num] = (uint) v;
1228             continue;
1229           }
1230 
1231         if (strcmp (name, "enable") == 0)
1232           {
1233             p -> configSwPortEnable[port_num] = (uint) v;
1234             continue;
1235           }
1236 
1237         if (strcmp (name, "initenable") == 0)
1238           {
1239             p -> configSwPortSysinitEnable[port_num] = (uint) v;
1240             continue;
1241           }
1242 
1243         if (strcmp (name, "halfsize") == 0)
1244           {
1245             p -> configSwPortHalfsize[port_num] = (uint) v;
1246             continue;
1247           }
1248 
1249         if (strcmp (name, "store_size") == 0)
1250           {
1251             p -> configSwPortStoresize[port_num] = (uint) v;
1252             continue;
1253           }
1254 
1255         sim_printf ("error: %s: Invalid cfg_parse rc <%ld>\r\n", __func__, (long) rc);
1256         cfg_parse_done (& cfg_state);
1257         return SCPE_ARG;
1258       } // process statements
1259     cfg_parse_done (& cfg_state);
1260     return SCPE_OK;
1261   }
1262 
1263 static t_stat iom_reset_unit (UNIT * uptr, UNUSED int32 value, UNUSED const char * cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1264                        UNUSED void * desc)
1265   {
1266     uint iom_unit_idx = (uint) (uptr - iom_unit);
1267     iom_unit_reset_idx (iom_unit_idx);
1268     return SCPE_OK;
1269   }
1270 
1271 static MTAB iom_mod[] =
1272   {
1273     {
1274        MTAB_XTD | MTAB_VUN | \
1275        MTAB_NMO | MTAB_NC,       /* Mask               */
1276       0,                         /* Match              */
1277       "MBX",                     /* Print string       */
1278       NULL,                      /* Match string       */
1279       NULL,                      /* Validation routine */
1280       iom_show_mbx,              /* Display routine    */
1281       NULL,                      /* Value descriptor   */
1282       NULL                       /* Help string        */
1283     },
1284     {
1285       MTAB_XTD | MTAB_VUN | \
1286       MTAB_NMO | MTAB_VALR,      /* Mask               */
1287       0,                         /* Match              */
1288       "CONFIG",                  /* Print string       */
1289       "CONFIG",                  /* Match string       */
1290       iom_set_config,            /* Validation routine */
1291       iom_show_config,           /* Display routine    */
1292       NULL,                      /* Value descriptor   */
1293       NULL                       /* Help string        */
1294     },
1295     {
1296       MTAB_XTD | MTAB_VUN | \
1297       MTAB_NMO | MTAB_VALR,      /* Mask               */
1298       0,                         /* Match              */
1299       (char *) "RESET",          /* Print string       */
1300       (char *) "RESET",          /* Match string       */
1301       iom_reset_unit,            /* Validation routine */
1302       NULL,                      /* Display routine    */
1303       (char *) "reset IOM unit", /* Value descriptor   */
1304       NULL                       /* Help               */
1305     },
1306     {
1307       MTAB_XTD | MTAB_VDV | \
1308       MTAB_NMO | MTAB_VALR,                 /* Mask               */
1309       0,                                    /* Match              */
1310       "NUNITS",                             /* Print string       */
1311       "NUNITS",                             /* Match string       */
1312       iom_set_units,                        /* Validation routine */
1313       iom_show_units,                       /* Display routine    */
1314       "Number of IOM units in the system",  /* Value descriptor   */
1315       NULL                                  /* Help string        */
1316     },
1317     {
1318       0, 0, NULL, NULL, 0, 0, NULL, NULL
1319     }
1320   };
1321 
1322 static DEBTAB iom_dt[] =
1323   {
1324     { "NOTIFY", DBG_NOTIFY, NULL },
1325     { "INFO",   DBG_INFO,   NULL },
1326     { "ERR",    DBG_ERR,    NULL },
1327     { "WARN",   DBG_WARN,   NULL },
1328     { "DEBUG",  DBG_DEBUG,  NULL },
1329     { "TRACE",  DBG_TRACE,  NULL },
1330     { "ALL",    DBG_ALL,    NULL }, // Don't move as it messes up DBG message
1331     { NULL,     0,          NULL }
1332   };
1333 
1334 //
1335 // iom_unit_reset_idx ()
1336 //
1337 //  Reset -- Reset to initial state -- clear all device flags and cancel any
1338 //  any outstanding timing operations. Used by RESET, RUN, and BOOT commands
1339 //
1340 //  Note that all reset()s run after once-only init().
1341 //
1342 //
1343 
1344 t_stat iom_unit_reset_idx (UNUSED uint iom_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
1345   {
1346     return SCPE_OK;
1347   }
1348 
1349 static t_stat iom_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1350   {
1351     //sim_debug (DBG_INFO, & iom_dev, "%s: running.\r\n", __func__);
1352 
1353     for (uint iom_unit_idx = 0; iom_unit_idx < iom_dev.numunits; iom_unit_idx ++)
1354       {
1355         iom_unit_reset_idx (iom_unit_idx);
1356       }
1357     return SCPE_OK;
1358   }
1359 
1360 static t_stat boot_svc (UNIT * unitp);
1361 static UNIT boot_channel_unit[N_IOM_UNITS_MAX] = {
1362 #if defined(NO_C_ELLIPSIS)
1363   { UDATA (& boot_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1364   { UDATA (& boot_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1365   { UDATA (& boot_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
1366   { UDATA (& boot_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
1367 #else
1368   [0 ... N_IOM_UNITS_MAX - 1] = {
1369     UDATA (& boot_svc, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
1370   }
1371 #endif
1372 };
1373 
1374 /*
1375  * init_memory_iom ()
1376  *
1377  * Load a few words into memory.   Simulates pressing the BOOTLOAD button
1378  * on an IOM or equivalent.
1379  *
1380  * All values are from bootload_tape_label.alm.  See the comments at the
1381  * top of that file.  See also doc #43A239854.
1382  *
1383  * NOTE: The values used here are for an IOM, not an IOX.
1384  * See init_memory_iox () below.
1385  *
1386  */
1387 
1388 static void init_memory_iom (uint iom_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
1389   {
1390     // The presence of a 0 in the top six bits of word 0 denote an IOM boot
1391     // from an IOX boot
1392 
1393     // " The channel number ("Chan#") is set by the switches on the IOM to be
1394     // " the channel for the tape subsystem holding the bootload tape. The
1395     // " drive number for the bootload tape is set by switches on the tape
1396     // " MPC itself.
1397 
1398     //sim_debug (DBG_INFO, & iom_dev,
1399       //"%s: Performing load of eleven words from IOM %c bootchannel to memory.\r\n",
1400       //__func__, iomChar (iom_unit_idx));
1401 
1402     word12 base = iom_unit_data[iom_unit_idx].configSwIomBaseAddress;
1403 
1404     // bootload_io.alm insists that pi_base match
1405     // template_slt_$iom_mailbox_absloc
1406 
1407     //uint pi_base = iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & ~3;
1408     word36 pi_base = (((word36)  iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress)  << 3) |
1409                      (((word36) (iom_unit_data[iom_unit_idx].configSwIomBaseAddress & 07700U)) << 6) ;
1410     word3 iom_num = ((word36) iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress) & 3;
1411     word36 cmd = 5;       // 6 bits; 05 for tape, 01 for cards
1412     word36 dev = 0;       // 6 bits: drive number
1413 
1414     // is-IMU flag
1415     word36 imu = iom_unit_data[iom_unit_idx].config_sw_model == CONFIG_SW_MODEL_IMU ? 1 : 0;       // 1 bit
1416 
1417     // Description of the bootload channel from 43A239854
1418     //    Legend
1419     //    BB - Bootload channel #
1420     //    C - Cmd (1 or 5)
1421     //    N - IOM #
1422     //    P - Port #
1423     //    XXXX00 - Base Addr -- 01400
1424     //    XXYYYY0 Program Interrupt Base
1425 
1426     enum config_sw_bootload_device_e bootdev = iom_unit_data[iom_unit_idx].configSwBootloadCardTape;
1427 
1428     word6 bootchan;
1429     if (bootdev == CONFIG_SW_BLCT_CARD)
1430       bootchan = iom_unit_data[iom_unit_idx].configSwBootloadCardrdrChan;
1431     else // CONFIG_SW_BLCT_TAPE
1432       bootchan = iom_unit_data[iom_unit_idx].configSwBootloadMagtapeChan;
1433 
1434     // 1
1435     // system fault vector; DIS 0 instruction (imu bit not mentioned by
1436     // 43A239854)
1437 
1438     word36 dis0 = 0616200;
1439 
1440     M[010 + 2 * iom_num + 0] = (imu << 34) | dis0;
1441 
1442     // Simulator specific hack; the simulator flags memory words as
1443     // being "uninitialized"; because the fault pair is fetched as a
1444     // pair, the odd word's uninitialized state may generate a
1445     // run-time warning, even though it is not executed.
1446     //
1447     // By zeroing it here, we clear the flag and avoid the message
1448     // Since the memory will have already been set to zero by
1449     // the "Initialize and clear", this will have no affect
1450     // on the boot operation.
1451 
1452     M[010 + 2 * iom_num + 1] = 0;
1453 
1454     // 2
1455     // terminate interrupt vector (overwritten by bootload)
1456 
1457     M[030 + 2 * iom_num] = dis0;
1458 
1459     // 3
1460     // tally word for sys fault status
1461 
1462     // XXX CAC: Clang -Wsign-conversion claims 'base<<6' is int
1463     word24 base_addr =  (word24) base << 6; // 01400
1464     M[base_addr + 7] = ((word36) base_addr << 18) | 02000002;
1465 
1466     // 4
1467     // Fault channel DCW
1468 
1469     // bootload_tape_label.alm says 04000, 43A239854 says 040000.  Since
1470     // 43A239854 says "no change", 40000 is correct; 4000 would be a
1471     // large tally
1472 
1473     // Connect channel LPW; points to PCW at 000000
1474     M[base_addr + 010] = 040000;
1475 
1476     // 5
1477     // Boot device LPW; points to IDCW at 000003
1478 
1479     word24 mbx = base_addr + 4u * bootchan;
1480     M[mbx]     = 03020003;
1481 
1482     // 6
1483     // Second IDCW: IOTD to loc 30 (startup fault vector)
1484 
1485     M[4] = 030 << 18;
1486 
1487     // 7
1488     // Default SCW points at unused first mailbox.
1489 
1490     // T&D tape overwrites this before the first status is saved, though.
1491     // CAC: But the status is never saved, only a $CON occurs, never
1492     // a status service
1493 
1494     // SCW
1495 
1496     M[mbx + 2] = ((word36)base_addr << 18);
1497 
1498     // 8
1499     // 1st word of bootload channel PCW
1500 
1501     M[0] = 0720201;
1502 
1503     // 9
1504     // 2nd word of PCW pair
1505 
1506     // SCU port
1507     word3 port = iom_unit_data[iom_unit_idx].configSwBootloadPort; // 3 bits;
1508 
1509     // Why does bootload_tape_label.alm claim that a port number belongs in
1510     // the low bits of the 2nd word of the PCW?  The lower 27 bits of the
1511     // odd word of a PCW should be all zero.
1512 
1513     //[CAC] Later, bootload_tape_label.alm does:
1514     //
1515     //     cioc    bootload_info+1 " port # stuck in PCW
1516     //     lda     0,x5            " check for status
1517     //
1518     // So this is a bootloader kludge to pass the bootload SCU number
1519     //
1520 
1521     //[CAC] From Rev01.AN70.archive:
1522     //  In BOS compatibility mode, the BOS BOOT command simulates the IOM,
1523     //  leaving the same information.  However, it also leaves a config deck
1524     //  and flagbox (although bce has its own flagbox) in the usual locations.
1525     //  This allows Bootload Multics to return to BOS if there is a BOS to
1526     //  return to.  The presence of BOS is indicated by the tape drive number
1527     //  being non-zero in the idcw in the "IOM" provided information.  (This is
1528     //  normally zero until firmware is loaded into the bootload tape MPC.)
1529 
1530     M[1] = ((word36) (bootchan) << 27) | port;
1531 
1532     // 10
1533     // word after PCW (used by program)
1534 
1535     M[2] = ((word36) base_addr << 18) | (pi_base) | iom_num;
1536 
1537     // 11
1538     // IDCW for read binary
1539 
1540     M[3] = (cmd << 30) | (dev << 24) | 0700000;
1541   }
1542 
1543 static t_stat boot_svc (UNIT * unitp)
     /* [previous][next][first][last][top][bottom][index][help] */
1544   {
1545     uint iom_unit_idx = (uint) (unitp - boot_channel_unit);
1546     // the docs say press sysinit, then boot; we don't have an
1547     // explicit "sysinit", so we treat "reset iom" as sysinit.
1548     // The docs don't say what the behavior is if you don't press
1549     // sysinit first so we won't worry about it.
1550 
1551     //sim_debug (DBG_DEBUG, & iom_dev, "%s: starting on IOM %c\r\n",
1552     //__func__, iomChar (iom_unit_idx));
1553 
1554     // This is needed to reset the interrupt mask registers; Multics tampers
1555     // with runtime values, and mucks up rebooting on multi-CPU systems.
1556     //scu_reset (NULL);
1557     for (int port_num = 0; port_num < N_SCU_PORTS; port_num ++)
1558       {
1559         if (! cables->iom_to_scu[iom_unit_idx][port_num].in_use)
1560           continue;
1561         uint scu_unit_idx = cables->iom_to_scu[iom_unit_idx][port_num].scu_unit_idx;
1562         scu_unit_reset ((int) scu_unit_idx);
1563       }
1564 
1565     // initialize memory with boot program
1566     init_memory_iom (iom_unit_idx);
1567 
1568     // Start the remote console listener
1569     startRemoteConsole ();
1570 
1571     // Start the FNP dialup listener
1572     startFNPListener ();
1573 
1574     // simulate $CON
1575     // XXX Making the assumption that low memory is connected to port 0, ..., high to 3
1576     if (! cables->iom_to_scu[iom_unit_idx][0].in_use)
1577       {
1578         sim_warn ("boot iom can't find a SCU\r\n");
1579         return SCPE_ARG;
1580       }
1581     uint scu_unit_idx = cables->iom_to_scu[iom_unit_idx][0].scu_unit_idx;
1582     iom_interrupt (scu_unit_idx, iom_unit_idx);
1583 
1584     //sim_debug (DBG_DEBUG, &iom_dev, "%s finished\r\n", __func__);
1585 
1586     // returning OK from the BOOT command causes the CPU start
1587     return SCPE_OK;
1588   }
1589 
1590 static t_stat iom_boot (int unitNum, UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1591   {
1592     if (unitNum < 0 || unitNum >= (int) iom_dev.numunits)
1593       {
1594         sim_printf ("%s: Invalid unit number %d\r\n", __func__, unitNum);
1595         return SCPE_ARG;
1596       }
1597     //uint iom_unit_idx = (uint) unitNum;
1598     boot_svc (& boot_channel_unit[unitNum]);
1599     return SCPE_OK;
1600   }
1601 
1602 DEVICE iom_dev =
1603   {
1604     "IOM",        /* name                */
1605     iom_unit,     /* units               */
1606     NULL,         /* registers           */
1607     iom_mod,      /* modifiers           */
1608     N_IOM_UNITS,  /* #units              */
1609     10,           /* address radix       */
1610     8,            /* address width       */
1611     1,            /* address increment   */
1612     8,            /* data radix          */
1613     8,            /* data width          */
1614     NULL,         /* examine routine     */
1615     NULL,         /* deposit routine     */
1616     iom_reset,    /* reset routine       */
1617     iom_boot,     /* boot routine        */
1618     NULL,         /* attach routine      */
1619     NULL,         /* detach routine      */
1620     NULL,         /* context             */
1621     DEV_DEBUG,    /* flags               */
1622     0,            /* debug control flags */
1623     iom_dt,       /* debug flag names    */
1624     NULL,         /* memory size change  */
1625     NULL,         /* logical name        */
1626     NULL,         /* help                */
1627     NULL,         /* attach help         */
1628     NULL,         /* help context        */
1629     NULL,         /* description         */
1630     NULL          /* end                 */
1631   };
1632 
1633 static uint mbxLoc (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1634   {
1635 // IDX is correct here as computation is based on physical unit
1636 // configuration switches
1637     word12 base      = iom_unit_data[iom_unit_idx].configSwIomBaseAddress;
1638     word24 base_addr = ((word24) base) << 6; // 01400
1639     word24 mbx       = base_addr + 4 * chan;
1640     //sim_debug (DBG_DEBUG, & iom_dev, "%s: IOM %c, chan %d is %012o\r\n",
1641       //__func__, iomChar (iom_unit_idx), chan, mbx);
1642     return mbx;
1643   }
1644 
1645 /*
1646  * status_service ()
1647  *
1648  * Write status info into a status mailbox.
1649  *
1650  * BUG: Only partially implemented.
1651  * WARNING: The diag tape will crash because we don't write a non-zero
1652  * value to the low 4 bits of the first status word.  See comments
1653  * at the top of mt.c. [CAC] Not true. The IIOC writes those bits to
1654  * tell the bootloader code whether the boot came from an IOM or IIOC.
1655  * The connect channel does not write status bits. The diag tape crash
1656  * was due so some other issue.
1657  *
1658  */
1659 
1660 // According to gtss_io_status_words.incl.pl1
1661 //
1662 //,     3 WORD1
1663 //,       4 Termination_indicator         bit(01)unal
1664 //,       4 Power_bit                     bit(01)unal
1665 //,       4 Major_status                  bit(04)unal
1666 //,       4 Substatus                     bit(06)unal
1667 //,       4 PSI_channel_odd_even_ind      bit(01)unal
1668 //,       4 Marker_bit_interrupt          bit(01)unal
1669 //,       4 Reserved                      bit(01)unal
1670 //,       4 Lost_interrupt_bit            bit(01)unal
1671 //,       4 Initiate_interrupt_ind        bit(01)unal
1672 //,       4 Abort_indicator               bit(01)unal
1673 //,       4 IOM_status                    bit(06)unal
1674 //,       4 Address_extension_bits        bit(06)unal
1675 //,       4 Record_count_residue          bit(06)unal
1676 //
1677 //,      3 WORD2
1678 //,       4 Data_address_residue          bit(18)unal
1679 //,       4 Character_count               bit(03)unal
1680 //,       4 Read_Write_control_bit        bit(01)unal
1681 //,       4 Action_code                   bit(02)unal
1682 //,       4 Word_count_residue            bit(12)unal
1683 
1684 // iom_stat.incl.pl1
1685 //
1686 // (2 t bit (1),             /* set to "1"b by IOM                         */
1687 //  2 power bit (1),         /* non-zero if peripheral absent or power off */
1688 //  2 major bit (4),         /* major status                               */
1689 //  2 sub bit (6),           /* substatus                                  */
1690 //  2 eo bit (1),            /* even/odd bit                               */
1691 //  2 marker bit (1),        /* non-zero if marker status                  */
1692 //  2 soft bit (2),          /* software status                            */
1693 //  2 initiate bit (1),      /* initiate bit                               */
1694 //  2 abort bit (1),         /* software abort bit                         */
1695 //  2 channel_stat bit (3),  /* IOM channel status                         */
1696 //  2 central_stat bit (3),  /* IOM central status                         */
1697 //  2 mbz bit (6),
1698 //  2 rcount bit (6),        /* record count residue                       */
1699 //
1700 //  2 address bit (18),      /* DCW address residue                        */
1701 //  2 char_pos bit (3),      /* character position residue                 */
1702 //  2 r bit (1),             /* non-zero if reading                        */
1703 //  2 type bit (2),          /* type of last DCW                           */
1704 //  2 tally bit (12)) unal;  /* DCW tally residue                          */
1705 
1706 // Searching the Multics source indicates that
1707 //   eo is  used by tape
1708 //   marker is used by tape and printer
1709 //   soft is set by tolts as /* set "timeout" */'
1710 //   soft is reported by tape, but not used.
1711 //   initiate is used by tape and printer
1712 //   abort is reported by tape, but not used
1713 //   char_pos is used by tape, console
1714 
1715 // pg B26: "The DCW residues stored in the status in page mode will
1716 // represent the next absolute address (bits 6-23) of data prior to
1717 // the application of the Page Table Word"
1718 
1719 static int status_service (uint iom_unit_idx, uint chan, bool marker)
     /* [previous][next][first][last][top][bottom][index][help] */
1720   {
1721 #if defined(TESTING)
1722     cpu_state_t * cpup = _cpup;
1723 #endif
1724     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1725     // See page 33 and AN87 for format of y-pair of status info
1726 
1727     // BUG: much of the following is not tracked
1728 
1729     word36 word1, word2;
1730     word1 = 0;
1731     putbits36_12 (& word1, 0, p -> stati);
1732     // isOdd can be set to zero; see
1733     //   https://ringzero.wikidot.com/wiki:cac-2015-10-22
1734     //putbits36_1 (& word1, 12, p -> isOdd ? 0 : 1);
1735     putbits36_1 (& word1, 13, marker ? 1 : 0);
1736     putbits36_2 (& word1, 14, 0); // software status
1737     putbits36_1 (& word1, 16, p -> initiate ? 1 : 0);
1738     putbits36_1 (& word1, 17, 0); // software abort bit
1739     putbits36_3 (& word1, 18, (word3) p -> chanStatus);
1740     //putbits36_3 (& word1, 21, iom_unit_data[iom_unit_idx].iomStatus);
1741     putbits36_3 (& word1, 21, 0);
1742 
1743 
1744 
1745 
1746     putbits36_6 (& word1, 30, p -> recordResidue);
1747 
1748     word2 = 0;
1749 
1750 
1751 
1752 
1753 
1754     putbits36_3  (& word2, 18, p -> charPos);
1755     putbits36_1  (& word2, 21, p -> isRead ? 1 : 0);
1756     putbits36_12 (& word2, 24, p -> tallyResidue);
1757 
1758     // BUG: need to write to mailbox queue
1759 
1760     // T&D tape does *not* expect us to cache original SCW, it expects us to
1761     // use the SCW loaded from tape.
1762 
1763     uint chanloc   = mbxLoc (iom_unit_idx, chan);
1764     word24 scwAddr = chanloc + IOM_MBX_SCW;
1765     word36 scw;
1766     iom_core_read_lock (iom_unit_idx, scwAddr, & scw, __func__);
1767     word18 addr    = getbits36_18 (scw, 0);   // absolute
1768     uint lq        = getbits36_2 (scw, 18);
1769     uint tally     = getbits36_12 (scw, 24);
1770 
1771     sim_debug (DBG_DEBUG, & iom_dev, "%s: Status: 0%012"PRIo64" 0%012"PRIo64"\r\n",
1772                __func__, word1, word2);
1773     if (lq == 3)
1774       {
1775         sim_debug (DBG_WARN, &iom_dev,
1776                    "%s: SCW for channel %d has illegal LQ\r\n",
1777                    __func__, chan);
1778         lq = 0;
1779       }
1780     iom_core_write2 (iom_unit_idx, addr, word1, word2, __func__);
1781 
1782     // XXX: PVS-Studio believes that tally is always == 0!?
1783     if (tally > 0 || (tally == 0 && lq != 0)) //-V560
1784       {
1785         switch (lq)
1786           {
1787             case 0:
1788               // list
1789               if (tally != 0)
1790                 {
1791                   tally --;
1792                   addr += 2;
1793                 }
1794               break;
1795 
1796             case 1:
1797               // 4 entry (8 word) queue
1798               if (tally % 8 == 1 /* || tally % 8 == -1 */)
1799                 addr -= 8;
1800               else
1801                 addr += 2;
1802               tally --;
1803               break;
1804 
1805             case 2:
1806               // 16 entry (32 word) queue
1807               if (tally % 32 == 1 /* || tally % 32 == -1 */)
1808                 addr -= 32;
1809               else
1810                 addr += 2;
1811               tally --;
1812               break;
1813           }
1814 
1815        // tally is 12 bit math
1816        if (tally & ~07777U)
1817           {
1818             sim_debug (DBG_WARN, & iom_dev,
1819                        "%s: Tally SCW address 0%o under/over flow\r\n",
1820                        __func__, tally);
1821             tally &= 07777;
1822           }
1823 
1824         putbits36_12 (& scw, 24, (word12) tally);
1825         putbits36_18 (& scw, 0, addr);
1826       }
1827 
1828     iom_core_write_unlock (iom_unit_idx, scwAddr, scw, __func__);
1829 
1830     // BUG: update SCW in core
1831     return 0;
1832   }
1833 
1834 static word24 UNUSED build_AUXPTW_address (uint iom_unit_idx, int chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1835   {
1836 //    0      5 6            16 17  18              21  22  23
1837 //   ---------------------------------------------------------
1838 //   | Zeroes | IOM Base Address  |                          |
1839 //   ---------------------------------------------------------
1840 //                         |    Channel Number       | 1 | 1 |
1841 //                         -----------------------------------
1842 // XXX Assuming 16 and 17 are or'ed. Pg B4 doesn't specify
1843 
1844     word12 IOMBaseAddress  = iom_unit_data[iom_unit_idx].configSwIomBaseAddress;
1845     word24 addr            = (((word24) IOMBaseAddress) & MASK12) << 6;
1846     addr                  |= ((uint) chan & MASK6) << 2;
1847     addr                  |= 03;
1848     return addr;
1849   }
1850 
1851 static word24 build_DDSPTW_address (word18 PCW_PAGE_TABLE_PTR, word8 pageNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
1852   {
1853 //    0      5 6        15  16  17  18                       23
1854 //   ----------------------------------------------------------
1855 //   | Page Table Pointer | 0 | 0 |                           |
1856 //   ----------------------------------------------------------
1857 // or
1858 //                        -------------------------------------
1859 //                        |  Direct chan addr 6-13            |
1860 //                        -------------------------------------
1861     if (PCW_PAGE_TABLE_PTR & 3)
1862       sim_warn ("%s: pcw_ptp %#o page_no  %#o\r\n", __func__, PCW_PAGE_TABLE_PTR, pageNumber);
1863     word24 addr = (((word24) PCW_PAGE_TABLE_PTR) & MASK18) << 6;
1864     addr |= pageNumber;
1865     return addr;
1866   }
1867 
1868 // Fetch Direct Data Service Page Table Word
1869 
1870 static void fetch_DDSPTW (uint iom_unit_idx, int chan, word18 addr)
     /* [previous][next][first][last][top][bottom][index][help] */
1871   {
1872     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1873     word24 pgte         = build_DDSPTW_address (p -> PCW_PAGE_TABLE_PTR,
1874                                       (addr >> 10) & MASK8);
1875     iom_core_read (iom_unit_idx, pgte, (word36 *) & p -> PTW_DCW, __func__);
1876     if ((p -> PTW_DCW & 0740000777747) != 04llu)
1877       sim_warn ("%s: chan %ld addr %#llo pgte %08llo ptw %012llo\r\n",
1878                 __func__, (long)chan, (unsigned long long)addr,
1879                 (unsigned long long)pgte, (unsigned long long)p -> PTW_DCW);
1880   }
1881 
1882 static word24 build_IDSPTW_address (word18 PCW_PAGE_TABLE_PTR, word1 seg, word8 pageNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
1883   {
1884 //    0      5 6        15  16  17  18                       23
1885 //   ----------------------------------------------------------
1886 //   | Page Table Pointer         |                           |
1887 //   ----------------------------------------------------------
1888 // plus
1889 //                    ----
1890 //                    | S |
1891 //                    | E |
1892 //                    | G |
1893 //                    -----
1894 // plus
1895 //                        -------------------------------------
1896 //                        |         DCW 0-7                   |
1897 //                        -------------------------------------
1898 
1899     word24 addr  = (((word24) PCW_PAGE_TABLE_PTR) & MASK18) << 6;
1900     addr        += (((word24) seg) & 01) << 8;
1901     addr        += pageNumber;
1902     return addr;
1903   }
1904 
1905 // Fetch Indirect Data Service Page Table Word
1906 
1907 static void fetch_IDSPTW (uint iom_unit_idx, int chan, word18 addr)
     /* [previous][next][first][last][top][bottom][index][help] */
1908   {
1909     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1910     word24 pgte         = build_IDSPTW_address (p -> PCW_PAGE_TABLE_PTR,
1911                                       p -> SEG,
1912                                       (addr >> 10) & MASK8);
1913     iom_core_read (iom_unit_idx, pgte, (word36 *) & p -> PTW_DCW, __func__);
1914     if ((p -> PTW_DCW & 0740000777747) != 04llu)
1915       sim_warn ("%s: chan %d addr %#o ptw %012llo\r\n",
1916                 __func__, chan, addr, (unsigned long long)p -> PTW_DCW);
1917   }
1918 
1919 static word24 build_LPWPTW_address (word18 PCW_PAGE_TABLE_PTR, word1 seg, word8 pageNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
1920   {
1921 //    0      5 6        15  16  17  18                       23
1922 //   ----------------------------------------------------------
1923 //   | Page Table Pointer         |                           |
1924 //   ----------------------------------------------------------
1925 // plus
1926 //                    ----
1927 //                    | S |
1928 //                    | E |
1929 //                    | G |
1930 //                    -----
1931 // plus
1932 //                        -------------------------------------
1933 //                        |         LPW 0-7                   |
1934 //                        -------------------------------------
1935 
1936     word24 addr  = (((word24) PCW_PAGE_TABLE_PTR) & MASK18) << 6;
1937     addr        += (((word24) seg) & 01) << 8;
1938     addr        += pageNumber;
1939     return addr;
1940   }
1941 
1942 static void fetch_LPWPTW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1943   {
1944     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1945     word24 addr         = build_LPWPTW_address (p -> PCW_PAGE_TABLE_PTR,
1946                                       p -> SEG,
1947                                       (p -> LPW_DCW_PTR >> 10) & MASK8);
1948     iom_core_read (iom_unit_idx, addr, (word36 *) & p -> PTW_LPW, __func__);
1949     if ((p -> PTW_LPW & 0740000777747) != 04llu)
1950       sim_warn ("%s: chan %d addr %#o ptw %012llo\r\n",
1951                 __func__, chan, addr, (unsigned long long)p -> PTW_LPW);
1952   }
1953 
1954 // 'write' means peripheral write; i.e. the peripheral is writing to core after
1955 // reading media.
1956 
1957 void iom_direct_data_service (uint iom_unit_idx, uint chan, word24 daddr, word36 * data,
     /* [previous][next][first][last][top][bottom][index][help] */
1958                               iom_direct_data_service_op op)
1959   {
1960     // The direct data service consists of one core storage cycle (Read Clear, Read
1961     // Restore or Clear Write, Double or Single Precision) using an absolute
1962     // 24-bit address supplied by the channel. This service is used by
1963     // "peripherals" capable of providing addresses from an external source,
1964     // such as the 355 Direct Interface Adapter.
1965 
1966     // The PCW received for the channel contains a Page Table Pointer
1967     // which is used as an absolute address pointing to the Page Table which
1968     // has been set up by software for the Direct Channel. All addresses
1969     // for the Direct Channel will be accessed using this Page Table if paging
1970     // has been requested in the PCW.
1971 
1972     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1973 
1974     if (p -> masked)
1975       return;
1976 
1977     if (p -> PCW_63_PTP && p -> PCW_64_PGE)
1978       {
1979         fetch_DDSPTW (iom_unit_idx, (int) chan, daddr);
1980         daddr = ((uint) getbits36_14 (p -> PTW_DCW, 4) << 10) | (daddr & MASK10);
1981       }
1982 
1983     if (op == direct_store)
1984       iom_core_write (iom_unit_idx, daddr, * data, __func__);
1985     else if (op == direct_load)
1986       iom_core_read (iom_unit_idx, daddr, data, __func__);
1987     else if (op == direct_read_clear)
1988       {
1989         iom_core_read_lock (iom_unit_idx, daddr, data, __func__);
1990         iom_core_write_unlock (iom_unit_idx, daddr, 0, __func__);
1991       }
1992   }
1993 
1994 // 'tally' is the transfer size request by Multics.
1995 // For write, '*cnt' is the number of words in 'data'.
1996 // For read, '*cnt' will be set to the number of words transferred.
1997 // Caller responsibility to allocate 'data' large enough to accommodate
1998 // 'tally' words.
1999 void iom_indirect_data_service (uint iom_unit_idx, uint chan, word36 * data, uint * cnt, bool write) {
     /* [previous][next][first][last][top][bottom][index][help] */
2000   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2001 
2002   if (p->masked)
2003     return;
2004 
2005   // tape_ioi_io.pl1  "If the initiate bit is set in the status, no data was
2006   //                   transferred (no tape movement occurred)."
2007   p->initiate = false;
2008 
2009   uint tally = p->DDCW_TALLY;
2010   uint daddr = p->DDCW_ADDR;
2011   if (tally == 0) {
2012     tally = 4096;
2013   }
2014   p->tallyResidue = (word12) tally;
2015 
2016   if (write) {
2017     uint c = * cnt;
2018     while (p->tallyResidue) {
2019       if (c == 0)
2020         break; // read buffer exhausted; returns w/tallyResidue != 0
2021 
2022       if (! IS_IONTP (p)) {
2023         if (p->PCW_63_PTP && p->PCW_64_PGE) {
2024           fetch_IDSPTW (iom_unit_idx, (int) chan, daddr);
2025           word24 addr = ((word24) (getbits36_14 (p -> PTW_DCW, 4) << 10)) | (daddr & MASK10);
2026           iom_core_write (iom_unit_idx, addr, * data, __func__);
2027         } else {
2028           if (daddr > MASK18) { // 256K overflow
2029             sim_warn ("%s 256K ovf\r\n", __func__); // XXX
2030             daddr &= MASK18;
2031           }
2032 
2033           // If PTP is not set, we are in cm1e or cm2e. Both are 'EXT DCW', so
2034           // we can elide the mode check here.
2035           uint daddr2 = daddr | (uint) p->ADDR_EXT << 18;
2036           iom_core_write (iom_unit_idx, daddr2, * data, __func__);
2037         }
2038       }
2039       daddr ++;
2040       data ++;
2041       p->tallyResidue --;
2042       c --;
2043     }
2044   } else { // read
2045     uint c = 0;
2046     while (p -> tallyResidue) {
2047       if (IS_IONTP (p)) {
2048         * data = 0;
2049       } else {
2050         if (p -> PCW_63_PTP  && p -> PCW_64_PGE) {
2051           fetch_IDSPTW (iom_unit_idx, (int) chan, daddr);
2052           word24 addr = ((word24) (getbits36_14 (p -> PTW_DCW, 4) << 10)) | (daddr & MASK10);
2053           iom_core_read (iom_unit_idx, addr, data, __func__);
2054         } else {
2055           // XXX assuming DCW_ABS
2056           if (daddr > MASK18) { // 256K overflow
2057             sim_warn ("%s 256K ovf\r\n", __func__); // XXX
2058             daddr &= MASK18;
2059           }
2060 
2061           // If PTP is not set, we are in cm1e or cm2e. Both are 'EXT DCW', so
2062           // we can elide the mode check here.
2063           uint daddr2 = daddr | (uint) p -> ADDR_EXT << 18;
2064           iom_core_read (iom_unit_idx, daddr2, data, __func__);
2065         }
2066       }
2067       daddr ++;
2068       p->tallyResidue --;
2069       data ++;
2070       c ++;
2071     }
2072     * cnt = c;
2073   }
2074 }
2075 
2076 static void update_chan_mode (uint iom_unit_idx, uint chan, bool tdcw)
     /* [previous][next][first][last][top][bottom][index][help] */
2077   {
2078     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2079     if (chan == IOM_CONNECT_CHAN)
2080       {
2081         p -> chanMode = cm1;
2082         return;
2083       }
2084 
2085     if (! tdcw)
2086       {
2087         if (p -> PCW_64_PGE == 0)
2088           {
2089             if (p -> LPW_20_AE == 0)
2090               {
2091                     p -> chanMode = cm1e;  // AE 0
2092               }
2093             else
2094               {
2095                     p -> chanMode = cm2e;  // AE 1
2096               }
2097 
2098           }
2099         else
2100           {
2101             if (p -> LPW_20_AE == 0)
2102               {
2103                 if (p -> LPW_23_REL == 0)
2104                   {
2105                     p -> chanMode = cm1;  // AE 0, REL 0
2106                   }
2107                 else
2108                   {
2109                     p -> chanMode = cm3a;  // AE 0, REL 1
2110                   }
2111 
2112               }
2113             else  // AE 1
2114               {
2115                 if (p -> LPW_23_REL == 0)
2116                   {
2117                     p -> chanMode = cm3b;  // AE 1, REL 0
2118                   }
2119                 else
2120                   {
2121                     p -> chanMode = cm4;  // AE 1, REL 1
2122                   }
2123 
2124               }
2125           }
2126       }
2127     else // tdcw
2128       {
2129         switch (p -> chanMode)
2130           {
2131             case cm1:
2132               if (p -> TDCW_32_PDTA)
2133                 {
2134                   p -> chanMode = cm2;
2135                   break;
2136                 }
2137               if (p -> TDCW_33_PDCW)
2138                 if (p -> TDCW_35_REL)
2139                   p -> chanMode = cm4; // 33, 35
2140                 else
2141                   p -> chanMode = cm3b; // 33, !35
2142               else
2143                 if (p -> TDCW_35_REL)
2144                   p -> chanMode = cm3a; // !33, 35
2145                 else
2146                   p -> chanMode = cm2; // !33, !35
2147               break;
2148 
2149             case cm2:
2150               if (p -> TDCW_33_PDCW)
2151                 if (p -> TDCW_35_REL)
2152                   p -> chanMode = cm4; // 33, 35
2153                 else
2154                   p -> chanMode = cm3b; // 33, !35
2155               else
2156                 if (p -> TDCW_35_REL)
2157                   p -> chanMode = cm3a; // !33, 35
2158                 else
2159                   p -> chanMode = cm2; // !33, !35
2160               break;
2161 
2162             case cm3a:
2163               if (p -> TDCW_33_PDCW)
2164                 p -> chanMode = cm4;
2165               break;
2166 
2167             case cm3b:
2168               if (p -> TDCW_35_REL)
2169                 p -> chanMode = cm4;
2170               break;
2171 
2172             case cm4:
2173               p -> chanMode = cm5;
2174               break;
2175 
2176             case cm5:
2177               break;
2178 
2179             case cm1e:
2180               {
2181                 if (p -> chanMode == cm1e && p -> TDCW_33_EC)
2182                   p -> chanMode = cm2e;
2183               }
2184               break;
2185 
2186             case cm2e:
2187               break;
2188           } // switch
2189       } // tdcw
2190   }
2191 
2192 static void write_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2193   {
2194     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2195 
2196     uint chanLoc = mbxLoc (iom_unit_idx, chan);
2197     iom_core_write (iom_unit_idx, chanLoc + IOM_MBX_LPW, p -> LPW, __func__);
2198     //sim_debug (DBG_DEBUG, & iom_dev,
2199                //"%s: chan %d lpw %012"PRIo64"\r\n",
2200                //__func__, chan, p -> LPW);
2201     if (chan != IOM_CONNECT_CHAN)
2202       {
2203         iom_core_write (iom_unit_idx, chanLoc + IOM_MBX_LPWX, p -> LPWX, __func__);
2204       }
2205   }
2206 
2207 /*
2208  * iom_advance_lpw ()
2209  *
2210  * Advance the channel LPW by one word and write it back to the channel
2211  * mailbox.  Called by the MGP device after delivering async packet data
2212  * via iom_indirect_data_service(), so that io_manager$get_status returns
2213  * next_lpw_offset = LPW_DCW_PTR + 1 instead of LPW_DCW_PTR.  This causes
2214  * ioi_masked to compute:
2215  *   auto_istat.offset = next_lpw_offset - 1 = old LPW_DCW_PTR
2216  * which (after divide-by-2) gives stop_buffer_number = buffer_number + 1,
2217  * allowing mgp_read_dcm_'s processing loop to fire.
2218  */
2219 void iom_advance_lpw (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2220   {
2221     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2222     p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 1u) & MASK18;
2223     putbits36_18 ((word36 *) & p -> LPW, 0, p -> LPW_DCW_PTR);
2224     write_LPW (iom_unit_idx, chan);
2225   }
2226 
2227 /*
2228  * iom_continue_channel ()
2229  *
2230  * Continue a payload channel that is currently in IOM_CMD_PENDING state
2231  * after the device has written data to the current workspace buffer.
2232  *
2233  * This is called by the MGP device after writing an incoming packet to the
2234  * current DCM workspace buffer[N].  It replicates one iteration of
2235  * doPayloadChannel's DCW list loop:
2236  *
2237  *   1. iom_list_service fetches IDCW[N+1] from the DCW list (advancing
2238  *      LPW_DCW_PTR to point to DDCW[N+1]).
2239  *
2240  *   2. d->iom_cmd dispatches IDCW[N+1] to the device handler.  For the
2241  *      MGP read command (cmd=001), the handler calls get_ddcw() which
2242  *      calls iom_list_service() again to fetch DDCW[N+1], setting
2243  *      DDCW_ADDR to workspace buffer[N+1] for the next incoming packet.
2244  *      LPW_DCW_PTR is then at IDCW[N+2].
2245  *
2246  *   3. send_marker_interrupt() signals Multics that buffer[N] is ready.
2247  *      mgp_read_dcm_ computes:
2248  *        stat.offset  = LPW_DCW_PTR_offset - 1 = 2*(N+2) - 1 = 2N+3
2249  *        stop_buffer  = divide(stat.offset, 2)  = N+1
2250  *      Since the DCM's buffer_number is N, the processing loop fires once,
2251  *      consuming buffer[N], then increments buffer_number to N+1.
2252  *
2253  * Because start_io sees running=true (marker interrupt), it does NOT call
2254  * ioi_$connect; the channel remains pending with DDCW_ADDR pointing to
2255  * buffer[N+1], ready for the next packet delivery.
2256  *
2257  * TDCW wrap-around is handled transparently by iom_list_service.
2258  */
2259 int iom_continue_channel (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2260   {
2261     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2262     struct iom_to_ctlr_s * d = & cables->iom_to_ctlr[iom_unit_idx][chan];
2263 
2264     if ((! d->in_use) || (! d->iom_cmd))
2265       {
2266         sim_warn ("%s: channel %d not in use\r\n", __func__, chan);
2267         return -1;
2268       }
2269 
2270     // Fetch the next IDCW (for buffer[N+1]) from the DCW list.
2271     // iom_list_service reads the DCW at LPW_DCW_PTR and advances the pointer.
2272     bool ptro, send, uff;
2273     int rc = iom_list_service (iom_unit_idx, chan, & ptro, & send, & uff);
2274     if (rc < 0)
2275       {
2276         sim_warn ("%s: list service failed for channel %d\r\n", __func__, chan);
2277         return -2;
2278       }
2279 
2280     // uff=true means the IOM tally ran out or a similar status condition was
2281     // flagged.  However, iom_list_service still fetches the correct IDCW and
2282     // sets send=true in all cases where the DCW dispatch is valid.  The uff
2283     // bit is a status indicator for the program's status queue, not a signal
2284     // that the DCW is unusable.  When send=false the !send check below will
2285     // abort; when send=true we should proceed.  Just log and continue.
2286     if (uff)
2287       {
2288         sim_warn ("%s: iom_list_service returned uff=%o for channel %d (proceeding)\r\n",
2289                   __func__, uff, chan);
2290       }
2291 
2292     if (! send)
2293       {
2294         sim_warn ("%s: iom_list_service returned send=%o for channel %d\r\n", __func__, send, chan);
2295         return -4;
2296       }
2297 
2298     // We expect an IDCW here (the marker+continue IDCW for the next buffer).
2299     // If we got a TDCW, iom_list_service already followed it and the DCW it
2300     // found after the transfer is what we have now.
2301     if (! IS_IDCW (p))
2302       {
2303         sim_warn ("%s: expected IDCW, got non-IDCW for channel %d\r\n",
2304                   __func__, chan);
2305         return -5;
2306       }
2307 
2308     // Set up channel state for an in-loop IDCW (not a PCW).
2309     p -> isPCW       = false;
2310     p -> tallyResidue = 0;
2311     p -> isRead      = true;
2312     p -> charPos     = 0;
2313     p -> chanStatus  = chanStatNormal;
2314 
2315     // Dispatch the IDCW to the device handler.  For MGP cmd=001 (read),
2316     // this calls get_ddcw() which fetches DDCW[N+1] and sets DDCW_ADDR to
2317     // workspace buffer[N+1].  The handler returns IOM_CMD_PENDING to keep
2318     // the channel active.
2319     int rc2 = d->iom_cmd (iom_unit_idx, chan);
2320 
2321     if (rc2 < 0)
2322       {
2323         sim_warn ("%s: iom_cmd failed for channel %d\r\n", __func__, chan);
2324         // we'll send a terminate interrupt in mgp_process_event
2325         return -6;
2326       }
2327 
2328     if (rc2 == IOM_CMD_DISCONNECT)
2329       {
2330         // we'll send a terminate interrupt in mgp_process_event
2331         return -7;
2332       }
2333 
2334     // rc2 == IOM_CMD_PENDING: DDCW_ADDR now points to buffer[N+1].
2335     // Send marker interrupt so mgp_read_dcm_ processes buffer[N].
2336     send_marker_interrupt (iom_unit_idx, (int) chan);
2337     return 0;
2338   }
2339 
2340 #if defined(TESTING)
2341 void dumpDCW (word36 DCW, word1 LPW_23_REL) {
     /* [previous][next][first][last][top][bottom][index][help] */
2342   static char * charCtrls[4] =
2343     { "terminate", "undefined", "proceed", "marker" };
2344   static char * chanCmds[16] =
2345     { "record",    "undefined", "nondata",     "undefined",
2346       "undefined", "undefined", "multirecord", "undefined",
2347       "character", "undefined", "undefined",   "undefined",
2348       "undefined", "undefined", "undefined",   "undefined"
2349     };
2350   word3 DCW_18_20_CP =      getbits36_3 (DCW, 18);
2351 
2352   if (DCW_18_20_CP == 07) { // IDCW
2353     word6 IDCW_DEV_CMD  =     getbits36_6 (DCW,  0);
2354     word6 IDCW_DEV_CODE =     getbits36_6 (DCW,  6);
2355  /* word6 IDCW_AE =           getbits36_6 (DCW,  12);
2356   * word1 IDCW_EC;
2357   * if (LPW_23_REL)
2358   *   IDCW_EC = 0;
2359   * else
2360   *   IDCW_EC =               getbits36_1 (DCW, 21); */
2361     word2 IDCW_CHAN_CTRL =    getbits36_2 (DCW, 22);
2362     word6 IDCW_CHAN_CMD  =    getbits36_6 (DCW, 24);
2363     word6 IDCW_COUNT     =    getbits36_6 (DCW, 30);
2364     sim_printf ("//   IDCW %012llo\r\n", DCW);
2365     sim_printf ("//     cmd             %02o %s\r\n", IDCW_DEV_CMD, cmdNames[IDCW_DEV_CMD]);
2366     sim_printf ("//     dev code        %02o\r\n", IDCW_DEV_CODE);
2367     sim_printf ("//     ctrl             %o (%s)\r\n", IDCW_CHAN_CTRL, charCtrls[IDCW_CHAN_CTRL]);
2368     sim_printf ("//     chancmd          %o (%s)\r\n", IDCW_CHAN_CMD, IDCW_CHAN_CMD < 16 ? chanCmds[IDCW_CHAN_CMD] : "unknown");
2369     sim_printf ("//     count            %o\r\n", IDCW_COUNT);
2370   } else { // TDCW or DDCW
2371     word18 TDCW_DATA_ADDRESS = getbits36_18 (DCW,  0);
2372     word1  TDCW_31_SEG       = getbits36_1  (DCW, 31);
2373     word1  TDCW_32_PDTA      = getbits36_1  (DCW, 32);
2374     word1  TDCW_33_PDCW      = getbits36_1  (DCW, 33);
2375     word1  TDCW_33_EC        = getbits36_1  (DCW, 33);
2376     word1  TDCW_34_RES       = getbits36_1  (DCW, 34);
2377     word1  TDCW_35_REL       = getbits36_1  (DCW, 35);
2378 
2379     word12 DDCW_TALLY        = getbits36_12 (DCW, 24);
2380     word18 DDCW_ADDR         = getbits36_18 (DCW,  0);
2381     word2  DDCW_22_23_TYPE   = getbits36_2  (DCW, 22);
2382     static char * types[4]   = { "IOTD", "IOTP", "TDCW", "IONTP" };
2383     if (DDCW_22_23_TYPE == 2) {
2384       sim_printf ("//   TDCW (2) %012llo\r\n", DCW);
2385       sim_printf ("//     dcw ptr   %06o\r\n", TDCW_DATA_ADDRESS);
2386       sim_printf ("//     seg            %o\r\n", TDCW_31_SEG);
2387       sim_printf ("//     pdta           %o\r\n", TDCW_32_PDTA);
2388       sim_printf ("//     pdcw           %o\r\n", TDCW_33_PDCW);
2389       sim_printf ("//     ec             %o\r\n", TDCW_33_EC);
2390       sim_printf ("//     res            %o\r\n", TDCW_34_RES);
2391       sim_printf ("//     rel            %o\r\n", TDCW_35_REL);
2392     } else {
2393       sim_printf ("//   %s (%o) %012llo\r\n", types[DDCW_22_23_TYPE], DDCW_22_23_TYPE, DCW);
2394       sim_printf ("//     tally       %04o\r\n", DDCW_TALLY);
2395       sim_printf ("//     addr            %02o\r\n", DDCW_ADDR);
2396       sim_printf ("//     cp               %o\r\n", getbits36_3 (DCW, 18));
2397     }
2398   }
2399 }
2400 
2401 static void dumpLPW (uint iom_unit_idx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
2402   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2403   char updateCase;
2404   if (p->LPW_21_NC)
2405     updateCase = 'c';
2406   else if (p->LPW_22_TAL)
2407     updateCase = 'b';
2408   else
2409     updateCase = 'a';
2410   static char * chanName[64] = {
2411         "0.",           //  0.   0
2412         "1.",           //  1.   1
2413         "Connect",      //  2.   2
2414         "3.",           //  3.   3
2415         "4.",           //  4.   4
2416         "5.",           //  5.   5
2417         "6.",           //  6.   6
2418         "7.",           //  7.   7
2419         "8.",           //  8.  10
2420         "9.",           //  9.  11
2421         "MTP Tape",     // 10.  12
2422         "IPC Disk",     // 11.  13
2423         "MPC Disk",     // 12.  14
2424         "Card reader",  // 13.  15
2425         "Card punch",   // 14.  16
2426         "Printer",      // 15.  17
2427         "FNP",          // 16.  20
2428         "FNP",          // 17.  21
2429         "FNP",          // 18.  22
2430         "FNP",          // 19.  23
2431         "FNP",          // 20.  24
2432         "FNP",          // 21.  25
2433         "FNP",          // 22.  26
2434         "FNP",          // 23.  27
2435         "24.",          // 24.  30
2436         "25.",          // 25.  31
2437         "ABSI",         // 26.  32
2438         "27.",          // 27.  33
2439         "28.",          // 28.  34
2440         "29.",          // 29.  35
2441         "Console",      // 30.  36
2442         "31.",          // 31.  37
2443         "Socket",       // 32.  40
2444         "Socket",       // 33.  41
2445         "Socket",       // 34.  42
2446         "Socket",       // 35.  43
2447         "Socket",       // 36.  44
2448         "Socket",       // 37.  45
2449         "Socket",       // 38.  46
2450         "39.",
2451         "40.",
2452         "41.",
2453         "42.",
2454         "43.",
2455         "44.",
2456         "45.",
2457         "46.",
2458         "47.",
2459         "48.",
2460         "49.",
2461         "50.",
2462         "51.",
2463         "52.",
2464         "53.",
2465         "54.",
2466         "55.",
2467         "56.",
2468         "57.",
2469         "58.",
2470         "59.",
2471         "60.",
2472         "61.",
2473         "62.",
2474         "63."
2475   };
2476   sim_printf ("// %s channel LPW %012llo (case %c)\r\n", chanName[chan], p->LPW, updateCase);
2477   sim_printf ("//   DCW pointer %06o\r\n",      p->LPW_DCW_PTR);
2478   sim_printf ("//     RES              %o\r\n", p->LPW_18_RES);
2479   sim_printf ("//     REL              %o\r\n", p->LPW_19_REL);
2480   sim_printf ("//     AE               %o\r\n", p->LPW_20_AE);
2481   sim_printf ("//     NC 21            %o\r\n", p->LPW_21_NC);
2482   sim_printf ("//     TAL 22           %o\r\n", p->LPW_22_TAL);
2483   sim_printf ("//     REL              %o\r\n", p->LPW_23_REL);
2484   sim_printf ("//     TALLY         %04o\r\n",  p->LPW_TALLY);
2485   sim_printf ("// \r\n");
2486 }
2487 #endif
2488 
2489 static void fetch_and_parse_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2490   {
2491 #if defined(TESTING)
2492     cpu_state_t * cpup = _cpup;
2493 #endif
2494     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2495     uint chanLoc        = mbxLoc (iom_unit_idx, chan);
2496 
2497     iom_core_read (iom_unit_idx, chanLoc + IOM_MBX_LPW, (word36 *) & p -> LPW, __func__);
2498     sim_debug (DBG_DEBUG, & iom_dev,
2499                "%s: lpw %08o %012"PRIo64"\r\n", __func__, chanLoc + IOM_MBX_LPW, p->LPW);
2500 
2501     p -> LPW_DCW_PTR = getbits36_18 (p -> LPW,  0);
2502     p -> LPW_18_RES  = getbits36_1  (p -> LPW, 18);
2503     p -> LPW_19_REL  = getbits36_1  (p -> LPW, 19);
2504     p -> LPW_20_AE   = getbits36_1  (p -> LPW, 20);
2505     p -> LPW_21_NC   = getbits36_1  (p -> LPW, 21);
2506     p -> LPW_22_TAL  = getbits36_1  (p -> LPW, 22);
2507     p -> LPW_23_REL  = getbits36_1  (p -> LPW, 23);
2508     p -> LPW_TALLY   = getbits36_12 (p -> LPW, 24);
2509 
2510     if (chan == IOM_CONNECT_CHAN)
2511       {
2512         p -> LPWX = 0;
2513         p -> LPWX_BOUND = 0;
2514         p -> LPWX_SIZE = 0;
2515       }
2516     else
2517       {
2518         iom_core_read (iom_unit_idx, chanLoc + IOM_MBX_LPWX, (word36 *) & p -> LPWX, __func__);
2519         p -> LPWX_BOUND = getbits36_18 (p -> LPWX, 0);
2520         p -> LPWX_SIZE  = getbits36_18 (p -> LPWX, 18);
2521       }
2522     update_chan_mode (iom_unit_idx, chan, false);
2523   }
2524 
2525 static void unpack_DCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2526   {
2527 #if defined(TESTING)
2528     cpu_state_t * cpup = _cpup;
2529 #endif
2530     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2531     p -> DCW_18_20_CP =      getbits36_3 (p -> DCW, 18);
2532 
2533     if (IS_IDCW (p)) // IDCW
2534       {
2535         p -> IDCW_DEV_CMD   = getbits36_6 (p -> DCW, 0);
2536         p -> IDCW_DEV_CODE  = getbits36_6 (p -> DCW, 6);
2537         p -> IDCW_AE        = getbits36_6 (p -> DCW, 12);
2538         if (p -> LPW_23_REL)
2539           p -> IDCW_EC      = 0;
2540         else
2541           p -> IDCW_EC      = getbits36_1 (p -> DCW, 21);
2542         if (p -> IDCW_EC)
2543           p -> SEG          = 1; // pat. step 45
2544         p -> IDCW_CHAN_CTRL = getbits36_2 (p -> DCW, 22);
2545         p -> IDCW_CHAN_CMD  = getbits36_6 (p -> DCW, 24);
2546         p -> IDCW_COUNT     = getbits36_6 (p -> DCW, 30);
2547         p->recordResidue = p->IDCW_COUNT;
2548 #if defined(TESTING)
2549         sim_debug (DBG_DEBUG, & iom_dev,
2550                    "%s: IDCW %012llo cmd %02o (%s) dev %02o ctrl %o chancmd %o\r\n",
2551                    __func__, p->DCW, p->IDCW_DEV_CMD, cmdNames[p->IDCW_DEV_CMD],
2552                    p->IDCW_DEV_CODE, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
2553 #endif
2554       }
2555     else // TDCW or DDCW
2556       {
2557         p -> TDCW_DATA_ADDRESS = getbits36_18 (p -> DCW,  0);
2558         p -> TDCW_31_SEG       = getbits36_1  (p -> DCW, 31);
2559         p -> TDCW_32_PDTA      = getbits36_1  (p -> DCW, 32);
2560         p -> TDCW_33_PDCW      = getbits36_1  (p -> DCW, 33);
2561         p -> TDCW_33_EC        = getbits36_1  (p -> DCW, 33);
2562         p -> TDCW_34_RES       = getbits36_1  (p -> DCW, 34);
2563         p -> TDCW_35_REL       = getbits36_1  (p -> DCW, 35);
2564         p -> DDCW_TALLY        = getbits36_12 (p -> DCW, 24);
2565         p -> DDCW_ADDR         = getbits36_18 (p -> DCW,  0);
2566         p -> DDCW_22_23_TYPE   = getbits36_2  (p -> DCW, 22);
2567         sim_debug (DBG_DEBUG, & iom_dev,
2568                    "%s: TDCW/DDCW %012llo tally %04o addr %06o type %o\r\n",
2569                    __func__, p->DCW, p->DDCW_TALLY, p->DDCW_ADDR, p->DDCW_22_23_TYPE);
2570 // POLTS_TESTING
2571 
2572 
2573 
2574 
2575 
2576 
2577 
2578 
2579 
2580 
2581 
2582 
2583 
2584 
2585 
2586 
2587 
2588 
2589 
2590 
2591       }
2592   }
2593 
2594 static void pack_DCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2595   {
2596     // DCW_18_20_CP is the only field ever changed.
2597     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2598     putbits36_3 ((word36 *) & p -> DCW, 18, p -> DCW_18_20_CP);
2599   }
2600 
2601 static void pack_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2602   {
2603     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2604     putbits36_18 ((word36 *) & p-> LPW,   0, p -> LPW_DCW_PTR);
2605     putbits36_1  ((word36 *) & p-> LPW,  18, p -> LPW_18_RES);
2606     putbits36_1  ((word36 *) & p-> LPW,  19, p -> LPW_19_REL);
2607     putbits36_1  ((word36 *) & p-> LPW,  20, p -> LPW_20_AE);
2608     putbits36_1  ((word36 *) & p-> LPW,  21, p -> LPW_21_NC);
2609     putbits36_1  ((word36 *) & p-> LPW,  22, p -> LPW_22_TAL);
2610     putbits36_1  ((word36 *) & p-> LPW,  23, p -> LPW_23_REL);
2611     putbits36_12 ((word36 *) & p-> LPW,  24, p -> LPW_TALLY);
2612     putbits36_18 ((word36 *) & p-> LPWX,  0, p -> LPWX_BOUND);
2613     putbits36_18 ((word36 *) & p-> LPWX, 18, p -> LPWX_SIZE);
2614   }
2615 
2616 static void fetch_and_parse_PCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2617   {
2618     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2619 
2620     iom_core_read2 (iom_unit_idx, p -> LPW_DCW_PTR, (word36 *) & p -> PCW0, (word36 *) & p -> PCW1, __func__);
2621     p -> PCW_CHAN           = getbits36_6  (p -> PCW1,  3);
2622     p -> PCW_AE             = getbits36_6  (p -> PCW0, 12);
2623     p -> PCW_21_MSK         = getbits36_1  (p -> PCW0, 21);
2624     p -> PCW_PAGE_TABLE_PTR = getbits36_18 (p -> PCW1,  9);
2625     p -> PCW_63_PTP         = getbits36_1  (p -> PCW1, 27);
2626     p -> PCW_64_PGE         = getbits36_1  (p -> PCW1, 28);
2627     p -> PCW_65_AUX         = getbits36_1  (p -> PCW1, 29);
2628     if (p -> PCW_65_AUX)
2629       sim_warn ("PCW_65_AUX\r\n");
2630     p -> DCW = p -> PCW0;
2631     unpack_DCW (iom_unit_idx, chan);
2632   }
2633 
2634 static void fetch_and_parse_DCW (uint iom_unit_idx, uint chan, UNUSED bool read_only)
     /* [previous][next][first][last][top][bottom][index][help] */
2635   {
2636 #if defined(TESTING)
2637     cpu_state_t * cpup = _cpup;
2638 #endif
2639     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2640     word24 addr         = p -> LPW_DCW_PTR & MASK18;
2641 
2642     sim_debug (DBG_DEBUG, & iom_dev, "%s: addr %08o\r\n", __func__, addr);
2643     switch (p -> chanMode)
2644       {
2645         // LPW ABS
2646         case cm1:
2647         case cm1e:
2648           {
2649             iom_core_read (iom_unit_idx, addr, (word36 *) & p -> DCW, __func__);
2650           }
2651           break;
2652 
2653         // LPW EXT
2654         case cm2e:
2655           {
2656             // LPXW_BOUND is mod 2; ie. val is * 2
2657             //addr |= ((word24) p -> LPWX_BOUND << 18);
2658             addr += ((word24) p -> LPWX_BOUND << 1);
2659             iom_core_read (iom_unit_idx, addr, (word36 *) & p -> DCW, __func__);
2660           }
2661           break;
2662 
2663         case cm2:
2664         case cm3b:
2665           {
2666             sim_warn ("fetch_and_parse_DCW LPW paged\r\n");
2667           }
2668           break;
2669 
2670         case cm3a:
2671         case cm4:
2672         case cm5:
2673           {
2674             fetch_LPWPTW (iom_unit_idx, chan);
2675             // Calculate effective address
2676             // PTW 4-17 || LPW 8-17
2677             word24 addr_ = ((word24) (getbits36_14 (p -> PTW_LPW, 4) << 10)) | ((p -> LPW_DCW_PTR) & MASK10);
2678             iom_core_read (iom_unit_idx, addr_, (word36 *) & p -> DCW, __func__);
2679           }
2680           break;
2681       }
2682     unpack_DCW (iom_unit_idx, chan);
2683 
2684     if (IS_IDCW (p))
2685       sim_debug (DBG_DEBUG, & iom_dev,
2686                  "%s: chan %d idcw: dev_cmd %#o dev_code %#o ae %#o ec %d control %#o chan_cmd %#o data %#o\r\n",
2687                  __func__, p -> PCW_CHAN, p -> IDCW_DEV_CMD, p -> IDCW_DEV_CODE, p -> IDCW_AE,
2688                  p -> IDCW_EC, p -> IDCW_CHAN_CTRL, p -> IDCW_CHAN_CMD, p -> IDCW_COUNT);
2689     else
2690       if (p -> DDCW_22_23_TYPE == 02)
2691         sim_debug (DBG_DEBUG, & iom_dev,
2692                    "%s: chan %d tdcw: address %#o seg %d pdta %d pdcw/ec %d res %d rel %d\r\n",
2693                    __func__, p -> PCW_CHAN, p -> TDCW_DATA_ADDRESS, p -> TDCW_31_SEG,
2694                    p -> TDCW_32_PDTA, p -> TDCW_33_PDCW, p -> TDCW_34_RES, p -> TDCW_35_REL);
2695       else
2696         sim_debug (DBG_DEBUG, & iom_dev,
2697                    "%s: chan %d ddcw: address %#o char_pos %#o type %#o tally %#o\r\n",
2698                    __func__, p -> PCW_CHAN, p -> DDCW_ADDR, p -> DCW_18_20_CP,
2699                    p -> DDCW_22_23_TYPE, p -> DDCW_TALLY);
2700   }
2701 
2702 /*
2703  * send_general_interrupt ()
2704  *
2705  * Send an interrupt from the IOM to the CPU.
2706  *
2707  */
2708 
2709 int send_general_interrupt (uint iom_unit_idx, uint chan, enum iomImwPics pic)
     /* [previous][next][first][last][top][bottom][index][help] */
2710   {
2711 #if defined(TESTING)
2712     cpu_state_t * cpup = _cpup;
2713 #endif
2714     uint imw_addr;
2715     uint chan_group    = chan < 32 ? 1 : 0;
2716     uint chan_in_group = chan & 037;
2717 
2718     uint iomUnitNum =
2719       iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & 3u;
2720     uint interrupt_num = iomUnitNum | (chan_group << 2) | ((uint) pic << 3);
2721     // Section 3.2.7 defines the upper bits of the IMW address as
2722     // being defined by the mailbox base address switches and the
2723     // multiplex base address switches.
2724     // However, AN-70 reports that the IMW starts at 01200.  If AN-70 is
2725     // correct, the bits defined by the mailbox base address switches would
2726     // have to always be zero.  We'll go with AN-70.  This is equivalent to
2727     // using bit value 0010100 for the bits defined by the multiplex base
2728     // address switches and zeros for the bits defined by the mailbox base
2729     // address switches.
2730     //imw_addr += 01200;  // all remaining bits
2731     uint pi_base = iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & ~3u;
2732     imw_addr = (pi_base << 3) | interrupt_num;
2733 
2734     sim_debug (DBG_DEBUG, & iom_dev,
2735                "%s: IOM %c, channel %d (%#o), level %d; "
2736                "Interrupt %d (%#o).\r\n",
2737                __func__, iomChar (iom_unit_idx), chan, chan, pic, interrupt_num,
2738                interrupt_num);
2739     word36 imw;
2740     iom_core_read_lock (iom_unit_idx, imw_addr, &imw, __func__);
2741     // The 5 least significant bits of the channel determine a bit to be
2742     // turned on.
2743     //sim_debug (DBG_DEBUG, & iom_dev,
2744                //"%s: IMW at %#o was %012"PRIo64"; setting bit %d\r\n",
2745                //__func__, imw_addr, imw, chan_in_group);
2746     putbits36_1 (& imw, chan_in_group, 1);
2747     iom_core_write_unlock (iom_unit_idx, imw_addr, imw, __func__);
2748     return scu_set_interrupt (iom_unit_data[iom_unit_idx].invokingScuUnitIdx, interrupt_num);
2749   }
2750 
2751 static void iom_fault (uint iom_unit_idx, uint chan, UNUSED const char * who,
     /* [previous][next][first][last][top][bottom][index][help] */
2752                       iomFaultServiceRequest req,
2753                       iomSysFaults_t signal)
2754   {
2755 #if defined(TESTING)
2756     cpu_state_t * cpup = _cpup;
2757 #endif
2758     sim_warn ("iom_fault %s\r\n", who);
2759 
2760     // iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2761     // TODO:
2762     // For a system fault:
2763     // Store the indicated fault into a system fault word (3.2.6) in
2764     // the system fault channel -- use a list service to get a DCW to do so
2765     // For a user fault, use the normal channel status mechanisms
2766 
2767     // sys fault masks channel
2768 
2769     // signal gets put in bits 30..35, but we need fault code for 26..29
2770 
2771     // BUG: mostly unimplemented
2772 
2773     // User Faults: Pg 78: "A user fault is an abnormal conditions that can
2774     // be caused by a user program operating in the slave mode in the
2775     // processor."
2776     //  and
2777     // "User faults can be detected by the IOM Central of by a channel. If
2778     // a user fault is detected by the IOM Central, the fault is indicated to
2779     // the channel, and the channel is responsible for reporting the user
2780     // fault as status is its regular status queue.
2781 
2782     // This code only handles system faults
2783     //
2784 
2785     sim_debug (DBG_DEBUG, & iom_dev,
2786                "%s: chan %d %s req %#o signal %#o\r\n",
2787                __func__, chan, who, req, signal);
2788 
2789     word36 faultWord = 0;
2790     putbits36_9 (& faultWord, 9, (word9) chan);
2791     putbits36_5 (& faultWord, 18, (word5) req);
2792     // IAC, bits 26..29
2793     putbits36_6 (& faultWord, 30, (word6) signal);
2794 
2795     uint chanloc = mbxLoc (iom_unit_idx, IOM_SYSTEM_FAULT_CHAN);
2796 
2797     word36 lpw;
2798     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
2799 
2800     word36 scw;
2801     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
2802 
2803     word36 dcw;
2804     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
2805 
2806     //sim_debug (DBG_DEBUG, & iom_dev,
2807                //"%s: lpw %012"PRIo64" scw %012"PRIo64" dcw %012"PRIo64"\r\n",
2808                //__func__, lpw, scw, dcw);
2809 
2810     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, faultWord, __func__);
2811 
2812     uint tally = dcw & MASK12;
2813     if (tally > 1)
2814       {
2815         dcw -= 01llu;        //-V536  // tally --
2816         dcw += 01000000llu;  //-V536  // addr ++
2817       }
2818     else
2819       dcw = scw; // reset to beginning of queue
2820     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
2821 
2822     send_general_interrupt (iom_unit_idx, IOM_SYSTEM_FAULT_CHAN, imwSystemFaultPic);
2823   }
2824 
2825 //  0 ok
2826 // -1 fault
2827 // There is a path through the code where no DCW is sent (IDCW && LPW_18_RES)
2828 // Does the -1 return cover that?
2829 
2830 int iom_list_service (uint iom_unit_idx, uint chan,
     /* [previous][next][first][last][top][bottom][index][help] */
2831                            bool * ptro, bool * sendp, bool * uffp)
2832   {
2833     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2834 
2835 // initialize
2836 
2837     bool isConnChan = chan == IOM_CONNECT_CHAN;
2838     * ptro          = false; // assume not PTRO
2839     bool uff        = false; // user fault flag
2840     bool send       = false;
2841 
2842 // Figure 4.3.1
2843 
2844 // START
2845 
2846     // FIRST SERVICE?
2847 
2848     if (p -> lsFirst)
2849       {
2850         // PULL LPW AND LPW EXT. FROM CORE MAILBOX
2851 
2852         fetch_and_parse_LPW (iom_unit_idx, chan);
2853         p -> wasTDCW = false;
2854         p -> SEG     = 0; // pat. FIG. 2, step 44
2855       }
2856     // else lpw and lpwx are in chanData;
2857 
2858     // CONNECT CHANNEL?
2859 
2860     if (isConnChan)
2861       { // connect channel
2862 
2863         // LPW 21 (NC), 22 (TAL) is {00, 01, 1x}?
2864 
2865         if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 0)
2866               {
2867                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2868                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2869                           iomIllTalCChnFlt);
2870                 return -1;
2871               }
2872 
2873         if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 1)
2874           { // 01
2875 
2876             // TALLY is {0, 1, >1}?
2877 
2878             if (p -> LPW_TALLY == 0)
2879               {
2880                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2881                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2882                           iomIllTalCChnFlt);
2883                 return -1;
2884               }
2885 
2886             if (p -> LPW_TALLY > 1)
2887               { // 00
2888 
2889                 // 256K OVERFLOW?
2890                 if (p -> LPW_20_AE == 0 &&
2891                     (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2892                     01000000llu)
2893                   {
2894                     iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2895                               p -> lsFirst ? iomFsrFirstList : iomFsrList,
2896                               iom256KFlt);
2897                     return -1;
2898                   }
2899               }
2900             else if (p -> LPW_TALLY == 1)
2901               * ptro = true;
2902           }
2903         else // 1x
2904           * ptro = true;
2905 
2906         // PULL PCW FROM CORE
2907 
2908 // B
2909 
2910         fetch_and_parse_PCW (iom_unit_idx, chan); // fills in DCW*
2911 
2912         // PCW 18-20 == 111?
2913         if (IS_NOT_IDCW (p))
2914           {
2915             iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2916                       p -> lsFirst ? iomFsrFirstList : iomFsrList,
2917                       iomNotPCWFlt);
2918             return -1;
2919           }
2920 
2921         // SELECT CHANNEL
2922 
2923         // chan = p -> PCW_CHAN;
2924 
2925 // detect unused slot as fault
2926 // if (no handler in slot)
2927 //  goto FAULT;
2928 
2929        // SEND PCW TO CHANNEL
2930 
2931         // p -> DCW = p -> DCW;
2932         send = true;
2933 
2934         goto D;
2935       }
2936 
2937     // Not connect channel
2938 
2939     // LPW 21 (NC), 22 (TAL) is {00, 01, 1x}?
2940 
2941     if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 0)
2942       {
2943         // XXX see pat. 46-51 re: SEG
2944         // 256K OVERFLOW?
2945         if (p -> LPW_20_AE == 0 &&
2946             (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2947             01000000llu)
2948           {
2949             iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2950                       p -> lsFirst ? iomFsrFirstList : iomFsrList,
2951                       iom256KFlt);
2952             return -1;
2953           }
2954        }
2955     else if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 1)
2956       { // 01
2957 A:;
2958         // TALLY is {0, 1, >1}?
2959 
2960         // Per the IOM EPS: TAL=0 means "a tally of zero will not cause a
2961         // fault indication".  Only set uff when TAL=1 (tally-zero faults
2962         // enabled).  When TAL=0 (circular DCW lists, e.g. the MGP read
2963         // channel), reaching TALLY=0 after a TDCW wrap is normal and must
2964         // not cause uff - doing so leaves DDCW_ADDR stale from the TDCW
2965         // unpack (= 0) and prevents mgp_cmd(001) from refreshing it.
2966         if (p -> LPW_TALLY == 0 && p -> LPW_22_TAL)
2967           {
2968             sim_warn("%s: setting uff for chan %d because LPW_TALLY was 0. LPW=%llo.\r\n",
2969                      __func__, chan, p -> LPW);
2970             uff = true;
2971           }
2972         else if (p -> LPW_TALLY > 1)
2973           { // 00
2974 
2975             // XXX see pat. 46-51 re: SEG
2976             // 256K OVERFLOW?
2977             if (p -> LPW_20_AE == 0 &&
2978                 (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2979                 01000000llu)
2980               {
2981                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2982                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2983                           iom256KFlt);
2984                 return -1;
2985               }
2986           }
2987       }
2988 
2989     // LPW 20? -- LPW_20 checked by fetch_and_parse_DCW
2990 
2991     // PULL DCW FROM CORE
2992     fetch_and_parse_DCW (iom_unit_idx, chan, false);
2993 
2994 // C
2995 
2996     if (p -> lsFirst)
2997       p -> LPWX_SIZE = p -> LPW_DCW_PTR;
2998 
2999 // pg B16: "If the IOM is paged [yes] and PCW bit 64 is off, LPW bit 23
3000 //          is ignored by the hardware. If bit 64 is set, LPW bit 23 causes
3001 //          the data to become segmented."
3002 // Handled in fetch_and_parse_LPW
3003 
3004     // LPW 23 REL?
3005 
3006     // TDCW ?
3007 
3008     if (IS_TDCW (p))
3009       {
3010         // SECOND TDCW?
3011         if (p -> wasTDCW)
3012           {
3013             sim_warn("%s: setting uff for chan %d because p->wasTDCW was %d\r\n",
3014                      __func__, chan, p->wasTDCW);
3015             uff = true;
3016             goto uffSet;
3017           }
3018         p -> wasTDCW = true;
3019 
3020         // PUT ADDRESS N LPW
3021 
3022         p -> LPW_DCW_PTR = p -> TDCW_DATA_ADDRESS;
3023         // OR TDCW 33, 34, 35 INTO LPW 20, 18, 23
3024         // XXX is 33 bogus? its semantics change based on PCW 64...
3025         // should be okay; pg B21 says that this is correct; implies that the
3026         // semantics are handled in the LPW code. Check...
3027         p -> LPW_20_AE  |= p -> TDCW_33_EC; // TDCW_33_PDCW
3028         p -> LPW_18_RES |= p -> TDCW_34_RES;
3029         p -> LPW_23_REL |= p -> TDCW_35_REL;
3030 
3031 // Pg B21: (TDCW_31_SEG)
3032 // "SEG = This bit furnishes the 19th address bit (MSD) of a TDCW address
3033 //  used for locating the DCW list in a 512 word page table. It will have
3034 //  meaning only in the TDCW where:
3035 //   (a) the DCW list is already paged and the TDCW calls for the
3036 //       DCW [to be] segmented
3037 //  or
3038 //   (b) the data is already segmented and the TDCW calls for the
3039 //       DCW list to be paged
3040 //  or
3041 //   (c) neither data is segmented nor DCW list is paged and the
3042 //       TDCW calls for both.
3043 //  and
3044 //   (d) an auxiliary PTW in not being used.
3045 
3046 //   (a) the DCW list is already paged   --  LPW PAGED: 3b, 4
3047 //       and the TDCW calls for the
3048 //       DCW [list to be] segmented      --  LPW SEG:       5
3049 
3050 //   (b) the data is already segmented   --  DCW SEG:   3a, 4
3051 //       and the TDCW calls for the
3052 //       DCW list to be paged            --  DCW PAGED:  2, 3b
3053 
3054 //   (c) neither data is segmented       -- DCW !SEG     1, 2, 3b
3055 //       nor DCW list is paged           -- LPW !PAGED   1, 2, 3a
3056 //                                       --              1, 2
3057 //       and the TDCW calls for both.    -- DCW SEG & LPW PAGED
3058 //                                                       4
3059 
3060 //put that weird SEG logic in here
3061 
3062         if (p -> TDCW_31_SEG)
3063           sim_warn ("TDCW_31_SEG\r\n");
3064 
3065         update_chan_mode (iom_unit_idx, chan, true);
3066 
3067         // Decrement tally
3068         p->LPW_TALLY = ((word12) (((word12s) p->LPW_TALLY) - 1)) & MASK12;
3069 
3070         pack_LPW (iom_unit_idx, chan);
3071 
3072         // AC CHANGE ERROR? (LPW 18 == 1 && DCW 33 == 1)
3073 
3074         if (p -> LPW_18_RES && p -> TDCW_33_EC) // same as TDCW_33_PDCW
3075           {
3076             sim_warn("%s: setting uff for chan %d because p->LPW_18_RES and p->TDCW_33_EC were set\r\n",
3077                      __func__, chan);
3078             uff = true;
3079             goto uffSet;
3080           }
3081 
3082         goto A;
3083       }
3084     else if (IS_IDCW (p))     // IDCW ?
3085       {
3086         // LPW_18_RES?
3087 
3088         if (p -> LPW_18_RES)
3089           {
3090             // SET USER FAULT FLAG
3091             sim_warn("%s: setting uff for chan %d because LPW_18_RES was %o\r\n",
3092                      __func__, chan, p->LPW_18_RES);
3093             uff = true; // XXX Why? uff isn't not examined later.
3094             // send = false; implicit...
3095           }
3096         else
3097           {
3098             // SEND IDCW TO CHANNEL
3099             // p -> DCW = p -> DCW;
3100             send = true;
3101           }
3102         p -> LPWX_SIZE = p -> LPW_DCW_PTR;
3103         goto D;
3104       }
3105 
3106     p -> wasTDCW = false;
3107     // NOT TDCW
3108 
3109     // CP VIOLATION?
3110 
3111     // 43A239854 3.2.3.3 "The byte size, defined by the channel, determines
3112     // what CP values are valid..."
3113 
3114     // If we get here, the DCW is not a IDCW and not a TDCW, therefore
3115     // it must be a DDCW. If the list service knew the sub-word size
3116     // size of the device, it could check the for valid values. Let
3117     // the device handler do that later.
3118 
3119     // if (cp decrepancy)
3120     //   {
3121     //      user_fault_flag = iomCsCpDiscrepancy;
3122     //      goto user_fault;
3123     //   }
3124     // if (cp violation)
3125     //   {
3126     //     uff = true;
3127     //     goto uffSet;
3128     //   }
3129 
3130     // USER FAULT FLAG SET?
3131 
3132     if (uff)
3133       {
3134 uffSet:;
3135         // PUT 7 into DCW 18-20
3136         p -> DCW_18_20_CP = 07u;
3137         pack_DCW (iom_unit_idx, chan);
3138       }
3139 
3140     // WRITE DCW IN [SCRATCH PAD] MAILBOX
3141 
3142     // p -> DVW  = P -> DCW;
3143 
3144     // SEND DCW TO CHANNEL
3145 
3146     // p -> DCW = p -> DCW;
3147     send = true;
3148 
3149     // goto D;
3150 
3151 D:;
3152 
3153     // SEND FLAGS TO CHANNEL
3154 
3155     * uffp  = uff;
3156     * sendp = send;
3157 
3158     // LPW 21 ?
3159 
3160     if (p -> LPW_21_NC == 0) // UPDATE
3161      {
3162        // UPDATE LPW ADDRESS & TALLY
3163        if (isConnChan)
3164          p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 2u) & MASK18;
3165        else
3166          p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 1u) & MASK18;
3167        // ubsan
3168        p->LPW_TALLY = ((word12) (((word12s) p->LPW_TALLY) - 1)) & MASK12;
3169        pack_LPW (iom_unit_idx, chan);
3170      }
3171 
3172     // IDCW OR FIRST LIST
3173     if (p -> DDCW_22_23_TYPE == 07u || p -> lsFirst)
3174       {
3175         // WRITE LPW & LPW EXT. INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3176         // scratch pad
3177         // p -> lpw = p -> lpw
3178         // core
3179         write_LPW (iom_unit_idx, chan);
3180       }
3181     else
3182       {
3183         if (p -> LPW_21_NC == 0 ||
3184             (IS_TDCW (p)))
3185           {
3186             // WRITE LPW INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3187             // scratch pad
3188             // p -> lpw = p -> lpw
3189             // core
3190             write_LPW (iom_unit_idx, chan);
3191           }
3192       }
3193 
3194     p -> lsFirst = false;
3195     // END
3196 
3197     return 0;
3198   }
3199 
3200 // 0 ok
3201 // -1 uff
3202 static int doPayloadChannel (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
3203 #if defined(TESTING)
3204   cpu_state_t * cpup = _cpup;
3205   sim_debug (DBG_DEBUG, & iom_dev, "%s: Payload channel %c%02o\r\n", __func__, iomChar (iomUnitIdx), chan);
3206 #endif
3207 // A dubious assumption being made is that the device code will always
3208 // be in bits 6-12 of the DCw. Normally, the controller would
3209 // decipher the device code and route to the device, but we
3210 // have elided the controllers and must do that ourselves.
3211 
3212 // Loop logic
3213 //
3214 //   The DCW list can be terminated two ways; either by having the
3215 //   tally run out, or a IDCW with control set to zero.
3216 //
3217 //   The device handler will absorb DDCWs
3218 //
3219 // loop until:
3220 //
3221 //  listService sets ptro, indicating that no more DCWs are available. or
3222 //     control is 0, indicating last IDCW
3223 
3224   iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
3225 
3226 #if defined(TESTING)
3227   word36 PCW_DCW        = p->DCW;
3228   word1  PCW_LPW_23_REL = p->LPW_23_REL;
3229 #endif
3230   p -> chanMode   = cm1;
3231   p -> LPW_18_RES = 0;
3232   p -> LPW_20_AE  = 0;
3233   p -> LPW_23_REL = 0;
3234 
3235   unpack_DCW (iomUnitIdx, chan);
3236 
3237   p->isPCW = true;
3238 
3239   // Masked is set in doConnectChannel
3240   //p->masked = !!p->PCW_21_MSK;
3241   struct iom_to_ctlr_s * d = & cables->iom_to_ctlr[iomUnitIdx][chan];
3242 
3243 // A device command of 051 in the PCW is only meaningful to the operator console;
3244 // all other channels should ignore it. We use (somewhat bogusly) a chanType of
3245 // chanTypeCPI to indicate the operator console.
3246   if (d->chan_type != chan_type_CPI && p -> IDCW_DEV_CMD == 051) {
3247     p->stati = 04501;
3248     send_terminate_interrupt (iomUnitIdx, chan);
3249     return 0;
3250   }
3251 
3252   if ((!d->in_use) || (!d->iom_cmd)) {
3253     p -> stati = 06000; // t, power off/missing
3254 #if defined(POLTS_TESTING)
3255 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3256                         ("// terminate 10. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3257 #endif
3258     goto terminate;
3259   }
3260 
3261 // 3.2.2. "Bits 12-17 [of the PCW] contain the address extension which is maintained by
3262 //         the channel for subsequent use by the IOM in generating a 24-bit
3263 //         address for list of data services for the extended address modes."
3264 // see also 3.2.3.1
3265   p -> ADDR_EXT     = p -> PCW_AE;
3266 
3267   p -> lsFirst      = true;
3268 
3269   p -> tallyResidue = 0;
3270   p -> isRead       = true;
3271   p -> charPos      = 0;
3272 // As far as I can tell, initiate false means that an IOTx succeeded in
3273 // transferring data; assume it didn't since that is the most common
3274 // code path.
3275   p -> initiate     = true;
3276   p -> chanStatus   = chanStatNormal;
3277 
3278 //
3279 // Send the PCW's DCW
3280 //
3281   int rc = d->iom_cmd (iomUnitIdx, chan);
3282 
3283   if (rc < 0) {
3284     p -> dev_code = getbits36_6 (p -> DCW, 6);
3285 #if defined(POLTS_TESTING)
3286 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3287                         sim_printf ("// terminate 9. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3288 #endif
3289     goto terminate;
3290   }
3291 
3292   if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_MARKER) { // IDCW marker bit set
3293     send_marker_interrupt (iomUnitIdx, (int) chan);
3294   }
3295 
3296   if (rc == IOM_CMD_DISCONNECT) {
3297 #if defined(POLTS_TESTING)
3298 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3299                         ("// terminate 8. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3300 #endif
3301     goto terminate;
3302   }
3303 
3304   if (p->masked) {
3305 #if defined(POLTS_TESTING)
3306 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3307                         ("// terminate 7. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3308 #endif
3309     goto terminate;
3310   }
3311 
3312   bool ptro, send, uff;
3313   bool terminate = false;
3314   p->isPCW       = false;
3315 
3316 #if defined(TESTING)
3317   bool first = true;
3318 #endif
3319 
3320   (void)terminate;
3321   bool idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3322   do {
3323     int rc2 = iom_list_service (iomUnitIdx, chan, & ptro, & send, & uff);
3324     if (rc2 < 0) {
3325 // XXX set status flags
3326       sim_warn ("%s list service failed\r\n", __func__);
3327       return -1;
3328     }
3329     if (uff) {
3330       // We get a uff if the LPW tally hit 0
3331 #if defined(POLTS_TESTING)
3332 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3333                         sim_printf ("// terminate 6. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3334 #endif
3335       goto terminate;
3336     }
3337     // List service failed to get a DCW
3338     if (! send) {
3339       sim_warn ("%s nothing to send\r\n", __func__);
3340       return 1;
3341     }
3342 
3343 #if defined(TESTING)
3344 # if defined(POLTS_TESTING)
3345 if (iomUnitIdx == 1 && chan == 020)
3346 # endif
3347     if_sim_debug (DBG_TRACE, & iom_dev) {
3348       if (first) {
3349         first = false;
3350         dumpLPW (iomUnitIdx, chan);
3351         sim_printf ("// DCW list dump: \r\n");
3352         dumpDCW (PCW_DCW, PCW_LPW_23_REL);
3353         for (int i = 0; i < 8; i ++)
3354           dumpDCW (M[p->LPW_DCW_PTR - 1 + i], p->LPW_23_REL);
3355         sim_printf ("// \r\n");
3356       }
3357     }
3358 #endif
3359 
3360 // 3.2.3.1 "If EC - Bit 21 = 1, The channel will replace the present address
3361 //          extension with the new address extension in bits 12-17. ... In
3362 //          Multics and NSA systems, EC is inhibited from the payload channel
3363 //          if LPW bit 23 = 1.
3364 
3365     if (IS_IDCW (p)) { // IDCW
3366       idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3367       if (p -> LPW_23_REL == 0 && p -> IDCW_EC == 1)
3368         p -> ADDR_EXT = getbits36_6 (p -> DCW, 12);
3369 
3370 //      p -> recordResidue = 0;
3371       p -> tallyResidue = 0;
3372       p -> isRead       = true;
3373       p -> charPos      = 0;
3374       p -> chanStatus   = chanStatNormal;
3375     }
3376 
3377 // Send the DCW list's DCW
3378 
3379     rc2 = d->iom_cmd (iomUnitIdx, chan);
3380 
3381     if (rc2 < 0) {
3382       p -> dev_code = getbits36_6 (p -> DCW, 6);
3383 #if defined(POLTS_TESTING)
3384 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3385                         sim_printf ("// terminate 5. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3386 #endif
3387       goto terminate;
3388     }
3389 
3390     if (rc2 == IOM_CMD_DISCONNECT) {
3391       terminate = true;
3392 #if defined(POLTS_TESTING)
3393 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3394                         ("// terminate 4. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3395 #endif
3396     }
3397 
3398     if (rc2 == IOM_CMD_PENDING) // handler still processing command, don't set
3399       goto pending;             // terminate interrupt.
3400 
3401     // If IDCW and terminate and nondata
3402     if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE && p->IDCW_CHAN_CMD == CHAN_CMD_NONDATA) {
3403 #if defined(POLTS_TESTING)
3404 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3405                         sim_printf ("// terminate 1. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3406 #endif
3407       goto terminate;
3408     }
3409     // If IOTD and last IDCW was terminate
3410     if (IS_IOTD (p) && idcw_terminate && rc2 != IOM_CMD_RESIDUE) {
3411 #if defined(POLTS_TESTING)
3412 //if (iomUnitIdx == 1 && chan == 020)      if_sim_debug (DBG_TRACE, & iom_dev)
3413 //                                           sim_printf ("// ctrl == 0 in chan %d (%o) IOTP\r\n", chan, chan);
3414 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3415                         sim_printf ("// terminate 2. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3416 #endif
3417       goto terminate;
3418     }
3419 
3420     // IOM_CMD_RESIDUE: Continue pushing DCWS until the record residue is 0
3421 
3422     if (IS_NOT_IDCW (p) && rc2 == IOM_CMD_RESIDUE) {
3423       if (p->recordResidue)
3424         p->recordResidue --;
3425       if (p->recordResidue == 0)
3426         goto terminate;
3427     }
3428   } while (! terminate);
3429 
3430 terminate:;
3431 #if defined(POLTS_TESTING)
3432 if (iomUnitIdx == 1 && chan == 020) sim_printf ("stati %04o\r\n", p->stati);
3433 #endif
3434   send_terminate_interrupt (iomUnitIdx, chan);
3435   return 0;
3436 
3437 pending:;
3438   return 0;
3439 }
3440 
3441 // doConnectChan ()
3442 //
3443 // Process the "connect channel".  This is what the IOM does when it
3444 // receives a $CON signal.
3445 //
3446 // Only called by iom_interrupt ()
3447 //
3448 // The connect channel requests one or more "list services" and processes the
3449 // resulting PCW control words.
3450 //
3451 
3452 static int doConnectChan (uint iom_unit_idx) {
     /* [previous][next][first][last][top][bottom][index][help] */
3453   // ... the connect channel obtains a list service from the IOM Central.
3454   // During this service the IOM Central will do a double precision read
3455   // from the core, under the control of the LPW for the connect channel.
3456   //
3457   // If the IOM Central does not indicate a PTRO during the list service,
3458   // the connect channel obtains another list service.
3459   //
3460   // The connect channel does not interrupt or store status.
3461   //
3462   // The DCW and SCW mailboxes for the connect channel are not used by
3463   // the IOM.
3464 #if defined(TESTING)
3465   cpu_state_t * cpup = _cpup;
3466 #endif
3467   sim_debug (DBG_DEBUG, & iom_dev, "%s: Connect channel\r\n", __func__);
3468   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][IOM_CONNECT_CHAN];
3469   p -> lsFirst = true;
3470 #if defined(TESTING) && defined(POLTS_TESTING)
3471   bool first = true;
3472 #endif
3473   bool ptro, send, uff;
3474   do {
3475     // Fetch the next PCW
3476     int rc = iom_list_service (iom_unit_idx, IOM_CONNECT_CHAN, & ptro, & send, & uff);
3477     if (rc < 0) {
3478       sim_warn ("connect channel connect failed\r\n");
3479       return -1;
3480     }
3481     if (uff) {
3482       sim_warn ("connect channel ignoring uff\r\n"); // XXX
3483     }
3484     if (! send) {
3485       sim_warn ("connect channel nothing to send\r\n");
3486     } else {
3487 #if defined(xTESTING)
3488       if_sim_debug (DBG_TRACE, & iom_dev) {
3489         if (first) {
3490           first = false;
3491           sim_printf ("// CIOC %lld\r\n", cpu.cycleCnt);
3492           dumpLPW (iom_unit_idx, IOM_CONNECT_CHAN);
3493           sim_printf ("// PCW %012llo %012llo\r\n",    p->PCW0, p->PCW1);
3494           sim_printf ("//   Chan info     %04o\r\n",   p->PCW_CHAN);
3495           sim_printf ("//   Addr ext        %02o\r\n", p->PCW_AE);
3496           sim_printf ("//   111              %o\r\n",  getbits36_3 (p->PCW0, 18));
3497           sim_printf ("//   M                %o\r\n",  p->PCW_21_MSK);
3498           sim_printf ("//   Chan info %08o\r\n",       getbits36_14 (p->PCW0, 22));
3499           sim_printf ("//   Pg Tbl Ptr  %06o\r\n",     p->PCW_PAGE_TABLE_PTR);
3500           sim_printf ("//   PTP              %o\r\n",  p->PCW_63_PTP);
3501           sim_printf ("//   PGE              %o\r\n",  p->PCW_64_PGE);
3502           sim_printf ("//   AUX              %o\r\n",  p->PCW_65_AUX);
3503           sim_printf ("//\r\n");
3504         }
3505       }
3506 #endif
3507       // Copy the PCW's DCW to the payload channel
3508       iom_chan_data_t * q = & iom_chan_data[iom_unit_idx][p -> PCW_CHAN];
3509 
3510       q -> PCW0               = p -> PCW0;
3511       q -> PCW1               = p -> PCW1;
3512       q -> PCW_CHAN           = p -> PCW_CHAN;
3513       q -> PCW_AE             = p -> PCW_AE;
3514       q -> PCW_PAGE_TABLE_PTR = p -> PCW_PAGE_TABLE_PTR;
3515       q -> PCW_63_PTP         = p -> PCW_63_PTP;
3516       q -> PCW_64_PGE         = p -> PCW_64_PGE;
3517       q -> PCW_65_AUX         = p -> PCW_65_AUX;
3518       q -> PCW_21_MSK         = p -> PCW_21_MSK;
3519       q -> DCW                = p -> DCW;
3520 
3521       sim_debug (DBG_DEBUG, & iom_dev, "%s: PCW %012llo %012llo chan %02o\r\n", __func__, q->PCW0, q->PCW1, q->PCW_CHAN);
3522 
3523       q -> masked = p -> PCW_21_MSK;
3524       if (q -> masked) {
3525         if (q -> in_use)
3526           sim_warn ("%s: chan %d masked while in use\r\n", __func__, p -> PCW_CHAN);
3527         q -> in_use = false;
3528         q -> start  = false;
3529       } else {
3530         if (q -> in_use)
3531           sim_warn ("%s: chan %d connect while in use\r\n", __func__, p -> PCW_CHAN);
3532         q -> in_use = true;
3533         q -> start  = true;
3534 #if defined(IO_THREADZ)
3535         setChnConnect (iom_unit_idx, p -> PCW_CHAN);
3536 #else
3537 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3538         doPayloadChannel (iom_unit_idx, p -> PCW_CHAN);
3539 # endif
3540 # if defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3541         pthread_cond_signal (& iomCond);
3542 # endif
3543 #endif
3544       }
3545     }
3546   } while (! ptro);
3547   return 0; // XXX
3548 }
3549 
3550 /*
3551  * send_marker_interrupt ()
3552  *
3553  * Send a "marker" interrupt to the CPU.
3554  *
3555  * Channels send marker interrupts to indicate normal completion of
3556  * a PCW or IDCW if the control field of the PCW/IDCW has a value
3557  * of three.
3558  */
3559 
3560 int send_marker_interrupt (uint iom_unit_idx, int chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3561   {
3562     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3563       return(0);
3564     status_service (iom_unit_idx, (uint) chan, true);
3565     return send_general_interrupt (iom_unit_idx, (uint) chan, imwMarkerPic);
3566   }
3567 
3568 /*
3569  * send_special_interrupt ()
3570  *
3571  * Send a "special" interrupt to the CPU.
3572  *
3573  */
3574 
3575 int send_special_interrupt (uint iom_unit_idx, uint chan, uint devCode,
     /* [previous][next][first][last][top][bottom][index][help] */
3576                             word8 status0, word8 status1)
3577   {
3578 //if (iom_unit_idx == 0 && chan == 013) sim_printf ("spec int %03o %013o\r\n", status0, status1);
3579     uint chanloc = mbxLoc (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN);
3580 
3581     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3582       return(0);
3583 
3584 #if defined(LOCKLESS)
3585     lock_iom();
3586 #endif
3587 
3588 // Multics uses an 12(8) word circular queue, managed by clever manipulation
3589 // of the LPW and DCW.
3590 // Rather then goes through the mechanics of parsing the LPW and DCW,
3591 // we will just assume that everything is set up the way we expect,
3592 // and update the circular queue.
3593     word36 lpw;
3594     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
3595 
3596     word36 scw;
3597     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
3598 
3599     word36 dcw;
3600     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
3601 
3602     word36 status  = 0400000000000ull;
3603     status        |= (((word36) chan)    & MASK6) << 27;
3604     status        |= (((word36) devCode) & MASK8) << 18;
3605     status        |= (((word36) status0) & MASK8) <<  9;
3606     status        |= (((word36) status1) & MASK8) <<  0;
3607     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, status, __func__);
3608 
3609     uint tally = dcw & MASK12;
3610     if (tally > 1)
3611       {
3612         dcw -= 01llu;  // tally --
3613         dcw += 01000000llu; // addr ++
3614       }
3615     else
3616       dcw = scw; // reset to beginning of queue
3617     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
3618 
3619 #if defined(LOCKLESS)
3620     unlock_iom();
3621 #endif
3622 
3623     send_general_interrupt (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN, imwSpecialPic);
3624     return 0;
3625   }
3626 
3627 /*
3628  * send_terminate_interrupt ()
3629  *
3630  * Send a "terminate" interrupt to the CPU.
3631  *
3632  * Channels send a terminate interrupt after doing a status service.
3633  *
3634  */
3635 
3636 int send_terminate_interrupt (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3637   {
3638     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3639       return 0;
3640     status_service (iom_unit_idx, chan, false);
3641     if (iom_chan_data [iom_unit_idx] [chan] . in_use == false)
3642       sim_warn ("%s: chan %d not in use\r\n", __func__, chan);
3643     iom_chan_data [iom_unit_idx] [chan] . in_use = false;
3644     send_general_interrupt (iom_unit_idx, chan, imwTerminatePic);
3645     return 0;
3646   }
3647 
3648 void iom_interrupt (uint scu_unit_idx, uint iom_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
3649   {
3650     cpu_state_t * cpup = _cpup;
3651     sim_debug (DBG_DEBUG, & iom_dev,
3652                "%s: IOM %c starting. [%"PRId64"] %05o:%08o\r\n",
3653                __func__, iomChar (iom_unit_idx), cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC);
3654 
3655     iom_unit_data[iom_unit_idx].invokingScuUnitIdx = scu_unit_idx;
3656 
3657 #if defined(IO_THREADZ)
3658     setIOMInterrupt (iom_unit_idx);
3659     iomDoneWait (iom_unit_idx);
3660 #else
3661     int ret = doConnectChan (iom_unit_idx);
3662 
3663     sim_debug (DBG_DEBUG, & iom_dev,
3664                "%s: IOM %c finished; doConnectChan returned %d.\r\n",
3665                __func__, iomChar (iom_unit_idx), ret);
3666 #endif
3667     // XXX doConnectChan return value ignored
3668   }
3669 
3670 #if defined(IO_THREADZ)
3671 void * chan_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3672   {
3673     uint myid     = (uint) * (int *) arg;
3674     this_iom_idx  = (uint) myid / MAX_CHANNELS;
3675     this_chan_num = (uint) myid % MAX_CHANNELS;
3676 
3677 // Set CPU context to allow sim_debug to work
3678 
3679     set_cpu_idx (0);
3680 
3681     sim_msg ("\rIOM %c Channel %u thread created\r\n", this_iom_idx + 'a', this_chan_num);
3682 # if defined(TESTING)
3683     printPtid(pthread_self());
3684 # endif /* if defined(TESTING) */
3685     if (realtime_ok) {
3686       set_realtime_priority (pthread_self(), realtime_max_priority() - 1);
3687       check_realtime_priority (pthread_self(), realtime_max_priority() - 1);
3688     } else {
3689 # if !defined(__QNX__)
3690       (void)sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
3691 # endif
3692     }
3693 
3694     setSignals ();
3695     while (1)
3696       {
3697 //sim_printf("IOM %c Channel %u thread waiting\r\n", this_iom_idx + 'a', this_chan_num);
3698         chnConnectWait ();
3699 //sim_printf("IOM %c Channel %u thread running\r\n", this_iom_idx + 'a', this_chan_num);
3700         doPayloadChannel (this_iom_idx, this_chan_num);
3701         chnConnectDone ();
3702       }
3703   }
3704 
3705 void * iom_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3706   {
3707     int myid = * (int *) arg;
3708     this_iom_idx = (uint) myid;
3709 
3710 // Set CPU context to allow sim_debug to work
3711 
3712     set_cpu_idx (0);
3713 
3714     sim_printf("IOM %c thread created\r\n", 'a' + myid);
3715 
3716     setSignals ();
3717     while (1)
3718       {
3719 //sim_printf("IOM %c thread waiting\r\n", 'a' + myid);
3720         iomInterruptWait ();
3721 //sim_printf("IOM %c thread running\r\n", 'a' + myid);
3722         int ret = doConnectChan (this_iom_idx);
3723 
3724         sim_debug (DBG_DEBUG, & iom_dev,
3725                    "%s: IOM %c finished; doConnectChan returned %d.\r\n",
3726                    __func__, iomChar (myid), ret);
3727         iomInterruptDone ();
3728       }
3729   }
3730 #endif
3731 
3732 /*
3733  * iom_init ()
3734  *
3735  *  Once-only initialization
3736  */
3737 
3738 void iom_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3739   {
3740     //sim_debug (DBG_INFO, & iom_dev, "%s: running.\r\n", __func__);
3741   }
3742 
3743 #if defined(PANEL68)
3744 void do_boot (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3745   {
3746     boot_svc (& boot_channel_unit[0]);
3747   }
3748 #endif
3749 
3750 #if defined(IO_ASYNC_PAYLOAD_CHAN) || defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3751 void iomProcess (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3752   {
3753     for (uint i = 0; i < N_IOM_UNITS_MAX; i++)
3754       for (uint j = 0; j < MAX_CHANNELS; j++)
3755         {
3756           iom_chan_data_t * p = &iom_chan_data [i] [j];
3757           if (p -> start)
3758             {
3759               p -> start = false;
3760               doPayloadChannel (i, j);
3761             }
3762         }
3763   }
3764 #endif
3765 
3766 char iomChar (uint iomUnitIdx)
     /* [previous][next][first][last][top][bottom][index][help] */
3767   {
3768     return (iom_unit_data[iomUnitIdx].configSwMultiplexBaseAddress & 3) + 'A';
3769   }

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