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 = ((word12) (((word12s) p->LPW_TALLY) - 1)) & 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        // ubsan
3022        p->LPW_TALLY = ((word12) (((word12s) p->LPW_TALLY) - 1)) & MASK12;
3023        pack_LPW (iom_unit_idx, chan);
3024      }
3025 
3026     // IDCW OR FIRST LIST
3027     if (p -> DDCW_22_23_TYPE == 07u || p -> lsFirst)
3028       {
3029         // WRITE LPW & LPW EXT. INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3030         // scratch pad
3031         // p -> lpw = p -> lpw
3032         // core
3033         write_LPW (iom_unit_idx, chan);
3034       }
3035     else
3036       {
3037         if (p -> LPW_21_NC == 0 ||
3038             (IS_TDCW (p)))
3039           {
3040             // WRITE LPW INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3041             // scratch pad
3042             // p -> lpw = p -> lpw
3043             // core
3044             write_LPW (iom_unit_idx, chan);
3045           }
3046       }
3047 
3048     p -> lsFirst = false;
3049     // END
3050 
3051     return 0;
3052   }
3053 
3054 // 0 ok
3055 // -1 uff
3056 static int doPayloadChannel (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
3057 #if defined(TESTING)
3058   cpu_state_t * cpup = _cpup;
3059   sim_debug (DBG_DEBUG, & iom_dev, "%s: Payload channel %c%02o\n", __func__, iomChar (iomUnitIdx), chan);
3060 #endif
3061 // A dubious assumption being made is that the device code will always
3062 // be in bits 6-12 of the DCw. Normally, the controller would
3063 // decipher the device code and route to the device, but we
3064 // have elided the controllers and must do that ourselves.
3065 
3066 // Loop logic
3067 //
3068 //   The DCW list can be terminated two ways; either by having the
3069 //   tally run out, or a IDCW with control set to zero.
3070 //
3071 //   The device handler will absorb DDCWs
3072 //
3073 // loop until:
3074 //
3075 //  listService sets ptro, indicating that no more DCWs are available. or
3076 //     control is 0, indicating last IDCW
3077 
3078   iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
3079 
3080 #if defined(TESTING)
3081   word36 PCW_DCW        = p->DCW;
3082   word1  PCW_LPW_23_REL = p->LPW_23_REL;
3083 #endif
3084   p -> chanMode   = cm1;
3085   p -> LPW_18_RES = 0;
3086   p -> LPW_20_AE  = 0;
3087   p -> LPW_23_REL = 0;
3088 
3089   unpack_DCW (iomUnitIdx, chan);
3090 
3091   p->isPCW = true;
3092 
3093   // Masked is set in doConnectChannel
3094   //p->masked = !!p->PCW_21_MSK;
3095   struct iom_to_ctlr_s * d = & cables->iom_to_ctlr[iomUnitIdx][chan];
3096 
3097 // A device command of 051 in the PCW is only meaningful to the operator console;
3098 // all other channels should ignore it. We use (somewhat bogusly) a chanType of
3099 // chanTypeCPI to indicate the operator console.
3100   if (d->chan_type != chan_type_CPI && p -> IDCW_DEV_CMD == 051) {
3101     p->stati = 04501;
3102     send_terminate_interrupt (iomUnitIdx, chan);
3103     return 0;
3104   }
3105 
3106   if ((!d->in_use) || (!d->iom_cmd)) {
3107     p -> stati = 06000; // t, power off/missing
3108 #if defined(POLTS_TESTING)
3109 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3110                         ("// terminate 10. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3111 #endif
3112     goto terminate;
3113   }
3114 
3115 // 3.2.2. "Bits 12-17 [of the PCW] contain the address extension which is maintained by
3116 //         the channel for subsequent use by the IOM in generating a 24-bit
3117 //         address for list of data services for the extended address modes."
3118 // see also 3.2.3.1
3119   p -> ADDR_EXT     = p -> PCW_AE;
3120 
3121   p -> lsFirst      = true;
3122 
3123   p -> tallyResidue = 0;
3124   p -> isRead       = true;
3125   p -> charPos      = 0;
3126 // As far as I can tell, initiate false means that an IOTx succeeded in
3127 // transferring data; assume it didn't since that is the most common
3128 // code path.
3129   p -> initiate     = true;
3130   p -> chanStatus   = chanStatNormal;
3131 
3132 //
3133 // Send the PCW's DCW
3134 //
3135   int rc = d->iom_cmd (iomUnitIdx, chan);
3136 
3137   if (rc < 0) {
3138     p -> dev_code = getbits36_6 (p -> DCW, 6);
3139 #if defined(POLTS_TESTING)
3140 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3141                         sim_printf ("// terminate 9. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3142 #endif
3143     goto terminate;
3144   }
3145 
3146   if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_MARKER) { // IDCW marker bit set
3147     send_marker_interrupt (iomUnitIdx, (int) chan);
3148   }
3149 
3150   if (rc == IOM_CMD_DISCONNECT) {
3151 #if defined(POLTS_TESTING)
3152 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3153                         ("// terminate 8. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3154 #endif
3155     goto terminate;
3156   }
3157 
3158   if (p->masked) {
3159 #if defined(POLTS_TESTING)
3160 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3161                         ("// terminate 7. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3162 #endif
3163     goto terminate;
3164   }
3165 
3166   bool ptro, send, uff;
3167   bool terminate = false;
3168   p->isPCW       = false;
3169 
3170 #if defined(TESTING)
3171   bool first = true;
3172 #endif
3173 
3174   (void)terminate;
3175   bool idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3176   do {
3177     int rc2 = iom_list_service (iomUnitIdx, chan, & ptro, & send, & uff);
3178     if (rc2 < 0) {
3179 // XXX set status flags
3180       sim_warn ("%s list service failed\n", __func__);
3181       return -1;
3182     }
3183     if (uff) {
3184       // We get a uff if the LPW tally hit 0
3185 #if defined(POLTS_TESTING)
3186 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3187                         sim_printf ("// terminate 6. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3188 #endif
3189       goto terminate;
3190     }
3191     // List service failed to get a DCW
3192     if (! send) {
3193       sim_warn ("%s nothing to send\n", __func__);
3194       return 1;
3195     }
3196 
3197 #if defined(TESTING)
3198 # if defined(POLTS_TESTING)
3199 if (iomUnitIdx == 1 && chan == 020)
3200 # endif
3201     if_sim_debug (DBG_TRACE, & iom_dev) {
3202       if (first) {
3203         first = false;
3204         dumpLPW (iomUnitIdx, chan);
3205         sim_printf ("// DCW list dump: \r\n");
3206         dumpDCW (PCW_DCW, PCW_LPW_23_REL);
3207         for (int i = 0; i < 8; i ++)
3208           dumpDCW (M[p->LPW_DCW_PTR - 1 + i], p->LPW_23_REL);
3209         sim_printf ("// \r\n");
3210       }
3211     }
3212 #endif
3213 
3214 // 3.2.3.1 "If EC - Bit 21 = 1, The channel will replace the present address
3215 //          extension with the new address extension in bits 12-17. ... In
3216 //          Multics and NSA systems, EC is inhibited from the payload channel
3217 //          if LPW bit 23 = 1.
3218 
3219     if (IS_IDCW (p)) { // IDCW
3220       idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3221       if (p -> LPW_23_REL == 0 && p -> IDCW_EC == 1)
3222         p -> ADDR_EXT = getbits36_6 (p -> DCW, 12);
3223 
3224       p -> tallyResidue = 0;
3225       p -> isRead       = true;
3226       p -> charPos      = 0;
3227       p -> chanStatus   = chanStatNormal;
3228     }
3229 
3230 // Send the DCW list's DCW
3231 
3232     rc2 = d->iom_cmd (iomUnitIdx, chan);
3233 
3234     if (rc2 < 0) {
3235       p -> dev_code = getbits36_6 (p -> DCW, 6);
3236 #if defined(POLTS_TESTING)
3237 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3238                         sim_printf ("// terminate 5. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3239 #endif
3240       goto terminate;
3241     }
3242 
3243     if (rc2 == IOM_CMD_DISCONNECT) {
3244       terminate = true;
3245 #if defined(POLTS_TESTING)
3246 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf \
3247                         ("// terminate 4. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3248 #endif
3249     }
3250 
3251     if (rc2 == IOM_CMD_PENDING) // handler still processing command, don't set
3252       goto pending;             // terminate interrupt.
3253 
3254     // If IDCW and terminate and nondata
3255     if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE && p->IDCW_CHAN_CMD == CHAN_CMD_NONDATA) {
3256 #if defined(POLTS_TESTING)
3257 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3258                         sim_printf ("// terminate 1. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3259 #endif
3260       goto terminate;
3261     }
3262     // If IOTD and last IDCW was terminate
3263     if (IS_IOTD (p) && idcw_terminate && rc2 != IOM_CMD_RESIDUE) {
3264 #if defined(POLTS_TESTING)
3265 //if (iomUnitIdx == 1 && chan == 020)      if_sim_debug (DBG_TRACE, & iom_dev)
3266 //                                           sim_printf ("// ctrl == 0 in chan %d (%o) IOTP\n", chan, chan);
3267 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) \
3268                         sim_printf ("// terminate 2. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3269 #endif
3270       goto terminate;
3271     }
3272 
3273     // IOM_CMD_RESIDUE: Continue pushing DCWS until the record residue is 0
3274 
3275     if (IS_NOT_IDCW (p) && rc2 == IOM_CMD_RESIDUE) {
3276       if (p->recordResidue)
3277         p->recordResidue --;
3278       if (p->recordResidue == 0)
3279         goto terminate;
3280     }
3281   } while (! terminate);
3282 
3283 terminate:;
3284 #if defined(POLTS_TESTING)
3285 if (iomUnitIdx == 1 && chan == 020) sim_printf ("stati %04o\r\n", p->stati);
3286 #endif
3287   send_terminate_interrupt (iomUnitIdx, chan);
3288   return 0;
3289 
3290 pending:;
3291   return 0;
3292 }
3293 
3294 // doConnectChan ()
3295 //
3296 // Process the "connect channel".  This is what the IOM does when it
3297 // receives a $CON signal.
3298 //
3299 // Only called by iom_interrupt ()
3300 //
3301 // The connect channel requests one or more "list services" and processes the
3302 // resulting PCW control words.
3303 //
3304 
3305 static int doConnectChan (uint iom_unit_idx) {
     /* [previous][next][first][last][top][bottom][index][help] */
3306   // ... the connect channel obtains a list service from the IOM Central.
3307   // During this service the IOM Central will do a double precision read
3308   // from the core, under the control of the LPW for the connect channel.
3309   //
3310   // If the IOM Central does not indicate a PTRO during the list service,
3311   // the connect channel obtains another list service.
3312   //
3313   // The connect channel does not interrupt or store status.
3314   //
3315   // The DCW and SCW mailboxes for the connect channel are not used by
3316   // the IOM.
3317 #if defined(TESTING)
3318   cpu_state_t * cpup = _cpup;
3319 #endif
3320   sim_debug (DBG_DEBUG, & iom_dev, "%s: Connect channel\n", __func__);
3321   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][IOM_CONNECT_CHAN];
3322   p -> lsFirst = true;
3323 #if defined(TESTING) && defined(POLTS_TESTING)
3324   bool first = true;
3325 #endif
3326   bool ptro, send, uff;
3327   do {
3328     // Fetch the next PCW
3329     int rc = iom_list_service (iom_unit_idx, IOM_CONNECT_CHAN, & ptro, & send, & uff);
3330     if (rc < 0) {
3331       sim_warn ("connect channel connect failed\n");
3332       return -1;
3333     }
3334     if (uff) {
3335       sim_warn ("connect channel ignoring uff\n"); // XXX
3336     }
3337     if (! send) {
3338       sim_warn ("connect channel nothing to send\n");
3339     } else {
3340 #if defined(xTESTING)
3341       if_sim_debug (DBG_TRACE, & iom_dev) {
3342         if (first) {
3343           first = false;
3344           sim_printf ("// CIOC %lld\r\n", cpu.cycleCnt);
3345           dumpLPW (iom_unit_idx, IOM_CONNECT_CHAN);
3346           sim_printf ("// PCW %012llo %012llo\r\n",    p->PCW0, p->PCW1);
3347           sim_printf ("//   Chan info     %04o\r\n",   p->PCW_CHAN);
3348           sim_printf ("//   Addr ext        %02o\r\n", p->PCW_AE);
3349           sim_printf ("//   111              %o\r\n",  getbits36_3 (p->PCW0, 18));
3350           sim_printf ("//   M                %o\r\n",  p->PCW_21_MSK);
3351           sim_printf ("//   Chan info %08o\r\n",       getbits36_14 (p->PCW0, 22));
3352           sim_printf ("//   Pg Tbl Ptr  %06o\r\n",     p->PCW_PAGE_TABLE_PTR);
3353           sim_printf ("//   PTP              %o\r\n",  p->PCW_63_PTP);
3354           sim_printf ("//   PGE              %o\r\n",  p->PCW_64_PGE);
3355           sim_printf ("//   AUX              %o\r\n",  p->PCW_65_AUX);
3356           sim_printf ("//\r\n");
3357         }
3358       }
3359 #endif
3360       // Copy the PCW's DCW to the payload channel
3361       iom_chan_data_t * q = & iom_chan_data[iom_unit_idx][p -> PCW_CHAN];
3362 
3363       q -> PCW0               = p -> PCW0;
3364       q -> PCW1               = p -> PCW1;
3365       q -> PCW_CHAN           = p -> PCW_CHAN;
3366       q -> PCW_AE             = p -> PCW_AE;
3367       q -> PCW_PAGE_TABLE_PTR = p -> PCW_PAGE_TABLE_PTR;
3368       q -> PCW_63_PTP         = p -> PCW_63_PTP;
3369       q -> PCW_64_PGE         = p -> PCW_64_PGE;
3370       q -> PCW_65_AUX         = p -> PCW_65_AUX;
3371       q -> PCW_21_MSK         = p -> PCW_21_MSK;
3372       q -> DCW                = p -> DCW;
3373 
3374       sim_debug (DBG_DEBUG, & iom_dev, "%s: PCW %012llo %012llo chan %02o\n", __func__, q->PCW0, q->PCW1, q->PCW_CHAN);
3375 
3376       q -> masked = p -> PCW_21_MSK;
3377       if (q -> masked) {
3378         if (q -> in_use)
3379           sim_warn ("%s: chan %d masked while in use\n", __func__, p -> PCW_CHAN);
3380         q -> in_use = false;
3381         q -> start  = false;
3382       } else {
3383         if (q -> in_use)
3384           sim_warn ("%s: chan %d connect while in use\n", __func__, p -> PCW_CHAN);
3385         q -> in_use = true;
3386         q -> start  = true;
3387 #if defined(IO_THREADZ)
3388         setChnConnect (iom_unit_idx, p -> PCW_CHAN);
3389 #else
3390 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3391         doPayloadChannel (iom_unit_idx, p -> PCW_CHAN);
3392 # endif
3393 # if defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3394         pthread_cond_signal (& iomCond);
3395 # endif
3396 #endif
3397       }
3398     }
3399   } while (! ptro);
3400   return 0; // XXX
3401 }
3402 
3403 /*
3404  * send_marker_interrupt ()
3405  *
3406  * Send a "marker" interrupt to the CPU.
3407  *
3408  * Channels send marker interrupts to indicate normal completion of
3409  * a PCW or IDCW if the control field of the PCW/IDCW has a value
3410  * of three.
3411  */
3412 
3413 int send_marker_interrupt (uint iom_unit_idx, int chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3414   {
3415     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3416       return(0);
3417     status_service (iom_unit_idx, (uint) chan, true);
3418     return send_general_interrupt (iom_unit_idx, (uint) chan, imwMarkerPic);
3419   }
3420 
3421 /*
3422  * send_special_interrupt ()
3423  *
3424  * Send a "special" interrupt to the CPU.
3425  *
3426  */
3427 
3428 int send_special_interrupt (uint iom_unit_idx, uint chan, uint devCode,
     /* [previous][next][first][last][top][bottom][index][help] */
3429                             word8 status0, word8 status1)
3430   {
3431 //if (iom_unit_idx == 0 && chan == 013) sim_printf ("spec int %03o %013o\n", status0, status1);
3432     uint chanloc = mbxLoc (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN);
3433 
3434     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3435       return(0);
3436 
3437 #if defined(LOCKLESS)
3438     lock_iom();
3439 #endif
3440 
3441 // Multics uses an 12(8) word circular queue, managed by clever manipulation
3442 // of the LPW and DCW.
3443 // Rather then goes through the mechanics of parsing the LPW and DCW,
3444 // we will just assume that everything is set up the way we expect,
3445 // and update the circular queue.
3446     word36 lpw;
3447     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
3448 
3449     word36 scw;
3450     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
3451 
3452     word36 dcw;
3453     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
3454 
3455     word36 status  = 0400000000000ull;
3456     status        |= (((word36) chan)    & MASK6) << 27;
3457     status        |= (((word36) devCode) & MASK8) << 18;
3458     status        |= (((word36) status0) & MASK8) <<  9;
3459     status        |= (((word36) status1) & MASK8) <<  0;
3460     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, status, __func__);
3461 
3462     uint tally = dcw & MASK12;
3463     if (tally > 1)
3464       {
3465         dcw -= 01llu;  // tally --
3466         dcw += 01000000llu; // addr ++
3467       }
3468     else
3469       dcw = scw; // reset to beginning of queue
3470     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
3471 
3472 #if defined(LOCKLESS)
3473     unlock_iom();
3474 #endif
3475 
3476     send_general_interrupt (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN, imwSpecialPic);
3477     return 0;
3478   }
3479 
3480 /*
3481  * send_terminate_interrupt ()
3482  *
3483  * Send a "terminate" interrupt to the CPU.
3484  *
3485  * Channels send a terminate interrupt after doing a status service.
3486  *
3487  */
3488 
3489 int send_terminate_interrupt (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3490   {
3491     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3492       return 0;
3493     status_service (iom_unit_idx, chan, false);
3494     if (iom_chan_data [iom_unit_idx] [chan] . in_use == false)
3495       sim_warn ("%s: chan %d not in use\n", __func__, chan);
3496     iom_chan_data [iom_unit_idx] [chan] . in_use = false;
3497     send_general_interrupt (iom_unit_idx, chan, imwTerminatePic);
3498     return 0;
3499   }
3500 
3501 void iom_interrupt (uint scu_unit_idx, uint iom_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
3502   {
3503     cpu_state_t * cpup = _cpup;
3504     sim_debug (DBG_DEBUG, & iom_dev,
3505                "%s: IOM %c starting. [%"PRId64"] %05o:%08o\n",
3506                __func__, iomChar (iom_unit_idx), cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC);
3507 
3508     iom_unit_data[iom_unit_idx].invokingScuUnitIdx = scu_unit_idx;
3509 
3510 #if defined(IO_THREADZ)
3511     setIOMInterrupt (iom_unit_idx);
3512     iomDoneWait (iom_unit_idx);
3513 #else
3514     int ret = doConnectChan (iom_unit_idx);
3515 
3516     sim_debug (DBG_DEBUG, & iom_dev,
3517                "%s: IOM %c finished; doConnectChan returned %d.\n",
3518                __func__, iomChar (iom_unit_idx), ret);
3519 #endif
3520     // XXX doConnectChan return value ignored
3521   }
3522 
3523 #if defined(IO_THREADZ)
3524 void * chan_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3525   {
3526     uint myid     = (uint) * (int *) arg;
3527     this_iom_idx  = (uint) myid / MAX_CHANNELS;
3528     this_chan_num = (uint) myid % MAX_CHANNELS;
3529 
3530 // Set CPU context to allow sim_debug to work
3531 
3532     set_cpu_idx (0);
3533 
3534     sim_msg ("\rIOM %c Channel %u thread created\r\n", this_iom_idx + 'a', this_chan_num);
3535 # if defined(TESTING)
3536     printPtid(pthread_self());
3537 # endif /* if defined(TESTING) */
3538     sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
3539 
3540     setSignals ();
3541     while (1)
3542       {
3543 //sim_printf("IOM %c Channel %u thread waiting\n", this_iom_idx + 'a', this_chan_num);
3544         chnConnectWait ();
3545 //sim_printf("IOM %c Channel %u thread running\n", this_iom_idx + 'a', this_chan_num);
3546         doPayloadChannel (this_iom_idx, this_chan_num);
3547         chnConnectDone ();
3548       }
3549   }
3550 
3551 void * iom_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3552   {
3553     int myid = * (int *) arg;
3554     this_iom_idx = (uint) myid;
3555 
3556 // Set CPU context to allow sim_debug to work
3557 
3558     set_cpu_idx (0);
3559 
3560     sim_printf("IOM %c thread created\n", 'a' + myid);
3561 
3562     setSignals ();
3563     while (1)
3564       {
3565 //sim_printf("IOM %c thread waiting\n", 'a' + myid);
3566         iomInterruptWait ();
3567 //sim_printf("IOM %c thread running\n", 'a' + myid);
3568         int ret = doConnectChan (this_iom_idx);
3569 
3570         sim_debug (DBG_DEBUG, & iom_dev,
3571                    "%s: IOM %c finished; doConnectChan returned %d.\n",
3572                    __func__, iomChar (myid), ret);
3573         iomInterruptDone ();
3574       }
3575   }
3576 #endif
3577 
3578 /*
3579  * iom_init ()
3580  *
3581  *  Once-only initialization
3582  */
3583 
3584 void iom_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3585   {
3586     //sim_debug (DBG_INFO, & iom_dev, "%s: running.\n", __func__);
3587   }
3588 
3589 #if defined(PANEL68)
3590 void do_boot (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3591   {
3592     boot_svc (& boot_channel_unit[0]);
3593   }
3594 #endif
3595 
3596 #if defined(IO_ASYNC_PAYLOAD_CHAN) || defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3597 void iomProcess (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3598   {
3599     for (uint i = 0; i < N_IOM_UNITS_MAX; i++)
3600       for (uint j = 0; j < MAX_CHANNELS; j++)
3601         {
3602           iom_chan_data_t * p = &iom_chan_data [i] [j];
3603           if (p -> start)
3604             {
3605               p -> start = false;
3606               doPayloadChannel (i, j);
3607             }
3608         }
3609   }
3610 #endif
3611 
3612 char iomChar (uint iomUnitIdx)
     /* [previous][next][first][last][top][bottom][index][help] */
3613   {
3614     return (iom_unit_data[iomUnitIdx].configSwMultiplexBaseAddress & 3) + 'A';
3615   }
3616 

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