root/src/dps8/dps8_iom.c

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

DEFINITIONS

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

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