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

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