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. tdcw_chan_mode
  31. update_chan_mode
  32. write_LPW
  33. iom_advance_lpw
  34. iom_continue_channel
  35. dumpDCW
  36. dumpLPW
  37. fetch_and_parse_LPW
  38. unpack_DCW
  39. pack_DCW
  40. pack_LPW
  41. fetch_and_parse_PCW
  42. fetch_and_parse_DCW
  43. peek_list_word
  44. iom_peek_next_dcw
  45. send_general_interrupt
  46. iom_fault
  47. iom_list_service
  48. doPayloadChannel
  49. doConnectChan
  50. send_marker_interrupt
  51. send_special_interrupt
  52. send_terminate_interrupt
  53. iom_interrupt
  54. chan_thread_main
  55. iom_thread_main
  56. iom_init
  57. do_boot
  58. iomProcess
  59. 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 // The channel mode after a TDCW with the given PDTA, PDCW/EC and REL bits
2077 // (TDCW bits 32, 33, 35), from mode 'mode'.  Shared by update_chan_mode
2078 // and iom_peek_next_dcw.
2079 
2080 static int tdcw_chan_mode (int mode, word1 pdta, word1 pdcw, word1 rel)
     /* [previous][next][first][last][top][bottom][index][help] */
2081   {
2082     switch (mode)
2083       {
2084         case cm1:
2085           if (pdta)
2086             {
2087               mode = cm2;
2088               break;
2089             }
2090           if (pdcw)
2091             if (rel)
2092               mode = cm4; // 33, 35
2093             else
2094               mode = cm3b; // 33, !35
2095           else
2096             if (rel)
2097               mode = cm3a; // !33, 35
2098             else
2099               mode = cm2; // !33, !35
2100           break;
2101 
2102         case cm2:
2103           if (pdcw)
2104             if (rel)
2105               mode = cm4; // 33, 35
2106             else
2107               mode = cm3b; // 33, !35
2108           else
2109             if (rel)
2110               mode = cm3a; // !33, 35
2111             else
2112               mode = cm2; // !33, !35
2113           break;
2114 
2115         case cm3a:
2116           if (pdcw)
2117             mode = cm4;
2118           break;
2119 
2120         case cm3b:
2121           if (rel)
2122             mode = cm4;
2123           break;
2124 
2125         case cm4:
2126           mode = cm5;
2127           break;
2128 
2129         case cm5:
2130           break;
2131 
2132         case cm1e:
2133           {
2134             if (pdcw)
2135               mode = cm2e;
2136           }
2137           break;
2138 
2139         case cm2e:
2140           break;
2141       } // switch
2142     return mode;
2143   }
2144 
2145 static void update_chan_mode (uint iom_unit_idx, uint chan, bool tdcw)
     /* [previous][next][first][last][top][bottom][index][help] */
2146   {
2147     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2148     if (chan == IOM_CONNECT_CHAN)
2149       {
2150         p -> chanMode = cm1;
2151         return;
2152       }
2153 
2154     if (! tdcw)
2155       {
2156         if (p -> PCW_64_PGE == 0)
2157           {
2158             if (p -> LPW_20_AE == 0)
2159               {
2160                     p -> chanMode = cm1e;  // AE 0
2161               }
2162             else
2163               {
2164                     p -> chanMode = cm2e;  // AE 1
2165               }
2166 
2167           }
2168         else
2169           {
2170             if (p -> LPW_20_AE == 0)
2171               {
2172                 if (p -> LPW_23_REL == 0)
2173                   {
2174                     p -> chanMode = cm1;  // AE 0, REL 0
2175                   }
2176                 else
2177                   {
2178                     p -> chanMode = cm3a;  // AE 0, REL 1
2179                   }
2180 
2181               }
2182             else  // AE 1
2183               {
2184                 if (p -> LPW_23_REL == 0)
2185                   {
2186                     p -> chanMode = cm3b;  // AE 1, REL 0
2187                   }
2188                 else
2189                   {
2190                     p -> chanMode = cm4;  // AE 1, REL 1
2191                   }
2192 
2193               }
2194           }
2195       }
2196     else // tdcw
2197       {
2198         p -> chanMode = tdcw_chan_mode (p -> chanMode, p -> TDCW_32_PDTA,
2199                                         p -> TDCW_33_PDCW, p -> TDCW_35_REL);
2200       } // tdcw
2201   }
2202 
2203 static void write_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2204   {
2205     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2206 
2207     uint chanLoc = mbxLoc (iom_unit_idx, chan);
2208     iom_core_write (iom_unit_idx, chanLoc + IOM_MBX_LPW, p -> LPW, __func__);
2209     //sim_debug (DBG_DEBUG, & iom_dev,
2210                //"%s: chan %d lpw %012"PRIo64"\r\n",
2211                //__func__, chan, p -> LPW);
2212     if (chan != IOM_CONNECT_CHAN)
2213       {
2214         iom_core_write (iom_unit_idx, chanLoc + IOM_MBX_LPWX, p -> LPWX, __func__);
2215       }
2216   }
2217 
2218 /*
2219  * iom_advance_lpw ()
2220  *
2221  * Advance the channel LPW by one word and write it back to the channel
2222  * mailbox.  Called by the MGP device after delivering async packet data
2223  * via iom_indirect_data_service(), so that io_manager$get_status returns
2224  * next_lpw_offset = LPW_DCW_PTR + 1 instead of LPW_DCW_PTR.  This causes
2225  * ioi_masked to compute:
2226  *   auto_istat.offset = next_lpw_offset - 1 = old LPW_DCW_PTR
2227  * which (after divide-by-2) gives stop_buffer_number = buffer_number + 1,
2228  * allowing mgp_read_dcm_'s processing loop to fire.
2229  */
2230 void iom_advance_lpw (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2231   {
2232     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2233     p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 1u) & MASK18;
2234     putbits36_18 ((word36 *) & p -> LPW, 0, p -> LPW_DCW_PTR);
2235     write_LPW (iom_unit_idx, chan);
2236   }
2237 
2238 /*
2239  * iom_continue_channel ()
2240  *
2241  * Continue a payload channel that is currently in IOM_CMD_PENDING state
2242  * after the device has written data to the current workspace buffer.
2243  *
2244  * This is called by the MGP device after writing an incoming packet to the
2245  * current DCM workspace buffer[N].  It replicates one iteration of
2246  * doPayloadChannel's DCW list loop:
2247  *
2248  *   1. iom_list_service fetches IDCW[N+1] from the DCW list (advancing
2249  *      LPW_DCW_PTR to point to DDCW[N+1]).
2250  *
2251  *   2. d->iom_cmd dispatches IDCW[N+1] to the device handler.  For the
2252  *      MGP read command (cmd=001), the handler calls get_ddcw() which
2253  *      calls iom_list_service() again to fetch DDCW[N+1], setting
2254  *      DDCW_ADDR to workspace buffer[N+1] for the next incoming packet.
2255  *      LPW_DCW_PTR is then at IDCW[N+2].
2256  *
2257  *   3. send_marker_interrupt() signals Multics that buffer[N] is ready.
2258  *      mgp_read_dcm_ computes:
2259  *        stat.offset  = LPW_DCW_PTR_offset - 1 = 2*(N+2) - 1 = 2N+3
2260  *        stop_buffer  = divide(stat.offset, 2)  = N+1
2261  *      Since the DCM's buffer_number is N, the processing loop fires once,
2262  *      consuming buffer[N], then increments buffer_number to N+1.
2263  *
2264  * Because start_io sees running=true (marker interrupt), it does NOT call
2265  * ioi_$connect; the channel remains pending with DDCW_ADDR pointing to
2266  * buffer[N+1], ready for the next packet delivery.
2267  *
2268  * TDCW wrap-around is handled transparently by iom_list_service.
2269  */
2270 int iom_continue_channel (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2271   {
2272     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2273     struct iom_to_ctlr_s * d = & cables->iom_to_ctlr[iom_unit_idx][chan];
2274 
2275     if ((! d->in_use) || (! d->iom_cmd))
2276       {
2277         sim_warn ("%s: channel %d not in use\r\n", __func__, chan);
2278         return -1;
2279       }
2280 
2281     // Fetch the next IDCW (for buffer[N+1]) from the DCW list.
2282     // iom_list_service reads the DCW at LPW_DCW_PTR and advances the pointer.
2283     bool ptro, send, uff;
2284     int rc = iom_list_service (iom_unit_idx, chan, & ptro, & send, & uff);
2285     if (rc < 0)
2286       {
2287         sim_warn ("%s: list service failed for channel %d\r\n", __func__, chan);
2288         return -2;
2289       }
2290 
2291     // uff=true means the IOM tally ran out or a similar status condition was
2292     // flagged.  However, iom_list_service still fetches the correct IDCW and
2293     // sets send=true in all cases where the DCW dispatch is valid.  The uff
2294     // bit is a status indicator for the program's status queue, not a signal
2295     // that the DCW is unusable.  When send=false the !send check below will
2296     // abort; when send=true we should proceed.  Just log and continue.
2297     if (uff)
2298       {
2299         sim_warn ("%s: iom_list_service returned uff=%o for channel %d (proceeding)\r\n",
2300                   __func__, uff, chan);
2301       }
2302 
2303     if (! send)
2304       {
2305         sim_warn ("%s: iom_list_service returned send=%o for channel %d\r\n", __func__, send, chan);
2306         return -4;
2307       }
2308 
2309     // We expect an IDCW here (the marker+continue IDCW for the next buffer).
2310     // If we got a TDCW, iom_list_service already followed it and the DCW it
2311     // found after the transfer is what we have now.
2312     if (! IS_IDCW (p))
2313       {
2314         sim_warn ("%s: expected IDCW, got non-IDCW for channel %d\r\n",
2315                   __func__, chan);
2316         return -5;
2317       }
2318 
2319     // Set up channel state for an in-loop IDCW (not a PCW).
2320     p -> isPCW       = false;
2321     p -> tallyResidue = 0;
2322     p -> isRead      = true;
2323     p -> charPos     = 0;
2324     p -> chanStatus  = chanStatNormal;
2325 
2326     // Dispatch the IDCW to the device handler.  For MGP cmd=001 (read),
2327     // this calls get_ddcw() which fetches DDCW[N+1] and sets DDCW_ADDR to
2328     // workspace buffer[N+1].  The handler returns IOM_CMD_PENDING to keep
2329     // the channel active.
2330     int rc2 = d->iom_cmd (iom_unit_idx, chan);
2331 
2332     if (rc2 < 0)
2333       {
2334         sim_warn ("%s: iom_cmd failed for channel %d\r\n", __func__, chan);
2335         // we'll send a terminate interrupt in mgp_process_event
2336         return -6;
2337       }
2338 
2339     if (rc2 == IOM_CMD_DISCONNECT)
2340       {
2341         // we'll send a terminate interrupt in mgp_process_event
2342         return -7;
2343       }
2344 
2345     // rc2 == IOM_CMD_PENDING: DDCW_ADDR now points to buffer[N+1].
2346     // Send marker interrupt so mgp_read_dcm_ processes buffer[N].
2347     send_marker_interrupt (iom_unit_idx, (int) chan);
2348     return 0;
2349   }
2350 
2351 #if defined(TESTING)
2352 void dumpDCW (word36 DCW, word1 LPW_23_REL) {
     /* [previous][next][first][last][top][bottom][index][help] */
2353   static char * charCtrls[4] =
2354     { "terminate", "undefined", "proceed", "marker" };
2355   static char * chanCmds[16] =
2356     { "record",    "undefined", "nondata",     "undefined",
2357       "undefined", "undefined", "multirecord", "undefined",
2358       "character", "undefined", "undefined",   "undefined",
2359       "undefined", "undefined", "undefined",   "undefined"
2360     };
2361   word3 DCW_18_20_CP =      getbits36_3 (DCW, 18);
2362 
2363   if (DCW_18_20_CP == 07) { // IDCW
2364     word6 IDCW_DEV_CMD  =     getbits36_6 (DCW,  0);
2365     word6 IDCW_DEV_CODE =     getbits36_6 (DCW,  6);
2366  /* word6 IDCW_AE =           getbits36_6 (DCW,  12);
2367   * word1 IDCW_EC;
2368   * if (LPW_23_REL)
2369   *   IDCW_EC = 0;
2370   * else
2371   *   IDCW_EC =               getbits36_1 (DCW, 21); */
2372     word2 IDCW_CHAN_CTRL =    getbits36_2 (DCW, 22);
2373     word6 IDCW_CHAN_CMD  =    getbits36_6 (DCW, 24);
2374     word6 IDCW_COUNT     =    getbits36_6 (DCW, 30);
2375     sim_printf ("//   IDCW %012llo\r\n", DCW);
2376     sim_printf ("//     cmd             %02o %s\r\n", IDCW_DEV_CMD, cmdNames[IDCW_DEV_CMD]);
2377     sim_printf ("//     dev code        %02o\r\n", IDCW_DEV_CODE);
2378     sim_printf ("//     ctrl             %o (%s)\r\n", IDCW_CHAN_CTRL, charCtrls[IDCW_CHAN_CTRL]);
2379     sim_printf ("//     chancmd          %o (%s)\r\n", IDCW_CHAN_CMD, IDCW_CHAN_CMD < 16 ? chanCmds[IDCW_CHAN_CMD] : "unknown");
2380     sim_printf ("//     count            %o\r\n", IDCW_COUNT);
2381   } else { // TDCW or DDCW
2382     word18 TDCW_DATA_ADDRESS = getbits36_18 (DCW,  0);
2383     word1  TDCW_31_SEG       = getbits36_1  (DCW, 31);
2384     word1  TDCW_32_PDTA      = getbits36_1  (DCW, 32);
2385     word1  TDCW_33_PDCW      = getbits36_1  (DCW, 33);
2386     word1  TDCW_33_EC        = getbits36_1  (DCW, 33);
2387     word1  TDCW_34_RES       = getbits36_1  (DCW, 34);
2388     word1  TDCW_35_REL       = getbits36_1  (DCW, 35);
2389 
2390     word12 DDCW_TALLY        = getbits36_12 (DCW, 24);
2391     word18 DDCW_ADDR         = getbits36_18 (DCW,  0);
2392     word2  DDCW_22_23_TYPE   = getbits36_2  (DCW, 22);
2393     static char * types[4]   = { "IOTD", "IOTP", "TDCW", "IONTP" };
2394     if (DDCW_22_23_TYPE == 2) {
2395       sim_printf ("//   TDCW (2) %012llo\r\n", DCW);
2396       sim_printf ("//     dcw ptr   %06o\r\n", TDCW_DATA_ADDRESS);
2397       sim_printf ("//     seg            %o\r\n", TDCW_31_SEG);
2398       sim_printf ("//     pdta           %o\r\n", TDCW_32_PDTA);
2399       sim_printf ("//     pdcw           %o\r\n", TDCW_33_PDCW);
2400       sim_printf ("//     ec             %o\r\n", TDCW_33_EC);
2401       sim_printf ("//     res            %o\r\n", TDCW_34_RES);
2402       sim_printf ("//     rel            %o\r\n", TDCW_35_REL);
2403     } else {
2404       sim_printf ("//   %s (%o) %012llo\r\n", types[DDCW_22_23_TYPE], DDCW_22_23_TYPE, DCW);
2405       sim_printf ("//     tally       %04o\r\n", DDCW_TALLY);
2406       sim_printf ("//     addr            %02o\r\n", DDCW_ADDR);
2407       sim_printf ("//     cp               %o\r\n", getbits36_3 (DCW, 18));
2408     }
2409   }
2410 }
2411 
2412 static void dumpLPW (uint iom_unit_idx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
2413   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2414   char updateCase;
2415   if (p->LPW_21_NC)
2416     updateCase = 'c';
2417   else if (p->LPW_22_TAL)
2418     updateCase = 'b';
2419   else
2420     updateCase = 'a';
2421   static char * chanName[64] = {
2422         "0.",           //  0.   0
2423         "1.",           //  1.   1
2424         "Connect",      //  2.   2
2425         "3.",           //  3.   3
2426         "4.",           //  4.   4
2427         "5.",           //  5.   5
2428         "6.",           //  6.   6
2429         "7.",           //  7.   7
2430         "8.",           //  8.  10
2431         "9.",           //  9.  11
2432         "MTP Tape",     // 10.  12
2433         "IPC Disk",     // 11.  13
2434         "MPC Disk",     // 12.  14
2435         "Card reader",  // 13.  15
2436         "Card punch",   // 14.  16
2437         "Printer",      // 15.  17
2438         "FNP",          // 16.  20
2439         "FNP",          // 17.  21
2440         "FNP",          // 18.  22
2441         "FNP",          // 19.  23
2442         "FNP",          // 20.  24
2443         "FNP",          // 21.  25
2444         "FNP",          // 22.  26
2445         "FNP",          // 23.  27
2446         "24.",          // 24.  30
2447         "25.",          // 25.  31
2448         "ABSI",         // 26.  32
2449         "27.",          // 27.  33
2450         "28.",          // 28.  34
2451         "29.",          // 29.  35
2452         "Console",      // 30.  36
2453         "31.",          // 31.  37
2454         "Socket",       // 32.  40
2455         "Socket",       // 33.  41
2456         "Socket",       // 34.  42
2457         "Socket",       // 35.  43
2458         "Socket",       // 36.  44
2459         "Socket",       // 37.  45
2460         "Socket",       // 38.  46
2461         "39.",
2462         "40.",
2463         "41.",
2464         "42.",
2465         "43.",
2466         "44.",
2467         "45.",
2468         "46.",
2469         "47.",
2470         "48.",
2471         "49.",
2472         "50.",
2473         "51.",
2474         "52.",
2475         "53.",
2476         "54.",
2477         "55.",
2478         "56.",
2479         "57.",
2480         "58.",
2481         "59.",
2482         "60.",
2483         "61.",
2484         "62.",
2485         "63."
2486   };
2487   sim_printf ("// %s channel LPW %012llo (case %c)\r\n", chanName[chan], p->LPW, updateCase);
2488   sim_printf ("//   DCW pointer %06o\r\n",      p->LPW_DCW_PTR);
2489   sim_printf ("//     RES              %o\r\n", p->LPW_18_RES);
2490   sim_printf ("//     REL              %o\r\n", p->LPW_19_REL);
2491   sim_printf ("//     AE               %o\r\n", p->LPW_20_AE);
2492   sim_printf ("//     NC 21            %o\r\n", p->LPW_21_NC);
2493   sim_printf ("//     TAL 22           %o\r\n", p->LPW_22_TAL);
2494   sim_printf ("//     REL              %o\r\n", p->LPW_23_REL);
2495   sim_printf ("//     TALLY         %04o\r\n",  p->LPW_TALLY);
2496   sim_printf ("// \r\n");
2497 }
2498 #endif
2499 
2500 static void fetch_and_parse_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2501   {
2502 #if defined(TESTING)
2503     cpu_state_t * cpup = _cpup;
2504 #endif
2505     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2506     uint chanLoc        = mbxLoc (iom_unit_idx, chan);
2507 
2508     iom_core_read (iom_unit_idx, chanLoc + IOM_MBX_LPW, (word36 *) & p -> LPW, __func__);
2509     sim_debug (DBG_DEBUG, & iom_dev,
2510                "%s: lpw %08o %012"PRIo64"\r\n", __func__, chanLoc + IOM_MBX_LPW, p->LPW);
2511 
2512     p -> LPW_DCW_PTR = getbits36_18 (p -> LPW,  0);
2513     p -> LPW_18_RES  = getbits36_1  (p -> LPW, 18);
2514     p -> LPW_19_REL  = getbits36_1  (p -> LPW, 19);
2515     p -> LPW_20_AE   = getbits36_1  (p -> LPW, 20);
2516     p -> LPW_21_NC   = getbits36_1  (p -> LPW, 21);
2517     p -> LPW_22_TAL  = getbits36_1  (p -> LPW, 22);
2518     p -> LPW_23_REL  = getbits36_1  (p -> LPW, 23);
2519     p -> LPW_TALLY   = getbits36_12 (p -> LPW, 24);
2520 
2521     if (chan == IOM_CONNECT_CHAN)
2522       {
2523         p -> LPWX = 0;
2524         p -> LPWX_BOUND = 0;
2525         p -> LPWX_SIZE = 0;
2526       }
2527     else
2528       {
2529         iom_core_read (iom_unit_idx, chanLoc + IOM_MBX_LPWX, (word36 *) & p -> LPWX, __func__);
2530         p -> LPWX_BOUND = getbits36_18 (p -> LPWX, 0);
2531         p -> LPWX_SIZE  = getbits36_18 (p -> LPWX, 18);
2532       }
2533     update_chan_mode (iom_unit_idx, chan, false);
2534   }
2535 
2536 static void unpack_DCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2537   {
2538 #if defined(TESTING)
2539     cpu_state_t * cpup = _cpup;
2540 #endif
2541     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2542     p -> DCW_18_20_CP =      getbits36_3 (p -> DCW, 18);
2543 
2544     if (IS_IDCW (p)) // IDCW
2545       {
2546         p -> IDCW_DEV_CMD   = getbits36_6 (p -> DCW, 0);
2547         p -> IDCW_DEV_CODE  = getbits36_6 (p -> DCW, 6);
2548         p -> IDCW_AE        = getbits36_6 (p -> DCW, 12);
2549         if (p -> LPW_23_REL)
2550           p -> IDCW_EC      = 0;
2551         else
2552           p -> IDCW_EC      = getbits36_1 (p -> DCW, 21);
2553         if (p -> IDCW_EC)
2554           p -> SEG          = 1; // pat. step 45
2555         p -> IDCW_CHAN_CTRL = getbits36_2 (p -> DCW, 22);
2556         p -> IDCW_CHAN_CMD  = getbits36_6 (p -> DCW, 24);
2557         p -> IDCW_COUNT     = getbits36_6 (p -> DCW, 30);
2558         p->recordResidue = p->IDCW_COUNT;
2559 #if defined(TESTING)
2560         sim_debug (DBG_DEBUG, & iom_dev,
2561                    "%s: IDCW %012llo cmd %02o (%s) dev %02o ctrl %o chancmd %o\r\n",
2562                    __func__, p->DCW, p->IDCW_DEV_CMD, cmdNames[p->IDCW_DEV_CMD],
2563                    p->IDCW_DEV_CODE, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
2564 #endif
2565       }
2566     else // TDCW or DDCW
2567       {
2568         p -> TDCW_DATA_ADDRESS = getbits36_18 (p -> DCW,  0);
2569         p -> TDCW_31_SEG       = getbits36_1  (p -> DCW, 31);
2570         p -> TDCW_32_PDTA      = getbits36_1  (p -> DCW, 32);
2571         p -> TDCW_33_PDCW      = getbits36_1  (p -> DCW, 33);
2572         p -> TDCW_33_EC        = getbits36_1  (p -> DCW, 33);
2573         p -> TDCW_34_RES       = getbits36_1  (p -> DCW, 34);
2574         p -> TDCW_35_REL       = getbits36_1  (p -> DCW, 35);
2575         p -> DDCW_TALLY        = getbits36_12 (p -> DCW, 24);
2576         p -> DDCW_ADDR         = getbits36_18 (p -> DCW,  0);
2577         p -> DDCW_22_23_TYPE   = getbits36_2  (p -> DCW, 22);
2578         sim_debug (DBG_DEBUG, & iom_dev,
2579                    "%s: TDCW/DDCW %012llo tally %04o addr %06o type %o\r\n",
2580                    __func__, p->DCW, p->DDCW_TALLY, p->DDCW_ADDR, p->DDCW_22_23_TYPE);
2581 // POLTS_TESTING
2582 
2583 
2584 
2585 
2586 
2587 
2588 
2589 
2590 
2591 
2592 
2593 
2594 
2595 
2596 
2597 
2598 
2599 
2600 
2601 
2602       }
2603   }
2604 
2605 static void pack_DCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2606   {
2607     // DCW_18_20_CP is the only field ever changed.
2608     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2609     putbits36_3 ((word36 *) & p -> DCW, 18, p -> DCW_18_20_CP);
2610   }
2611 
2612 static void pack_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2613   {
2614     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2615     putbits36_18 ((word36 *) & p-> LPW,   0, p -> LPW_DCW_PTR);
2616     putbits36_1  ((word36 *) & p-> LPW,  18, p -> LPW_18_RES);
2617     putbits36_1  ((word36 *) & p-> LPW,  19, p -> LPW_19_REL);
2618     putbits36_1  ((word36 *) & p-> LPW,  20, p -> LPW_20_AE);
2619     putbits36_1  ((word36 *) & p-> LPW,  21, p -> LPW_21_NC);
2620     putbits36_1  ((word36 *) & p-> LPW,  22, p -> LPW_22_TAL);
2621     putbits36_1  ((word36 *) & p-> LPW,  23, p -> LPW_23_REL);
2622     putbits36_12 ((word36 *) & p-> LPW,  24, p -> LPW_TALLY);
2623     putbits36_18 ((word36 *) & p-> LPWX,  0, p -> LPWX_BOUND);
2624     putbits36_18 ((word36 *) & p-> LPWX, 18, p -> LPWX_SIZE);
2625   }
2626 
2627 static void fetch_and_parse_PCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2628   {
2629     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2630 
2631     iom_core_read2 (iom_unit_idx, p -> LPW_DCW_PTR, (word36 *) & p -> PCW0, (word36 *) & p -> PCW1, __func__);
2632     p -> PCW_CHAN           = getbits36_6  (p -> PCW1,  3);
2633     p -> PCW_AE             = getbits36_6  (p -> PCW0, 12);
2634     p -> PCW_21_MSK         = getbits36_1  (p -> PCW0, 21);
2635     p -> PCW_PAGE_TABLE_PTR = getbits36_18 (p -> PCW1,  9);
2636     p -> PCW_63_PTP         = getbits36_1  (p -> PCW1, 27);
2637     p -> PCW_64_PGE         = getbits36_1  (p -> PCW1, 28);
2638     p -> PCW_65_AUX         = getbits36_1  (p -> PCW1, 29);
2639     if (p -> PCW_65_AUX)
2640       sim_warn ("PCW_65_AUX\r\n");
2641     p -> DCW = p -> PCW0;
2642     unpack_DCW (iom_unit_idx, chan);
2643   }
2644 
2645 static void fetch_and_parse_DCW (uint iom_unit_idx, uint chan, UNUSED bool read_only)
     /* [previous][next][first][last][top][bottom][index][help] */
2646   {
2647 #if defined(TESTING)
2648     cpu_state_t * cpup = _cpup;
2649 #endif
2650     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2651     word24 addr         = p -> LPW_DCW_PTR & MASK18;
2652 
2653     sim_debug (DBG_DEBUG, & iom_dev, "%s: addr %08o\r\n", __func__, addr);
2654     switch (p -> chanMode)
2655       {
2656         // LPW ABS
2657         case cm1:
2658         case cm1e:
2659           {
2660             iom_core_read (iom_unit_idx, addr, (word36 *) & p -> DCW, __func__);
2661           }
2662           break;
2663 
2664         // LPW EXT
2665         case cm2e:
2666           {
2667             // LPXW_BOUND is mod 2; ie. val is * 2
2668             //addr |= ((word24) p -> LPWX_BOUND << 18);
2669             addr += ((word24) p -> LPWX_BOUND << 1);
2670             iom_core_read (iom_unit_idx, addr, (word36 *) & p -> DCW, __func__);
2671           }
2672           break;
2673 
2674         case cm2:
2675         case cm3b:
2676           {
2677             sim_warn ("fetch_and_parse_DCW LPW paged\r\n");
2678           }
2679           break;
2680 
2681         case cm3a:
2682         case cm4:
2683         case cm5:
2684           {
2685             fetch_LPWPTW (iom_unit_idx, chan);
2686             // Calculate effective address
2687             // PTW 4-17 || LPW 8-17
2688             word24 addr_ = ((word24) (getbits36_14 (p -> PTW_LPW, 4) << 10)) | ((p -> LPW_DCW_PTR) & MASK10);
2689             iom_core_read (iom_unit_idx, addr_, (word36 *) & p -> DCW, __func__);
2690           }
2691           break;
2692       }
2693     unpack_DCW (iom_unit_idx, chan);
2694 
2695     if (IS_IDCW (p))
2696       sim_debug (DBG_DEBUG, & iom_dev,
2697                  "%s: chan %d idcw: dev_cmd %#o dev_code %#o ae %#o ec %d control %#o chan_cmd %#o data %#o\r\n",
2698                  __func__, p -> PCW_CHAN, p -> IDCW_DEV_CMD, p -> IDCW_DEV_CODE, p -> IDCW_AE,
2699                  p -> IDCW_EC, p -> IDCW_CHAN_CTRL, p -> IDCW_CHAN_CMD, p -> IDCW_COUNT);
2700     else
2701       if (p -> DDCW_22_23_TYPE == 02)
2702         sim_debug (DBG_DEBUG, & iom_dev,
2703                    "%s: chan %d tdcw: address %#o seg %d pdta %d pdcw/ec %d res %d rel %d\r\n",
2704                    __func__, p -> PCW_CHAN, p -> TDCW_DATA_ADDRESS, p -> TDCW_31_SEG,
2705                    p -> TDCW_32_PDTA, p -> TDCW_33_PDCW, p -> TDCW_34_RES, p -> TDCW_35_REL);
2706       else
2707         sim_debug (DBG_DEBUG, & iom_dev,
2708                    "%s: chan %d ddcw: address %#o char_pos %#o type %#o tally %#o\r\n",
2709                    __func__, p -> PCW_CHAN, p -> DDCW_ADDR, p -> DCW_18_20_CP,
2710                    p -> DDCW_22_23_TYPE, p -> DDCW_TALLY);
2711   }
2712 
2713 // Read the list word at 'addr' (an LPW_DCW_PTR value) as
2714 // fetch_and_parse_DCW would in channel mode 'mode', without changing the
2715 // channel's DCW or PTW state.  Returns 0 on success, -1 if the address
2716 // can't be translated (invalid PTW) or the mode is one fetch_and_parse_DCW
2717 // doesn't support for the list (cm2, cm3b: "LPW paged").
2718 
2719 static int peek_list_word (uint iom_unit_idx, uint chan, int mode, word18 addr, word36 * data)
     /* [previous][next][first][last][top][bottom][index][help] */
2720   {
2721     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2722     switch (mode)
2723       {
2724         case cm1:
2725         case cm1e:
2726           iom_core_read (iom_unit_idx, addr, data, __func__);
2727           return 0;
2728 
2729         case cm2e:
2730           iom_core_read (iom_unit_idx, (word24) addr + ((word24) p -> LPWX_BOUND << 1), data, __func__);
2731           return 0;
2732 
2733         case cm3a:
2734         case cm4:
2735         case cm5:
2736           {
2737             word36 ptw;
2738             word24 pgte = build_LPWPTW_address (p -> PCW_PAGE_TABLE_PTR, p -> SEG,
2739                                                 (addr >> 10) & MASK8);
2740             iom_core_read (iom_unit_idx, pgte, & ptw, __func__);
2741             if ((ptw & 0740000777747) != 04llu)
2742               return -1;
2743             iom_core_read (iom_unit_idx,
2744                            ((word24) (getbits36_14 (ptw, 4) << 10)) | (addr & MASK10),
2745                            data, __func__);
2746             return 0;
2747           }
2748 
2749         default:
2750           return -1;
2751       }
2752   }
2753 
2754 // Return in '*dcw' the next IDCW or DDCW the channel's list service would
2755 // process: the word at LPW_DCW_PTR or, if that is a TDCW, the word it
2756 // transfers to.  Lets a device look at a DCW it has not yet fetched (e.g.
2757 // a halt IDCW the software has just stored) without changing the
2758 // channel's state.  A TDCW is modelled as iom_list_service applies it,
2759 // on local copies: the new address, the channel mode after the transfer
2760 // (tdcw_chan_mode), and LPW RES ORed with TDCW RES.  Returns -1 where the
2761 // list service would not deliver a DCW or the word can't be read: an
2762 // untranslatable address or unsupported mode, a TDCW to a TDCW, an AC
2763 // change error (RES with TDCW EC/PDCW), or an IDCW under RES.  A TDCW
2764 // with SEG (bit 31), which iom_list_service only warns about, is also
2765 // refused, since its address can't be modelled.
2766 
2767 int iom_peek_next_dcw (uint iom_unit_idx, uint chan, word36 * dcw)
     /* [previous][next][first][last][top][bottom][index][help] */
2768   {
2769     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2770     int    mode = p -> chanMode;
2771     word18 addr = p -> LPW_DCW_PTR;
2772     word1  res  = p -> LPW_18_RES;
2773     word36 w;
2774 
2775     if (peek_list_word (iom_unit_idx, chan, mode, addr, & w) != 0)
2776       return -1;
2777     if (getbits36_3 (w, 18) != 07 && getbits36_2 (w, 22) == 02)    // TDCW
2778       {
2779         word1 seg  = getbits36_1 (w, 31);
2780         word1 pdta = getbits36_1 (w, 32);
2781         word1 pdcw = getbits36_1 (w, 33);
2782         word1 tres = getbits36_1 (w, 34);
2783         word1 rel  = getbits36_1 (w, 35);
2784         res |= tres;
2785         if (seg || (res && pdcw))
2786           return -1;
2787         mode = tdcw_chan_mode (mode, pdta, pdcw, rel);
2788         addr = getbits36_18 (w, 0);
2789         if (peek_list_word (iom_unit_idx, chan, mode, addr, & w) != 0)
2790           return -1;
2791         if (getbits36_3 (w, 18) != 07 && getbits36_2 (w, 22) == 02)
2792           return -1;                                                // second TDCW
2793       }
2794     if (getbits36_3 (w, 18) == 07 && res)
2795       return -1;                                                    // IDCW under RES
2796     * dcw = w;
2797     return 0;
2798   }
2799 
2800 /*
2801  * send_general_interrupt ()
2802  *
2803  * Send an interrupt from the IOM to the CPU.
2804  *
2805  */
2806 
2807 int send_general_interrupt (uint iom_unit_idx, uint chan, enum iomImwPics pic)
     /* [previous][next][first][last][top][bottom][index][help] */
2808   {
2809 #if defined(TESTING)
2810     cpu_state_t * cpup = _cpup;
2811 #endif
2812     uint imw_addr;
2813     uint chan_group    = chan < 32 ? 1 : 0;
2814     uint chan_in_group = chan & 037;
2815 
2816     uint iomUnitNum =
2817       iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & 3u;
2818     uint interrupt_num = iomUnitNum | (chan_group << 2) | ((uint) pic << 3);
2819     // Section 3.2.7 defines the upper bits of the IMW address as
2820     // being defined by the mailbox base address switches and the
2821     // multiplex base address switches.
2822     // However, AN-70 reports that the IMW starts at 01200.  If AN-70 is
2823     // correct, the bits defined by the mailbox base address switches would
2824     // have to always be zero.  We'll go with AN-70.  This is equivalent to
2825     // using bit value 0010100 for the bits defined by the multiplex base
2826     // address switches and zeros for the bits defined by the mailbox base
2827     // address switches.
2828     //imw_addr += 01200;  // all remaining bits
2829     uint pi_base = iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & ~3u;
2830     imw_addr = (pi_base << 3) | interrupt_num;
2831 
2832     sim_debug (DBG_DEBUG, & iom_dev,
2833                "%s: IOM %c, channel %d (%#o), level %d; "
2834                "Interrupt %d (%#o).\r\n",
2835                __func__, iomChar (iom_unit_idx), chan, chan, pic, interrupt_num,
2836                interrupt_num);
2837     word36 imw;
2838     iom_core_read_lock (iom_unit_idx, imw_addr, &imw, __func__);
2839     // The 5 least significant bits of the channel determine a bit to be
2840     // turned on.
2841     //sim_debug (DBG_DEBUG, & iom_dev,
2842                //"%s: IMW at %#o was %012"PRIo64"; setting bit %d\r\n",
2843                //__func__, imw_addr, imw, chan_in_group);
2844     putbits36_1 (& imw, chan_in_group, 1);
2845     iom_core_write_unlock (iom_unit_idx, imw_addr, imw, __func__);
2846     return scu_set_interrupt (iom_unit_data[iom_unit_idx].invokingScuUnitIdx, interrupt_num);
2847   }
2848 
2849 static void iom_fault (uint iom_unit_idx, uint chan, UNUSED const char * who,
     /* [previous][next][first][last][top][bottom][index][help] */
2850                       iomFaultServiceRequest req,
2851                       iomSysFaults_t signal)
2852   {
2853 #if defined(TESTING)
2854     cpu_state_t * cpup = _cpup;
2855 #endif
2856     // T&D: the executive's survey connects to each IOM with an illegal
2857     // connect channel LPW tally and expects the system fault (stored and
2858     // interrupted below); not worth a warning there
2859     if (! tandd_mode_any ())
2860       sim_warn ("iom_fault %s: IOM %c channel %o, fault %o\r\n", who,
2861                 'A' + iom_unit_idx, chan, (uint) signal);
2862 
2863     // iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2864     // TODO:
2865     // For a system fault:
2866     // Store the indicated fault into a system fault word (3.2.6) in
2867     // the system fault channel -- use a list service to get a DCW to do so
2868     // For a user fault, use the normal channel status mechanisms
2869 
2870     // sys fault masks channel
2871 
2872     // signal gets put in bits 30..35, but we need fault code for 26..29
2873 
2874     // BUG: mostly unimplemented
2875 
2876     // User Faults: Pg 78: "A user fault is an abnormal conditions that can
2877     // be caused by a user program operating in the slave mode in the
2878     // processor."
2879     //  and
2880     // "User faults can be detected by the IOM Central of by a channel. If
2881     // a user fault is detected by the IOM Central, the fault is indicated to
2882     // the channel, and the channel is responsible for reporting the user
2883     // fault as status is its regular status queue.
2884 
2885     // This code only handles system faults
2886     //
2887 
2888     sim_debug (DBG_DEBUG, & iom_dev,
2889                "%s: chan %d %s req %#o signal %#o\r\n",
2890                __func__, chan, who, req, signal);
2891 
2892     word36 faultWord = 0;
2893     putbits36_9 (& faultWord, 9, (word9) chan);
2894     putbits36_5 (& faultWord, 18, (word5) req);
2895     // IAC, bits 26..29
2896     putbits36_6 (& faultWord, 30, (word6) signal);
2897 
2898     uint chanloc = mbxLoc (iom_unit_idx, IOM_SYSTEM_FAULT_CHAN);
2899 
2900     word36 lpw;
2901     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
2902 
2903     word36 scw;
2904     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
2905 
2906     word36 dcw;
2907     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
2908 
2909     //sim_debug (DBG_DEBUG, & iom_dev,
2910                //"%s: lpw %012"PRIo64" scw %012"PRIo64" dcw %012"PRIo64"\r\n",
2911                //__func__, lpw, scw, dcw);
2912 
2913     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, faultWord, __func__);
2914 
2915     uint tally = dcw & MASK12;
2916     if (tally > 1)
2917       {
2918         dcw -= 01llu;        //-V536  // tally --
2919         dcw += 01000000llu;  //-V536  // addr ++
2920       }
2921     else
2922       dcw = scw; // reset to beginning of queue
2923     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
2924 
2925     send_general_interrupt (iom_unit_idx, IOM_SYSTEM_FAULT_CHAN, imwSystemFaultPic);
2926   }
2927 
2928 //  0 ok
2929 // -1 fault
2930 // There is a path through the code where no DCW is sent (IDCW && LPW_18_RES)
2931 // Does the -1 return cover that?
2932 
2933 int iom_list_service (uint iom_unit_idx, uint chan,
     /* [previous][next][first][last][top][bottom][index][help] */
2934                            bool * ptro, bool * sendp, bool * uffp)
2935   {
2936     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2937 
2938 // initialize
2939 
2940     bool isConnChan = chan == IOM_CONNECT_CHAN;
2941     * ptro          = false; // assume not PTRO
2942     bool uff        = false; // user fault flag
2943     bool send       = false;
2944 
2945 // Figure 4.3.1
2946 
2947 // START
2948 
2949     // FIRST SERVICE?
2950 
2951     if (p -> lsFirst)
2952       {
2953         // PULL LPW AND LPW EXT. FROM CORE MAILBOX
2954 
2955         fetch_and_parse_LPW (iom_unit_idx, chan);
2956         p -> wasTDCW = false;
2957         p -> SEG     = 0; // pat. FIG. 2, step 44
2958       }
2959     // else lpw and lpwx are in chanData;
2960 
2961     // CONNECT CHANNEL?
2962 
2963     if (isConnChan)
2964       { // connect channel
2965 
2966         // LPW 21 (NC), 22 (TAL) is {00, 01, 1x}?
2967 
2968         if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 0)
2969               {
2970                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2971                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2972                           iomIllTalCChnFlt);
2973                 return -1;
2974               }
2975 
2976         if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 1)
2977           { // 01
2978 
2979             // TALLY is {0, 1, >1}?
2980 
2981             if (p -> LPW_TALLY == 0)
2982               {
2983                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2984                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2985                           iomIllTalCChnFlt);
2986                 return -1;
2987               }
2988 
2989             if (p -> LPW_TALLY > 1)
2990               { // 00
2991 
2992                 // 256K OVERFLOW?
2993                 if (p -> LPW_20_AE == 0 &&
2994                     (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2995                     01000000llu)
2996                   {
2997                     iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2998                               p -> lsFirst ? iomFsrFirstList : iomFsrList,
2999                               iom256KFlt);
3000                     return -1;
3001                   }
3002               }
3003             else if (p -> LPW_TALLY == 1)
3004               * ptro = true;
3005           }
3006         else // 1x
3007           * ptro = true;
3008 
3009         // PULL PCW FROM CORE
3010 
3011 // B
3012 
3013         fetch_and_parse_PCW (iom_unit_idx, chan); // fills in DCW*
3014 
3015         // PCW 18-20 == 111?
3016         if (IS_NOT_IDCW (p))
3017           {
3018             iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
3019                       p -> lsFirst ? iomFsrFirstList : iomFsrList,
3020                       iomNotPCWFlt);
3021             return -1;
3022           }
3023 
3024         // SELECT CHANNEL
3025 
3026         // chan = p -> PCW_CHAN;
3027 
3028 // detect unused slot as fault
3029 // if (no handler in slot)
3030 //  goto FAULT;
3031 
3032        // SEND PCW TO CHANNEL
3033 
3034         // p -> DCW = p -> DCW;
3035         send = true;
3036 
3037         goto D;
3038       }
3039 
3040     // Not connect channel
3041 
3042     // LPW 21 (NC), 22 (TAL) is {00, 01, 1x}?
3043 
3044     if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 0)
3045       {
3046         // XXX see pat. 46-51 re: SEG
3047         // 256K OVERFLOW?
3048         if (p -> LPW_20_AE == 0 &&
3049             (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
3050             01000000llu)
3051           {
3052             iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
3053                       p -> lsFirst ? iomFsrFirstList : iomFsrList,
3054                       iom256KFlt);
3055             return -1;
3056           }
3057        }
3058     else if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 1)
3059       { // 01
3060 A:;
3061         // TALLY is {0, 1, >1}?
3062 
3063         // Per the IOM EPS: TAL=0 means "a tally of zero will not cause a
3064         // fault indication".  Only set uff when TAL=1 (tally-zero faults
3065         // enabled).  When TAL=0 (circular DCW lists, e.g. the MGP read
3066         // channel), reaching TALLY=0 after a TDCW wrap is normal and must
3067         // not cause uff - doing so leaves DDCW_ADDR stale from the TDCW
3068         // unpack (= 0) and prevents mgp_cmd(001) from refreshing it.
3069         if (p -> LPW_TALLY == 0 && p -> LPW_22_TAL)
3070           {
3071             sim_warn("%s: setting uff for chan %d because LPW_TALLY was 0. LPW=%llo.\r\n",
3072                      __func__, chan, p -> LPW);
3073             uff = true;
3074           }
3075         else if (p -> LPW_TALLY > 1)
3076           { // 00
3077 
3078             // XXX see pat. 46-51 re: SEG
3079             // 256K OVERFLOW?
3080             if (p -> LPW_20_AE == 0 &&
3081                 (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
3082                 01000000llu)
3083               {
3084                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
3085                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
3086                           iom256KFlt);
3087                 return -1;
3088               }
3089           }
3090       }
3091 
3092     // LPW 20? -- LPW_20 checked by fetch_and_parse_DCW
3093 
3094     // PULL DCW FROM CORE
3095     fetch_and_parse_DCW (iom_unit_idx, chan, false);
3096 
3097 // C
3098 
3099     if (p -> lsFirst)
3100       p -> LPWX_SIZE = p -> LPW_DCW_PTR;
3101 
3102 // pg B16: "If the IOM is paged [yes] and PCW bit 64 is off, LPW bit 23
3103 //          is ignored by the hardware. If bit 64 is set, LPW bit 23 causes
3104 //          the data to become segmented."
3105 // Handled in fetch_and_parse_LPW
3106 
3107     // LPW 23 REL?
3108 
3109     // TDCW ?
3110 
3111     if (IS_TDCW (p))
3112       {
3113         // SECOND TDCW?
3114         if (p -> wasTDCW)
3115           {
3116             sim_warn("%s: setting uff for chan %d because p->wasTDCW was %d\r\n",
3117                      __func__, chan, p->wasTDCW);
3118             uff = true;
3119             goto uffSet;
3120           }
3121         p -> wasTDCW = true;
3122 
3123         // PUT ADDRESS N LPW
3124 
3125         p -> LPW_DCW_PTR = p -> TDCW_DATA_ADDRESS;
3126         // OR TDCW 33, 34, 35 INTO LPW 20, 18, 23
3127         // XXX is 33 bogus? its semantics change based on PCW 64...
3128         // should be okay; pg B21 says that this is correct; implies that the
3129         // semantics are handled in the LPW code. Check...
3130         p -> LPW_20_AE  |= p -> TDCW_33_EC; // TDCW_33_PDCW
3131         p -> LPW_18_RES |= p -> TDCW_34_RES;
3132         p -> LPW_23_REL |= p -> TDCW_35_REL;
3133 
3134 // Pg B21: (TDCW_31_SEG)
3135 // "SEG = This bit furnishes the 19th address bit (MSD) of a TDCW address
3136 //  used for locating the DCW list in a 512 word page table. It will have
3137 //  meaning only in the TDCW where:
3138 //   (a) the DCW list is already paged and the TDCW calls for the
3139 //       DCW [to be] segmented
3140 //  or
3141 //   (b) the data is already segmented and the TDCW calls for the
3142 //       DCW list to be paged
3143 //  or
3144 //   (c) neither data is segmented nor DCW list is paged and the
3145 //       TDCW calls for both.
3146 //  and
3147 //   (d) an auxiliary PTW in not being used.
3148 
3149 //   (a) the DCW list is already paged   --  LPW PAGED: 3b, 4
3150 //       and the TDCW calls for the
3151 //       DCW [list to be] segmented      --  LPW SEG:       5
3152 
3153 //   (b) the data is already segmented   --  DCW SEG:   3a, 4
3154 //       and the TDCW calls for the
3155 //       DCW list to be paged            --  DCW PAGED:  2, 3b
3156 
3157 //   (c) neither data is segmented       -- DCW !SEG     1, 2, 3b
3158 //       nor DCW list is paged           -- LPW !PAGED   1, 2, 3a
3159 //                                       --              1, 2
3160 //       and the TDCW calls for both.    -- DCW SEG & LPW PAGED
3161 //                                                       4
3162 
3163 //put that weird SEG logic in here
3164 
3165         if (p -> TDCW_31_SEG)
3166           sim_warn ("TDCW_31_SEG\r\n");
3167 
3168         update_chan_mode (iom_unit_idx, chan, true);
3169 
3170         // Decrement tally
3171         p->LPW_TALLY = ((word12) (((word12s) p->LPW_TALLY) - 1)) & MASK12;
3172 
3173         pack_LPW (iom_unit_idx, chan);
3174 
3175         // AC CHANGE ERROR? (LPW 18 == 1 && DCW 33 == 1)
3176 
3177         if (p -> LPW_18_RES && p -> TDCW_33_EC) // same as TDCW_33_PDCW
3178           {
3179             sim_warn("%s: setting uff for chan %d because p->LPW_18_RES and p->TDCW_33_EC were set\r\n",
3180                      __func__, chan);
3181             uff = true;
3182             goto uffSet;
3183           }
3184 
3185         goto A;
3186       }
3187     else if (IS_IDCW (p))     // IDCW ?
3188       {
3189         // LPW_18_RES?
3190 
3191         if (p -> LPW_18_RES)
3192           {
3193             // SET USER FAULT FLAG
3194             sim_warn("%s: setting uff for chan %d because LPW_18_RES was %o\r\n",
3195                      __func__, chan, p->LPW_18_RES);
3196             uff = true; // XXX Why? uff isn't not examined later.
3197             // send = false; implicit...
3198           }
3199         else
3200           {
3201             // SEND IDCW TO CHANNEL
3202             // p -> DCW = p -> DCW;
3203             send = true;
3204           }
3205         p -> LPWX_SIZE = p -> LPW_DCW_PTR;
3206         goto D;
3207       }
3208 
3209     p -> wasTDCW = false;
3210     // NOT TDCW
3211 
3212     // CP VIOLATION?
3213 
3214     // 43A239854 3.2.3.3 "The byte size, defined by the channel, determines
3215     // what CP values are valid..."
3216 
3217     // If we get here, the DCW is not a IDCW and not a TDCW, therefore
3218     // it must be a DDCW. If the list service knew the sub-word size
3219     // size of the device, it could check the for valid values. Let
3220     // the device handler do that later.
3221 
3222     // if (cp decrepancy)
3223     //   {
3224     //      user_fault_flag = iomCsCpDiscrepancy;
3225     //      goto user_fault;
3226     //   }
3227     // if (cp violation)
3228     //   {
3229     //     uff = true;
3230     //     goto uffSet;
3231     //   }
3232 
3233     // USER FAULT FLAG SET?
3234 
3235     if (uff)
3236       {
3237 uffSet:;
3238         // PUT 7 into DCW 18-20
3239         p -> DCW_18_20_CP = 07u;
3240         pack_DCW (iom_unit_idx, chan);
3241       }
3242 
3243     // WRITE DCW IN [SCRATCH PAD] MAILBOX
3244 
3245     // p -> DVW  = P -> DCW;
3246 
3247     // SEND DCW TO CHANNEL
3248 
3249     // p -> DCW = p -> DCW;
3250     send = true;
3251 
3252     // goto D;
3253 
3254 D:;
3255 
3256     // SEND FLAGS TO CHANNEL
3257 
3258     * uffp  = uff;
3259     * sendp = send;
3260 
3261     // LPW 21 ?
3262 
3263     if (p -> LPW_21_NC == 0) // UPDATE
3264      {
3265        // UPDATE LPW ADDRESS & TALLY
3266        if (isConnChan)
3267          p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 2u) & MASK18;
3268        else
3269          p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 1u) & MASK18;
3270        // ubsan
3271        p->LPW_TALLY = ((word12) (((word12s) p->LPW_TALLY) - 1)) & MASK12;
3272        pack_LPW (iom_unit_idx, chan);
3273      }
3274 
3275     // IDCW OR FIRST LIST
3276     if (p -> DDCW_22_23_TYPE == 07u || p -> lsFirst)
3277       {
3278         // WRITE LPW & LPW EXT. INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3279         // scratch pad
3280         // p -> lpw = p -> lpw
3281         // core
3282         write_LPW (iom_unit_idx, chan);
3283       }
3284     else
3285       {
3286         if (p -> LPW_21_NC == 0 ||
3287             (IS_TDCW (p)))
3288           {
3289             // WRITE LPW INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3290             // scratch pad
3291             // p -> lpw = p -> lpw
3292             // core
3293             write_LPW (iom_unit_idx, chan);
3294           }
3295       }
3296 
3297     p -> lsFirst = false;
3298     // END
3299 
3300     return 0;
3301   }
3302 
3303 // 0 ok
3304 // -1 uff
3305 static int doPayloadChannel (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
3306 #if defined(TESTING)
3307   cpu_state_t * cpup = _cpup;
3308   sim_debug (DBG_DEBUG, & iom_dev, "%s: Payload channel %c%02o\r\n", __func__, iomChar (iomUnitIdx), chan);
3309 #endif
3310 // A dubious assumption being made is that the device code will always
3311 // be in bits 6-12 of the DCw. Normally, the controller would
3312 // decipher the device code and route to the device, but we
3313 // have elided the controllers and must do that ourselves.
3314 
3315 // Loop logic
3316 //
3317 //   The DCW list can be terminated two ways; either by having the
3318 //   tally run out, or a IDCW with control set to zero.
3319 //
3320 //   The device handler will absorb DDCWs
3321 //
3322 // loop until:
3323 //
3324 //  listService sets ptro, indicating that no more DCWs are available. or
3325 //     control is 0, indicating last IDCW
3326 
3327   iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
3328 
3329 #if defined(TESTING)
3330   word36 PCW_DCW        = p->DCW;
3331   word1  PCW_LPW_23_REL = p->LPW_23_REL;
3332 #endif
3333   p -> chanMode   = cm1;
3334   p -> LPW_18_RES = 0;
3335   p -> LPW_20_AE  = 0;
3336   p -> LPW_23_REL = 0;
3337 
3338   unpack_DCW (iomUnitIdx, chan);
3339 
3340   p->isPCW = true;
3341 
3342   // Masked is set in doConnectChannel
3343   //p->masked = !!p->PCW_21_MSK;
3344   struct iom_to_ctlr_s * d = & cables->iom_to_ctlr[iomUnitIdx][chan];
3345 
3346 // A device command of 051 in the PCW is only meaningful to the operator console;
3347 // all other channels should ignore it. We use (somewhat bogusly) a chanType of
3348 // chanTypeCPI to indicate the operator console.
3349   if (d->chan_type != chan_type_CPI && p -> IDCW_DEV_CMD == 051) {
3350     p->stati = 04501;
3351     send_terminate_interrupt (iomUnitIdx, chan);
3352     return 0;
3353   }
3354 
3355   if ((!d->in_use) || (!d->iom_cmd)) {
3356     p -> stati = 06000; // t, power off/missing
3357 #if defined(POLTS_TESTING)
3358 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3359                         ("// terminate 10. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3360 #endif
3361     goto terminate;
3362   }
3363 
3364 // 3.2.2. "Bits 12-17 [of the PCW] contain the address extension which is maintained by
3365 //         the channel for subsequent use by the IOM in generating a 24-bit
3366 //         address for list of data services for the extended address modes."
3367 // see also 3.2.3.1
3368   p -> ADDR_EXT     = p -> PCW_AE;
3369 
3370   p -> lsFirst      = true;
3371 
3372   p -> tallyResidue = 0;
3373   p -> isRead       = true;
3374   p -> charPos      = 0;
3375 // As far as I can tell, initiate false means that an IOTx succeeded in
3376 // transferring data; assume it didn't since that is the most common
3377 // code path.
3378   p -> initiate     = true;
3379   p -> chanStatus   = chanStatNormal;
3380 
3381 //
3382 // Send the PCW's DCW
3383 //
3384   int rc = d->iom_cmd (iomUnitIdx, chan);
3385 
3386   if (rc < 0) {
3387     p -> dev_code = getbits36_6 (p -> DCW, 6);
3388 #if defined(POLTS_TESTING)
3389 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3390                         sim_printf ("// terminate 9. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3391 #endif
3392     goto terminate;
3393   }
3394 
3395   if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_MARKER) { // IDCW marker bit set
3396     send_marker_interrupt (iomUnitIdx, (int) chan);
3397   }
3398 
3399   if (rc == IOM_CMD_DISCONNECT) {
3400 #if defined(POLTS_TESTING)
3401 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3402                         ("// terminate 8. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3403 #endif
3404     goto terminate;
3405   }
3406 
3407   if (p->masked) {
3408 #if defined(POLTS_TESTING)
3409 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3410                         ("// terminate 7. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3411 #endif
3412     goto terminate;
3413   }
3414 
3415   bool ptro, send, uff;
3416   bool terminate = false;
3417   p->isPCW       = false;
3418 
3419 #if defined(TESTING)
3420   bool first = true;
3421 #endif
3422 
3423   (void)terminate;
3424   bool idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3425   do {
3426     int rc2 = iom_list_service (iomUnitIdx, chan, & ptro, & send, & uff);
3427     if (rc2 < 0) {
3428 // XXX set status flags
3429       sim_warn ("%s list service failed\r\n", __func__);
3430       return -1;
3431     }
3432     if (uff) {
3433       // We get a uff if the LPW tally hit 0
3434 #if defined(POLTS_TESTING)
3435 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3436                         sim_printf ("// terminate 6. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3437 #endif
3438       goto terminate;
3439     }
3440     // List service failed to get a DCW
3441     if (! send) {
3442       sim_warn ("%s nothing to send\r\n", __func__);
3443       return 1;
3444     }
3445 
3446 #if defined(TESTING)
3447 # if defined(POLTS_TESTING)
3448 if (iomUnitIdx == 1 && chan == 020)
3449 # endif
3450     if_sim_debug (DBG_TRACE, & iom_dev) {
3451       if (first) {
3452         first = false;
3453         dumpLPW (iomUnitIdx, chan);
3454         sim_printf ("// DCW list dump: \r\n");
3455         dumpDCW (PCW_DCW, PCW_LPW_23_REL);
3456         for (int i = 0; i < 8; i ++)
3457           dumpDCW (M[p->LPW_DCW_PTR - 1 + i], p->LPW_23_REL);
3458         sim_printf ("// \r\n");
3459       }
3460     }
3461 #endif
3462 
3463 // 3.2.3.1 "If EC - Bit 21 = 1, The channel will replace the present address
3464 //          extension with the new address extension in bits 12-17. ... In
3465 //          Multics and NSA systems, EC is inhibited from the payload channel
3466 //          if LPW bit 23 = 1.
3467 
3468     if (IS_IDCW (p)) { // IDCW
3469       idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3470       if (p -> LPW_23_REL == 0 && p -> IDCW_EC == 1)
3471         p -> ADDR_EXT = getbits36_6 (p -> DCW, 12);
3472 
3473 //      p -> recordResidue = 0;
3474       p -> tallyResidue = 0;
3475       p -> isRead       = true;
3476       p -> charPos      = 0;
3477       p -> chanStatus   = chanStatNormal;
3478     }
3479 
3480 // Send the DCW list's DCW
3481 
3482     rc2 = d->iom_cmd (iomUnitIdx, chan);
3483 
3484     if (rc2 < 0) {
3485       p -> dev_code = getbits36_6 (p -> DCW, 6);
3486 #if defined(POLTS_TESTING)
3487 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3488                         sim_printf ("// terminate 5. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3489 #endif
3490       goto terminate;
3491     }
3492 
3493     if (rc2 == IOM_CMD_DISCONNECT) {
3494       terminate = true;
3495 #if defined(POLTS_TESTING)
3496 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3497                         ("// terminate 4. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3498 #endif
3499     }
3500 
3501     if (rc2 == IOM_CMD_PENDING) // handler still processing command, don't set
3502       goto pending;             // terminate interrupt.
3503 
3504     // If IDCW and terminate and nondata
3505     if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE && p->IDCW_CHAN_CMD == CHAN_CMD_NONDATA) {
3506 #if defined(POLTS_TESTING)
3507 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3508                         sim_printf ("// terminate 1. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3509 #endif
3510       goto terminate;
3511     }
3512     // If IOTD and last IDCW was terminate
3513     if (IS_IOTD (p) && idcw_terminate && rc2 != IOM_CMD_RESIDUE) {
3514 #if defined(POLTS_TESTING)
3515 //if (iomUnitIdx == 1 && chan == 020)      if_sim_debug (DBG_TRACE, & iom_dev)
3516 //                                           sim_printf ("// ctrl == 0 in chan %d (%o) IOTP\r\n", chan, chan);
3517 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3518                         sim_printf ("// terminate 2. ctrl == 0 in chan %d (%o) DCW\r\n", chan, chan);
3519 #endif
3520       goto terminate;
3521     }
3522 
3523     // IOM_CMD_RESIDUE: Continue pushing DCWS until the record residue is 0
3524 
3525     if (IS_NOT_IDCW (p) && rc2 == IOM_CMD_RESIDUE) {
3526       if (p->recordResidue)
3527         p->recordResidue --;
3528       if (p->recordResidue == 0)
3529         goto terminate;
3530     }
3531   } while (! terminate);
3532 
3533 terminate:;
3534 #if defined(POLTS_TESTING)
3535 if (iomUnitIdx == 1 && chan == 020) sim_printf ("stati %04o\r\n", p->stati);
3536 #endif
3537   send_terminate_interrupt (iomUnitIdx, chan);
3538   return 0;
3539 
3540 pending:;
3541   return 0;
3542 }
3543 
3544 // doConnectChan ()
3545 //
3546 // Process the "connect channel".  This is what the IOM does when it
3547 // receives a $CON signal.
3548 //
3549 // Only called by iom_interrupt ()
3550 //
3551 // The connect channel requests one or more "list services" and processes the
3552 // resulting PCW control words.
3553 //
3554 
3555 static int doConnectChan (uint iom_unit_idx) {
     /* [previous][next][first][last][top][bottom][index][help] */
3556   // ... the connect channel obtains a list service from the IOM Central.
3557   // During this service the IOM Central will do a double precision read
3558   // from the core, under the control of the LPW for the connect channel.
3559   //
3560   // If the IOM Central does not indicate a PTRO during the list service,
3561   // the connect channel obtains another list service.
3562   //
3563   // The connect channel does not interrupt or store status.
3564   //
3565   // The DCW and SCW mailboxes for the connect channel are not used by
3566   // the IOM.
3567 #if defined(TESTING)
3568   cpu_state_t * cpup = _cpup;
3569 #endif
3570   sim_debug (DBG_DEBUG, & iom_dev, "%s: Connect channel\r\n", __func__);
3571   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][IOM_CONNECT_CHAN];
3572   p -> lsFirst = true;
3573 #if defined(TESTING) && defined(POLTS_TESTING)
3574   bool first = true;
3575 #endif
3576   bool ptro, send, uff;
3577   do {
3578     // Fetch the next PCW
3579     int rc = iom_list_service (iom_unit_idx, IOM_CONNECT_CHAN, & ptro, & send, & uff);
3580     if (rc < 0) {
3581       if (! tandd_mode_any ()) // see iom_fault
3582         sim_warn ("connect channel connect failed\r\n");
3583       return -1;
3584     }
3585     if (uff) {
3586       sim_warn ("connect channel ignoring uff\r\n"); // XXX
3587     }
3588     if (! send) {
3589       sim_warn ("connect channel nothing to send\r\n");
3590     } else {
3591 #if defined(xTESTING)
3592       if_sim_debug (DBG_TRACE, & iom_dev) {
3593         if (first) {
3594           first = false;
3595           sim_printf ("// CIOC %lld\r\n", cpu.cycleCnt);
3596           dumpLPW (iom_unit_idx, IOM_CONNECT_CHAN);
3597           sim_printf ("// PCW %012llo %012llo\r\n",    p->PCW0, p->PCW1);
3598           sim_printf ("//   Chan info     %04o\r\n",   p->PCW_CHAN);
3599           sim_printf ("//   Addr ext        %02o\r\n", p->PCW_AE);
3600           sim_printf ("//   111              %o\r\n",  getbits36_3 (p->PCW0, 18));
3601           sim_printf ("//   M                %o\r\n",  p->PCW_21_MSK);
3602           sim_printf ("//   Chan info %08o\r\n",       getbits36_14 (p->PCW0, 22));
3603           sim_printf ("//   Pg Tbl Ptr  %06o\r\n",     p->PCW_PAGE_TABLE_PTR);
3604           sim_printf ("//   PTP              %o\r\n",  p->PCW_63_PTP);
3605           sim_printf ("//   PGE              %o\r\n",  p->PCW_64_PGE);
3606           sim_printf ("//   AUX              %o\r\n",  p->PCW_65_AUX);
3607           sim_printf ("//\r\n");
3608         }
3609       }
3610 #endif
3611       // Copy the PCW's DCW to the payload channel
3612       iom_chan_data_t * q = & iom_chan_data[iom_unit_idx][p -> PCW_CHAN];
3613 
3614       q -> PCW0               = p -> PCW0;
3615       q -> PCW1               = p -> PCW1;
3616       q -> PCW_CHAN           = p -> PCW_CHAN;
3617       q -> PCW_AE             = p -> PCW_AE;
3618       q -> PCW_PAGE_TABLE_PTR = p -> PCW_PAGE_TABLE_PTR;
3619       q -> PCW_63_PTP         = p -> PCW_63_PTP;
3620       q -> PCW_64_PGE         = p -> PCW_64_PGE;
3621       q -> PCW_65_AUX         = p -> PCW_65_AUX;
3622       q -> PCW_21_MSK         = p -> PCW_21_MSK;
3623       q -> DCW                = p -> DCW;
3624 
3625       sim_debug (DBG_DEBUG, & iom_dev, "%s: PCW %012llo %012llo chan %02o\r\n", __func__, q->PCW0, q->PCW1, q->PCW_CHAN);
3626 
3627       q -> masked = p -> PCW_21_MSK;
3628       if (q -> masked) {
3629         if (q -> in_use)
3630           sim_warn ("%s: chan %d masked while in use\r\n", __func__, p -> PCW_CHAN);
3631         q -> in_use = false;
3632         q -> start  = false;
3633       } else {
3634         if (q -> in_use)
3635           sim_warn ("%s: chan %d connect while in use\r\n", __func__, p -> PCW_CHAN);
3636         q -> in_use = true;
3637         q -> start  = true;
3638 #if defined(IO_THREADZ)
3639         setChnConnect (iom_unit_idx, p -> PCW_CHAN);
3640 #else
3641 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3642         doPayloadChannel (iom_unit_idx, p -> PCW_CHAN);
3643 # endif
3644 # if defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3645         pthread_cond_signal (& iomCond);
3646 # endif
3647 #endif
3648       }
3649     }
3650   } while (! ptro);
3651   return 0; // XXX
3652 }
3653 
3654 /*
3655  * send_marker_interrupt ()
3656  *
3657  * Send a "marker" interrupt to the CPU.
3658  *
3659  * Channels send marker interrupts to indicate normal completion of
3660  * a PCW or IDCW if the control field of the PCW/IDCW has a value
3661  * of three.
3662  */
3663 
3664 int send_marker_interrupt (uint iom_unit_idx, int chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3665   {
3666     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3667       return(0);
3668     status_service (iom_unit_idx, (uint) chan, true);
3669     return send_general_interrupt (iom_unit_idx, (uint) chan, imwMarkerPic);
3670   }
3671 
3672 /*
3673  * send_special_interrupt ()
3674  *
3675  * Send a "special" interrupt to the CPU.
3676  *
3677  */
3678 
3679 int send_special_interrupt (uint iom_unit_idx, uint chan, uint devCode,
     /* [previous][next][first][last][top][bottom][index][help] */
3680                             word8 status0, word8 status1)
3681   {
3682 //if (iom_unit_idx == 0 && chan == 013) sim_printf ("spec int %03o %013o\r\n", status0, status1);
3683     uint chanloc = mbxLoc (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN);
3684 
3685     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3686       return(0);
3687 
3688 #if defined(LOCKLESS)
3689     lock_iom();
3690 #endif
3691 
3692 // Multics uses an 12(8) word circular queue, managed by clever manipulation
3693 // of the LPW and DCW.
3694 // Rather then goes through the mechanics of parsing the LPW and DCW,
3695 // we will just assume that everything is set up the way we expect,
3696 // and update the circular queue.
3697     word36 lpw;
3698     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
3699 
3700     word36 scw;
3701     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
3702 
3703     word36 dcw;
3704     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
3705 
3706     word36 status  = 0400000000000ull;
3707     status        |= (((word36) chan)    & MASK6) << 27;
3708     status        |= (((word36) devCode) & MASK8) << 18;
3709     status        |= (((word36) status0) & MASK8) <<  9;
3710     status        |= (((word36) status1) & MASK8) <<  0;
3711     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, status, __func__);
3712 
3713     uint tally = dcw & MASK12;
3714     if (tally > 1)
3715       {
3716         dcw -= 01llu;  // tally --
3717         dcw += 01000000llu; // addr ++
3718       }
3719     else
3720       dcw = scw; // reset to beginning of queue
3721     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
3722 
3723 #if defined(LOCKLESS)
3724     unlock_iom();
3725 #endif
3726 
3727     send_general_interrupt (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN, imwSpecialPic);
3728     return 0;
3729   }
3730 
3731 /*
3732  * send_terminate_interrupt ()
3733  *
3734  * Send a "terminate" interrupt to the CPU.
3735  *
3736  * Channels send a terminate interrupt after doing a status service.
3737  *
3738  */
3739 
3740 int send_terminate_interrupt (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3741   {
3742     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3743       return 0;
3744     status_service (iom_unit_idx, chan, false);
3745     if (iom_chan_data [iom_unit_idx] [chan] . in_use == false)
3746       sim_warn ("%s: chan %d not in use\r\n", __func__, chan);
3747     iom_chan_data [iom_unit_idx] [chan] . in_use = false;
3748     send_general_interrupt (iom_unit_idx, chan, imwTerminatePic);
3749     return 0;
3750   }
3751 
3752 void iom_interrupt (uint scu_unit_idx, uint iom_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
3753   {
3754     cpu_state_t * cpup = _cpup;
3755     sim_debug (DBG_DEBUG, & iom_dev,
3756                "%s: IOM %c starting. [%"PRId64"] %05o:%08o\r\n",
3757                __func__, iomChar (iom_unit_idx), cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC);
3758 
3759     iom_unit_data[iom_unit_idx].invokingScuUnitIdx = scu_unit_idx;
3760 
3761 #if defined(IO_THREADZ)
3762     setIOMInterrupt (iom_unit_idx);
3763     iomDoneWait (iom_unit_idx);
3764 #else
3765     int ret = doConnectChan (iom_unit_idx);
3766 
3767     sim_debug (DBG_DEBUG, & iom_dev,
3768                "%s: IOM %c finished; doConnectChan returned %d.\r\n",
3769                __func__, iomChar (iom_unit_idx), ret);
3770 #endif
3771     // XXX doConnectChan return value ignored
3772   }
3773 
3774 #if defined(IO_THREADZ)
3775 void * chan_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3776   {
3777     uint myid     = (uint) * (int *) arg;
3778     this_iom_idx  = (uint) myid / MAX_CHANNELS;
3779     this_chan_num = (uint) myid % MAX_CHANNELS;
3780 
3781 // Set CPU context to allow sim_debug to work
3782 
3783     set_cpu_idx (0);
3784 
3785     sim_msg ("\rIOM %c Channel %u thread created\r\n", this_iom_idx + 'a', this_chan_num);
3786 # if defined(TESTING)
3787     printPtid(pthread_self());
3788 # endif /* if defined(TESTING) */
3789     if (realtime_ok) {
3790       set_realtime_priority (pthread_self(), realtime_max_priority() - 1);
3791       check_realtime_priority (pthread_self(), realtime_max_priority() - 1);
3792     } else {
3793 # if !defined(__QNX__)
3794       (void)sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
3795 # endif
3796     }
3797 
3798     setSignals ();
3799     while (1)
3800       {
3801 //sim_printf("IOM %c Channel %u thread waiting\r\n", this_iom_idx + 'a', this_chan_num);
3802         chnConnectWait ();
3803 //sim_printf("IOM %c Channel %u thread running\r\n", this_iom_idx + 'a', this_chan_num);
3804         doPayloadChannel (this_iom_idx, this_chan_num);
3805         chnConnectDone ();
3806       }
3807   }
3808 
3809 void * iom_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3810   {
3811     int myid = * (int *) arg;
3812     this_iom_idx = (uint) myid;
3813 
3814 // Set CPU context to allow sim_debug to work
3815 
3816     set_cpu_idx (0);
3817 
3818     sim_printf("IOM %c thread created\r\n", 'a' + myid);
3819 
3820     setSignals ();
3821     while (1)
3822       {
3823 //sim_printf("IOM %c thread waiting\r\n", 'a' + myid);
3824         iomInterruptWait ();
3825 //sim_printf("IOM %c thread running\r\n", 'a' + myid);
3826         int ret = doConnectChan (this_iom_idx);
3827 
3828         sim_debug (DBG_DEBUG, & iom_dev,
3829                    "%s: IOM %c finished; doConnectChan returned %d.\r\n",
3830                    __func__, iomChar (myid), ret);
3831         iomInterruptDone ();
3832       }
3833   }
3834 #endif
3835 
3836 /*
3837  * iom_init ()
3838  *
3839  *  Once-only initialization
3840  */
3841 
3842 void iom_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3843   {
3844     //sim_debug (DBG_INFO, & iom_dev, "%s: running.\r\n", __func__);
3845   }
3846 
3847 #if defined(PANEL68)
3848 void do_boot (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3849   {
3850     boot_svc (& boot_channel_unit[0]);
3851   }
3852 #endif
3853 
3854 #if defined(IO_ASYNC_PAYLOAD_CHAN) || defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3855 void iomProcess (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3856   {
3857     for (uint i = 0; i < N_IOM_UNITS_MAX; i++)
3858       for (uint j = 0; j < MAX_CHANNELS; j++)
3859         {
3860           iom_chan_data_t * p = &iom_chan_data [i] [j];
3861           if (p -> start)
3862             {
3863               p -> start = false;
3864               doPayloadChannel (i, j);
3865             }
3866         }
3867   }
3868 #endif
3869 
3870 char iomChar (uint iomUnitIdx)
     /* [previous][next][first][last][top][bottom][index][help] */
3871   {
3872     return (iom_unit_data[iomUnitIdx].configSwMultiplexBaseAddress & 3) + 'A';
3873   }

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