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  * All rights reserved.
  15  *
  16  * This software is made available under the terms of the ICU
  17  * License, version 1.8.1 or later.  For more details, see the
  18  * LICENSE.md file at the top-level directory of this distribution.
  19  *
  20  * ---------------------------------------------------------------------------
  21  *
  22  * This source file may contain code comments that adapt, include, and/or
  23  * incorporate Multics program code and/or documentation distributed under
  24  * the Multics License.  In the event of any discrepancy between code
  25  * comments herein and the original Multics materials, the original Multics
  26  * materials should be considered authoritative unless otherwise noted.
  27  * For more details and historical background, see the LICENSE.md file at
  28  * the top-level directory of this distribution.
  29  *
  30  * ---------------------------------------------------------------------------
  31  */
  32 
  33 //
  34 // IOM rework # 7
  35 //
  36 
  37 // Implementation notes:
  38 //
  39 // The IOM can be configured to operate in GCOS, Extended GCOS, and Paged modes.
  40 // Only the Paged mode is supported.
  41 //
  42 // Backup list service not implemented
  43 // Wraparound channel not implemented
  44 // Snapshot channel not implemented
  45 // Scratchpad Access channel not implemented
  46 
  47 // Direct data service
  48 
  49 // Pg: B20 "Address Extension bits will be used for core selection when the
  50 //          "the IOM is [...] in Paged mode for a non-paged channel.
  51 
  52 // Definitions:
  53 //  unit number -- ambiguous: May indicate either the Multics
  54 //                 designation for IOMA, IOMB, etc. or the scp
  55 //                 UNIT table offset.
  56 //    unit_idx --  always refers to the scp unit table offset.
  57 //
  58 //
  59 
  60 // Functional layout:
  61 
  62 // CPU emulator:
  63 //   CIOC command -> SCU cable routing to IOM emulator "iom_connect"
  64 //
  65 // IOM emulator:
  66 //   iom_connect performs the connect channel operations of fetching
  67 //   the PCW, parsing out the channel number and initial DCW.
  68 //
  69 //   The channel number and initial DCW are passed to doPayloadChannel
  70 //
  71 //   Using the cable tables, doPayloadChannel determines the handler and
  72 //   unit_idx for that channel number and calls
  73 //     iom_unit[channel_number].iom_cmd (unit_idx, DCW)
  74 //   It then walks the channel DCW list, passing DCWs to the handler.
  75 //
  76 
  77 //        make_idcw IDCW_name,
  78 //                  Command,
  79 //                  Device,
  80 //                  {record,multirecord,character,nondata},
  81 //                  {proceed,terminate,marker},
  82 //                  [ChanData,]
  83 //                  [Extension,]
  84 //                  [mask,]
  85 //
  86 //   record      0
  87 //   nondata     2
  88 //   multirecord 6
  89 //   character   8
  90 //
  91 //   terminate   0
  92 //   proceed     2
  93 //   marker      3
  94 //
  95 // Observed channel command dialects
  96 //
  97 // IOM boot
  98 // CIOC 0
  99 // Connect channel LPW 000000040000 (case c)
 100 //   DCW pointer 000000
 101 //     RES              0
 102 //     REL              0
 103 //     AE               0
 104 //     NC 21            1
 105 //     TAL 22           0
 106 //     REL              0
 107 //     TALLY         0000
 108 //
 109 // PCW 000000720201 012000000000
 110 //   Chan info     0012
 111 //   Addr ext        00
 112 //   111              7
 113 //   M                0
 114 //   Chan info  22046447
 115 //   Pg Tbl Ptr  000000
 116 //   PTP              0
 117 //   PGE              0
 118 //   AUX              0
 119 //
 120 // MTP Tape channel LPW 000004020002 (case b)
 121 //   DCW pointer 000004
 122 //     RES              0
 123 //     REL              0
 124 //     AE               0
 125 //     NC 21            0
 126 //     TAL 22           1
 127 //     REL              0
 128 //     TALLY         0002
 129 //
 130 // DCW list dump:
 131 //   IDCW 000000720201
 132 //     cmd             00 Request Status
 133 //     dev code        00
 134 //     ctrl             2 (proceed)
 135 //     chancmd          2 (nondata)
 136 //   IDCW 050000700000
 137 //     cmd             05 Rd Bin Rcrd
 138 //     dev code        00
 139 //     ctrl             0 (terminate)
 140 //     chancmd          0 (record)
 141 //   IOTD (0) 000030000000
 142 //     tally       0000
 143 //     addr            30
 144 //     cp               0
 145 //   IOTD (0) 400000000000000000000
 146 //     tally       0000
 147 //     addr            00
 148 //     cp               0
 149 //   IOTD (0) 400000000000000000000
 150 //     tally       0000
 151 //     addr            00
 152 //     cp               0
 153 //
 154 // Tape commands done:
 155 //   Tape Request Status
 156 //   Tape Read Binary Record
 157 //   IOTD (0) 000030000000
 158 //     tally       0000
 159 //     addr            30
 160 //     cp               0
 161 //   Tape IOT Read
 162 //     Tape 0 reads record 1
 163 //   ctrl == 0 in chan 10 (12) DCW
 164 
 165 // Bootload check tape controller firmware loaded test
 166 //
 167 // CIOC 138
 168 // Connect channel LPW 000000040000 (case c)
 169 //   DCW pointer 000000
 170 //     RES              0
 171 //     REL              0
 172 //     AE               0
 173 //     NC 21            1
 174 //     TAL 22           0
 175 //     REL              0
 176 //     TALLY         0000
 177 //
 178 // PCW 000000720201 012000000000
 179 //   Chan info     0012
 180 //   Addr ext        00
 181 //   111              7
 182 //   M                0
 183 //   Chan info    22046447
 184 //   Pg Tbl Ptr  000000
 185 //   PTP              0
 186 //   PGE              0
 187 //   AUX              0
 188 //
 189 // MTP Tape channel LPW 000540007777 (case a)
 190 //   DCW pointer 000540
 191 //     RES              0
 192 //     REL              0
 193 //     AE               0
 194 //     NC 21            0
 195 //     TAL 22           0
 196 //     REL              0
 197 //     TALLY         7777
 198 //
 199 // DCW list dump:
 200 //   IDCW 000000720201
 201 //     cmd             00 Request Status
 202 //     dev code        00
 203 //     ctrl             2 (proceed)
 204 //     chancmd          2 (nondata)
 205 //   IDCW 510000700200
 206 //     cmd             51 Reset Dev Stat/Alert
 207 //     dev code        00
 208 //     ctrl             0 (terminate)
 209 //     chancmd          2 (nondata)
 210 //   IDCW 000000700200
 211 //     cmd             00 Request Status
 212 //     dev code        00
 213 //     ctrl             0 (terminate)
 214 //     chancmd          2 (nondata)
 215 //   IOTD (0) 200000000000
 216 //     tally       0000
 217 //     addr            200000
 218 //     cp               0
 219 //
 220 // Tape commands done:
 221 //   Tape Request Status
 222 //   Tape Reset Device Status
 223 //   ctrl == 0 in chan 10 (12) DCW
 224 
 225 // Bootload tape read
 226 //
 227 // CIOC 7999826
 228 // Connect channel LPW 000000040000 (case c)
 229 //   DCW pointer 000000
 230 //     RES              0
 231 //     REL              0
 232 //     AE               0
 233 //     NC 21            1
 234 //     TAL 22           0
 235 //     REL              0
 236 //     TALLY         0000
 237 //
 238 // PCW 000000720201 012000000000
 239 //   Chan info     0012
 240 //   Addr ext        00
 241 //   111              7
 242 //   M                0
 243 //   Chan info    22046447
 244 //   Pg Tbl Ptr  000000
 245 //   PTP              0
 246 //   PGE              0
 247 //   AUX              0
 248 //
 249 // MTP Tape channel LPW 000621007777 (case a)
 250 //   DCW pointer 000621
 251 //     RES              0
 252 //     REL              0
 253 //     AE               0
 254 //     NC 21            0
 255 //     TAL 22           0
 256 //     REL              0
 257 //     TALLY         7777
 258 //
 259 // DCW list dump:
 260 //   IDCW 000000720201
 261 //     cmd             00 Request Status
 262 //     dev code        00
 263 //     ctrl             2 (proceed)
 264 //     chancmd          2 (nondata)
 265 //   IDCW 050000700000
 266 //     cmd             05 Rd Bin Rcrd
 267 //     dev code        00
 268 //     ctrl             0 (terminate)
 269 //     chancmd          0 (record)
 270 //   IOTD (0) 060001002020
 271 //     tally       2020
 272 //     addr            60001
 273 //     cp               0
 274 //   IOTD (0) 024000000000
 275 //     tally       0000
 276 //     addr            24000
 277 //     cp               0
 278 //
 279 // Tape commands done:
 280 //   Tape Request Status
 281 //   Tape Read Binary Record
 282 //   IOTD (0) 060001002020
 283 //     tally       2020
 284 //     addr            60001
 285 //     cp               0
 286 //   Tape IOT Read
 287 //   ctrl == 0 in chan 10 (12) DCW
 288 
 289 // Bootload searching for console
 290 //
 291 // CIOC 8191828
 292 // Connect channel LPW 037312020000 (case b)
 293 //   DCW pointer 037312
 294 //     RES              0
 295 //     REL              0
 296 //     AE               0
 297 //     NC 21            0
 298 //     TAL 22           1
 299 //     REL              0
 300 //     TALLY         0000
 301 //
 302 // PCW 510000700200 010000000000
 303 //   Chan info     0010
 304 //   Addr ext        00
 305 //   111              7
 306 //   M                0
 307 //   Chan info 22046447
 308 //   Pg Tbl Ptr  000000
 309 //   PTP              0
 310 //   PGE              0
 311 //   AUX              0
 312 //
 313 
 314 // Bootload finding console
 315 //
 316 // CIOC 8324312
 317 // Connect channel LPW 037312020000 (case b)
 318 //   DCW pointer 037312
 319 //     RES              0
 320 //     REL              0
 321 //     AE               0
 322 //     NC 21            0
 323 //     TAL 22           1
 324 //     REL              0
 325 //     TALLY         0000
 326 //
 327 // PCW 510000700200 036000000000
 328 //   Chan info     0036
 329 //   Addr ext        00
 330 //   111              7
 331 //   M                0
 332 //   Chan info 22046447
 333 //   Pg Tbl Ptr  000000
 334 //   PTP              0
 335 //   PGE              0
 336 //   AUX              0
 337 //
 338 // CONSOLE: ALERT
 339 
 340 // Bootload console read ID
 341 //
 342 // Connect channel LPW 037312020000 (case b)
 343 //   DCW pointer 037312
 344 //     RES              0
 345 //     REL              0
 346 //     AE               0
 347 //     NC 21            0
 348 //     TAL 22           1
 349 //     REL              0
 350 //     TALLY         0000
 351 //
 352 // PCW 570000700200 036000000000
 353 //   Chan info     0036
 354 //   Addr ext        00
 355 //   111              7
 356 //   M                0
 357 //   Chan info 22046447
 358 //   Pg Tbl Ptr  000000
 359 //   PTP              0
 360 //   PGE              0
 361 //   AUX              0
 362 //
 363 
 364 // Bootload console write ASCII
 365 //
 366 // Connect channel LPW 037312020000 (case b)
 367 //   DCW pointer 037312
 368 //     RES              0
 369 //     REL              0
 370 //     AE               0
 371 //     NC 21            0
 372 //     TAL 22           1
 373 //     REL              0
 374 //     TALLY         0000
 375 //
 376 // PCW 330000700000 036000000000
 377 //   Chan info     0036
 378 //   Addr ext        00
 379 //   111              7
 380 //   M                0
 381 //   Chan info 22046447
 382 //   Pg Tbl Ptr  000000
 383 //   PTP              0
 384 //   PGE              0
 385 //   AUX              0
 386 //
 387 // IOTD (0) 033624000022
 388 //   tally       0022
 389 //   addr            33624
 390 //   cp               0
 391 // Console channel LPW 033357007777 (case a)
 392 //   DCW pointer 033357
 393 //     RES              0
 394 //     REL              0
 395 //     AE               0
 396 //     NC 21            0
 397 //     TAL 22           0
 398 //     REL              0
 399 //     TALLY         7777
 400 //
 401 // DCW list dump:
 402 //   IDCW 330000700000
 403 //     cmd             33 Write ASCII
 404 //     dev code        00
 405 //     ctrl             0 (terminate)
 406 //     chancmd          0 (record)
 407 //   IOTD (0) 033624000022
 408 //     tally       0022
 409 //     addr            33624
 410 //     cp               0
 411 //   IDCW 510000700000
 412 //     cmd             51 Reset Dev Stat/Alert
 413 //     dev code        00
 414 //     ctrl             0 (terminate)
 415 //     chancmd          0 (record)
 416 
 417 //
 418 //  T&D Read block 3
 419 //    Connect Channel LPW 006312040001 (case c)
 420 //      DCW pointer 006312
 421 //      RES              0
 422 //      REL              0
 423 //      AE               0
 424 //      NC 21            1
 425 //      TAL 22           0
 426 //      REL              0
 427 //      TALLY         0001
 428 //    PCW 050000700001 012000000000
 429 //      Chan info     0500
 430 //      Addr ext        00
 431 //      111              7
 432 //      M                0
 433 //      Chan info    00001
 434 //      Chan            12
 435 //      Pg Tbl Ptr  000000
 436 //      PTP              0
 437 //      PGE              0
 438 //      AUX              0
 439 //    IDCW 050000700001
 440 //      cmd             05 (Read Binary Record)
 441 //      dev             00
 442 //      ctrl             0
 443 //      chancmd          0
 444 //
 445 //    Payload channel
 446 //      IOTD 010000000000
 447 //        tally       0000
 448 //        addr      010000
 449 //
 450 
 451 // Table 3.2.1 Updating of LPW address and tally and indication of PTRO and TRO
 452 //
 453 //  case   LPW 21   LPW 22  TALLY  UPDATE   INDICATE    INDICATE
 454 //         NC       TALLY   VALUE  ADDR &   PTRO TO     TRO
 455 //                                 TALLY    CONN. CH.   FAULT
 456 //    a.     0       0       any    yes       no         no
 457 //    b.     0       1       > 2    yes       no         no
 458 //                            1     yes       yes        no
 459 //                            0     no        no         yes
 460 //    c.     1       x       any    no        yes        no
 461 //
 462 // Case a. is GCOS common Peripheral Interface payloads channels;
 463 // a tally of 0 is not a fault since the tally field will not be
 464 // used for that purpose.
 465 //
 466 // Case b. is the configuration expected of standard operation with real-time
 467 // applications and/or direct data channels.
 468 //
 469 // Case c. will be used normally only with the connect channel servicing CPI
 470 // type payload channels. [CAC: "INDICATE PTRO TO CONN. CH." means that the
 471 // connect channel should not call list service to obtain additional PCW's]
 472 //
 473 // If case a. is used with the connect channels, a "System Fault: Tally
 474 // Control Error, Connect Channel" will be generated. Case c. will be
 475 // used in the bootload program.
 476 
 477 // LPW bits
 478 //
 479 //  0-17  DCW pointer
 480 //  18    RES - Restrict IDCW's [and] changing AE flag
 481 //  19    REL - IOM controlled image of REL bit
 482 //  20    AE - Address extension flag
 483 //  21    NC
 484 //  22    TAL
 485 //  23    REL
 486 //  24-35 TALLY
 487 //
 488 
 489 // LPWX (sometimes called LPWE)
 490 //
 491 //  0-8   Lower bound
 492 //  9-17  Size
 493 //  18-35 Pointer to DCW of most recent instruction
 494 //
 495 
 496 // PCW bits
 497 //
 498 // word 1
 499 //   0-11 Channel information
 500 //  12-17 Address extension
 501 //  18-20 111
 502 //  21    M
 503 //  22-35 Channel Information
 504 //
 505 // word 2
 506 //  36-38 SBZ
 507 //  39-44 Channel Number
 508 //  45-62 Page table ptr
 509 //  63    PTP
 510 //  64    PGE
 511 //  65    AUX
 512 //  66-21 not used
 513 
 514 // IDCW
 515 //
 516 //   0-11 Channel information
 517 //  12-17 Address extension
 518 //  18-20 111
 519 //  21    EC
 520 //  22-35 Channel Information
 521 //
 522 
 523 // TDCW
 524 //
 525 // Paged, PCW 64 = 0
 526 //
 527 //   0-17 DCW pointer
 528 //  18-20 Not 111
 529 //  21    0
 530 //  22    1
 531 //  23    0
 532 //  24-32 SBZ
 533 //  33    EC
 534 //  34    RES
 535 //  35    REL
 536 //
 537 // Paged, PCW 64 = 1
 538 //
 539 //   0-17 DCW pointer
 540 //  18-20 Not 111
 541 //  21    0
 542 //  22    1
 543 //  23    0
 544 //  24-30 SBZ
 545 //  31    SEG
 546 //  32    PDTA
 547 //  33    PDCW
 548 //  34    RES
 549 //  35    REL
 550 //
 551 
 552 #include "dps8.h"
 553 #include "dps8_cpu.h"
 554 #include "dps8_sys.h"
 555 #include "dps8_faults.h"
 556 #include "dps8_scu.h"
 557 #include "dps8_iom.h"
 558 #include "dps8_cable.h"
 559 #include "dps8_console.h"
 560 #include "dps8_fnp2.h"
 561 #include "dps8_utils.h"
 562 #if defined(LOCKLESS)
 563 # include "threadz.h"
 564 #endif
 565 
 566 #define DBG_CTR 1
 567 
 568 // Nomenclature
 569 //
 570 //  IDX index   refers to emulator unit
 571 //  NUM         refers to the number that the unit is configured as ("IOMA,
 572 //              IOMB,..."). Encoded in the low to bits of configSwMultiplexBaseAddress
 573 
 574 // Default
 575 #define N_IOM_UNITS 1
 576 
 577 #define IOM_UNIT_IDX(uptr) ((uptr) - iom_unit)
 578 
 579 #define IOM_SYSTEM_FAULT_CHAN 1U
 580 #define IOM_CONNECT_CHAN 2U
 581 #define IOM_SPECIAL_STATUS_CHAN 6U
 582 
 583 iom_chan_data_t iom_chan_data[N_IOM_UNITS_MAX][MAX_CHANNELS];
 584 
 585 typedef enum iom_status_t
 586   {
 587     iomStatNormal         = 0,
 588     iomStatLPWTRO         = 1,
 589     iomStat2TDCW          = 2,
 590     iomStatBoundaryError  = 3,
 591     iomStatAERestricted   = 4,
 592     iomStatIDCWRestricted = 5,
 593     iomStatCPDiscrepancy  = 6,
 594     iomStatParityErr      = 7
 595   } iom_status_t;
 596 
 597 enum config_sw_OS_t
 598   {
 599     CONFIG_SW_STD_GCOS,
 600     CONFIG_SW_EXT_GCOS,
 601     CONFIG_SW_MULTICS  // "Paged"
 602   };
 603 
 604 enum config_sw_model_t
 605   {
 606     CONFIG_SW_MODEL_IOM,
 607     CONFIG_SW_MODEL_IMU
 608   };
 609 
 610 // Boot device: CARD/TAPE;
 611 enum config_sw_bootload_device_e { CONFIG_SW_BLCT_CARD, CONFIG_SW_BLCT_TAPE };
 612 
 613 typedef struct
 614   {
 615     // Configuration switches
 616 
 617     // Interrupt multiplex base address: 12 toggles
 618     word12 configSwIomBaseAddress;
 619 
 620     // Mailbox base aka IOM base address: 9 toggles
 621     // Note: The IOM number is encoded in the lower two bits
 622 
 623     // AM81, pg 60 shows an image of a Level 68 IOM configuration panel
 624     // The switches are arranged and labeled
 625     //
 626     //  12   13   14   15   16   17   18   --   --  --  IOM
 627     //                                                  NUMBER
 628     //   X    X    X    X    X    X    X                X     X
 629     //
 630 
 631     word9 configSwMultiplexBaseAddress;
 632 
 633     enum config_sw_model_t config_sw_model; // IOM or IMU
 634 
 635     // OS: Three position switch: GCOS, EXT GCOS, Multics
 636     enum config_sw_OS_t config_sw_OS; // = CONFIG_SW_MULTICS;
 637 
 638     // Bootload device: Toggle switch CARD/TAPE
 639     enum config_sw_bootload_device_e configSwBootloadCardTape; // = CONFIG_SW_BLCT_TAPE;
 640 
 641     // Bootload tape IOM channel: 6 toggles
 642     word6 configSwBootloadMagtapeChan; // = 0;
 643 
 644     // Bootload cardreader IOM channel: 6 toggles
 645     word6 configSwBootloadCardrdrChan; // = 1;
 646 
 647     // Bootload: pushbutton
 648 
 649     // Sysinit: pushbutton
 650 
 651     // Bootload SCU port: 3 toggle AKA "ZERO BASE S.C. PORT NO"
 652     // "the port number of the SC through which which connects are to
 653     // be sent to the IOM
 654     word3 configSwBootloadPort; // = 0;
 655 
 656     // 8 Ports: CPU/IOM connectivity
 657     // Port configuration: 3 toggles/port
 658     // Which SCU number is this port attached to
 659     uint configSwPortAddress[N_IOM_PORTS]; // = { 0, 1, 2, 3, 4, 5, 6, 7 };
 660 
 661     // Port interlace: 1 toggle/port
 662     uint configSwPortInterface[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 663 
 664     // Port enable: 1 toggle/port
 665     uint configSwPortEnable[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 666 
 667     // Port system initialize enable: 1 toggle/port // XXX What is this
 668     uint configSwPortSysinitEnable[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 669 
 670     // Port half-size: 1 toggle/port // XXX what is this
 671     uint configSwPortHalfsize[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 672     // Port store size: 1 8 pos. rotary/port
 673     uint configSwPortStoresize[N_IOM_PORTS]; // = { 0, 0, 0, 0, 0, 0, 0, 0 };
 674 
 675     // other switches:
 676     //   alarm disable
 677     //   test/normal
 678     iom_status_t iomStatus;
 679 
 680     uint invokingScuUnitIdx; // the unit number of the SCU that did the connect.
 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 #ifdef IO_THREADZ
 738 __thread uint this_iom_idx;
 739 __thread uint this_chan_num;
 740 #endif
 741 
 742 #ifdef 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 #ifdef LOCKLESS
 815 # ifndef SUNLINT
 816     word36 v;
 817     LOAD_ACQ_CORE_WORD(v, addr);
 818     * data = v & DMASK;
 819 # endif /* ifndef 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 #ifdef LOCKLESS
 828 # ifndef SUNLINT
 829     word36 v;
 830     LOAD_ACQ_CORE_WORD(v, addr);
 831     * even = v & DMASK;
 832 # endif /* ifndef SUNLINT */
 833     addr++;
 834 # ifndef SUNLINT
 835     LOAD_ACQ_CORE_WORD(v, addr);
 836     * odd = v & DMASK;
 837 # endif /* ifndef SUNLINT */
 838 #else
 839     * even = M[addr ++] & DMASK;
 840     * odd =  M[addr]    & DMASK;
 841 #endif
 842   }
 843 
 844 void iom_core_write (UNUSED uint iom_unit_idx, word24 addr, word36 data, UNUSED const char * ctx)
     /* [previous][next][first][last][top][bottom][index][help] */
 845   {
 846 #ifdef LOCKLESS
 847     LOCK_CORE_WORD(addr);
 848 # ifndef SUNLINT
 849     STORE_REL_CORE_WORD(addr, data);
 850 # endif /* ifndef 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 #ifdef LOCKLESS
 859     LOCK_CORE_WORD(addr);
 860 # ifndef SUNLINT
 861     STORE_REL_CORE_WORD(addr, even);
 862 # endif /* ifndef SUNLINT */
 863     addr++;
 864     LOCK_CORE_WORD(addr);
 865 # ifndef SUNLINT
 866     STORE_REL_CORE_WORD(addr, odd);
 867 # endif /* ifndef 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 #ifdef LOCKLESS
 877     LOCK_CORE_WORD(addr);
 878 # ifndef SUNLINT
 879     word36 v;
 880     LOAD_ACQ_CORE_WORD(v, addr);
 881     * data = v & DMASK;
 882 # endif /* ifndef 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 #ifdef LOCKLESS
 891 # ifndef SUNLINT
 892     STORE_REL_CORE_WORD(addr, data);
 893 # endif /* ifndef 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 #ifdef 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 #ifdef 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     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1718     // See page 33 and AN87 for format of y-pair of status info
1719 
1720     // BUG: much of the following is not tracked
1721 
1722     word36 word1, word2;
1723     word1 = 0;
1724     putbits36_12 (& word1, 0, p -> stati);
1725     // isOdd can be set to zero; see
1726     //   https://ringzero.wikidot.com/wiki:cac-2015-10-22
1727     //putbits36_1 (& word1, 12, p -> isOdd ? 0 : 1);
1728     putbits36_1 (& word1, 13, marker ? 1 : 0);
1729     putbits36_2 (& word1, 14, 0); // software status
1730     putbits36_1 (& word1, 16, p -> initiate ? 1 : 0);
1731     putbits36_1 (& word1, 17, 0); // software abort bit
1732     putbits36_3 (& word1, 18, (word3) p -> chanStatus);
1733     //putbits36_3 (& word1, 21, iom_unit_data[iom_unit_idx].iomStatus);
1734     putbits36_3 (& word1, 21, 0);
1735 
1736 
1737 
1738 
1739     putbits36_6 (& word1, 30, p -> recordResidue);
1740 
1741     word2 = 0;
1742 
1743 
1744 
1745 
1746 
1747     putbits36_3  (& word2, 18, p -> charPos);
1748     putbits36_1  (& word2, 21, p -> isRead ? 1 : 0);
1749     putbits36_12 (& word2, 24, p -> tallyResidue);
1750 
1751     // BUG: need to write to mailbox queue
1752 
1753     // T&D tape does *not* expect us to cache original SCW, it expects us to
1754     // use the SCW loaded from tape.
1755 
1756     uint chanloc   = mbxLoc (iom_unit_idx, chan);
1757     word24 scwAddr = chanloc + IOM_MBX_SCW;
1758     word36 scw;
1759     iom_core_read_lock (iom_unit_idx, scwAddr, & scw, __func__);
1760     word18 addr    = getbits36_18 (scw, 0);   // absolute
1761     uint lq        = getbits36_2 (scw, 18);
1762     uint tally     = getbits36_12 (scw, 24);
1763 
1764     sim_debug (DBG_DEBUG, & iom_dev, "%s: Status: 0%012"PRIo64" 0%012"PRIo64"\n",
1765                __func__, word1, word2);
1766     if (lq == 3)
1767       {
1768         sim_debug (DBG_WARN, &iom_dev,
1769                    "%s: SCW for channel %d has illegal LQ\n",
1770                    __func__, chan);
1771         lq = 0;
1772       }
1773     iom_core_write2 (iom_unit_idx, addr, word1, word2, __func__);
1774 
1775     // XXX: PVS-Studio believes that tally is always == 0!?
1776     if (tally > 0 || (tally == 0 && lq != 0)) //-V560
1777       {
1778         switch (lq)
1779           {
1780             case 0:
1781               // list
1782               if (tally != 0)
1783                 {
1784                   tally --;
1785                   addr += 2;
1786                 }
1787               break;
1788 
1789             case 1:
1790               // 4 entry (8 word) queue
1791               if (tally % 8 == 1 /* || tally % 8 == -1 */)
1792                 addr -= 8;
1793               else
1794                 addr += 2;
1795               tally --;
1796               break;
1797 
1798             case 2:
1799               // 16 entry (32 word) queue
1800               if (tally % 32 == 1 /* || tally % 32 == -1 */)
1801                 addr -= 32;
1802               else
1803                 addr += 2;
1804               tally --;
1805               break;
1806           }
1807 
1808        // tally is 12 bit math
1809        if (tally & ~07777U)
1810           {
1811             sim_debug (DBG_WARN, & iom_dev,
1812                        "%s: Tally SCW address 0%o under/over flow\n",
1813                        __func__, tally);
1814             tally &= 07777;
1815           }
1816 
1817         putbits36_12 (& scw, 24, (word12) tally);
1818         putbits36_18 (& scw, 0, addr);
1819       }
1820 
1821     iom_core_write_unlock (iom_unit_idx, scwAddr, scw, __func__);
1822 
1823     // BUG: update SCW in core
1824     return 0;
1825   }
1826 
1827 static word24 UNUSED build_AUXPTW_address (uint iom_unit_idx, int chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1828   {
1829 
1830 //    0      5 6            16 17  18              21  22  23
1831 //   ---------------------------------------------------------
1832 //   | Zeroes | IOM Base Address  |                          |
1833 //   ---------------------------------------------------------
1834 //                         |    Channel Number       | 1 | 1 |
1835 //                         -----------------------------------
1836 // XXX Assuming 16 and 17 are or'ed. Pg B4 doesn't specify
1837 
1838     word12 IOMBaseAddress  = iom_unit_data[iom_unit_idx].configSwIomBaseAddress;
1839     word24 addr            = (((word24) IOMBaseAddress) & MASK12) << 6;
1840     addr                  |= ((uint) chan & MASK6) << 2;
1841     addr                  |= 03;
1842     return addr;
1843   }
1844 
1845 static word24 build_DDSPTW_address (word18 PCW_PAGE_TABLE_PTR, word8 pageNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
1846   {
1847 //    0      5 6        15  16  17  18                       23
1848 //   ----------------------------------------------------------
1849 //   | Page Table Pointer | 0 | 0 |                           |
1850 //   ----------------------------------------------------------
1851 // or
1852 //                        -------------------------------------
1853 //                        |  Direct chan addr 6-13            |
1854 //                        -------------------------------------
1855     if (PCW_PAGE_TABLE_PTR & 3)
1856       sim_warn ("%s: pcw_ptp %#o page_no  %#o\n", __func__, PCW_PAGE_TABLE_PTR, pageNumber);
1857     word24 addr = (((word24) PCW_PAGE_TABLE_PTR) & MASK18) << 6;
1858     addr |= pageNumber;
1859     return addr;
1860   }
1861 
1862 // Fetch Direct Data Service Page Table Word
1863 
1864 static void fetch_DDSPTW (uint iom_unit_idx, int chan, word18 addr)
     /* [previous][next][first][last][top][bottom][index][help] */
1865   {
1866     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1867     word24 pgte         = build_DDSPTW_address (p -> PCW_PAGE_TABLE_PTR,
1868                                       (addr >> 10) & MASK8);
1869     iom_core_read (iom_unit_idx, pgte, (word36 *) & p -> PTW_DCW, __func__);
1870     if ((p -> PTW_DCW & 0740000777747) != 04llu)
1871       sim_warn ("%s: chan %ld addr %#llo pgte %08llo ptw %012llo\n",
1872                 __func__, (long)chan, (unsigned long long)addr,
1873                 (unsigned long long)pgte, (unsigned long long)p -> PTW_DCW);
1874   }
1875 
1876 static word24 build_IDSPTW_address (word18 PCW_PAGE_TABLE_PTR, word1 seg, word8 pageNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
1877   {
1878 //    0      5 6        15  16  17  18                       23
1879 //   ----------------------------------------------------------
1880 //   | Page Table Pointer         |                           |
1881 //   ----------------------------------------------------------
1882 // plus
1883 //                    ----
1884 //                    | S |
1885 //                    | E |
1886 //                    | G |
1887 //                    -----
1888 // plus
1889 //                        -------------------------------------
1890 //                        |         DCW 0-7                   |
1891 //                        -------------------------------------
1892 
1893     word24 addr  = (((word24) PCW_PAGE_TABLE_PTR) & MASK18) << 6;
1894     addr        += (((word24) seg) & 01) << 8;
1895     addr        += pageNumber;
1896     return addr;
1897   }
1898 
1899 // Fetch Indirect Data Service Page Table Word
1900 
1901 static void fetch_IDSPTW (uint iom_unit_idx, int chan, word18 addr)
     /* [previous][next][first][last][top][bottom][index][help] */
1902   {
1903     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1904     word24 pgte         = build_IDSPTW_address (p -> PCW_PAGE_TABLE_PTR,
1905                                       p -> SEG,
1906                                       (addr >> 10) & MASK8);
1907     iom_core_read (iom_unit_idx, pgte, (word36 *) & p -> PTW_DCW, __func__);
1908     if ((p -> PTW_DCW & 0740000777747) != 04llu)
1909       sim_warn ("%s: chan %d addr %#o ptw %012llo\n",
1910                 __func__, chan, addr, (unsigned long long)p -> PTW_DCW);
1911   }
1912 
1913 static word24 build_LPWPTW_address (word18 PCW_PAGE_TABLE_PTR, word1 seg, word8 pageNumber)
     /* [previous][next][first][last][top][bottom][index][help] */
1914   {
1915 
1916 //    0      5 6        15  16  17  18                       23
1917 //   ----------------------------------------------------------
1918 //   | Page Table Pointer         |                           |
1919 //   ----------------------------------------------------------
1920 // plus
1921 //                    ----
1922 //                    | S |
1923 //                    | E |
1924 //                    | G |
1925 //                    -----
1926 // plus
1927 //                        -------------------------------------
1928 //                        |         LPW 0-7                   |
1929 //                        -------------------------------------
1930 
1931     word24 addr  = (((word24) PCW_PAGE_TABLE_PTR) & MASK18) << 6;
1932     addr        += (((word24) seg) & 01) << 8;
1933     addr        += pageNumber;
1934     return addr;
1935   }
1936 
1937 static void fetch_LPWPTW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
1938   {
1939     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1940     word24 addr         = build_LPWPTW_address (p -> PCW_PAGE_TABLE_PTR,
1941                                       p -> SEG,
1942                                       (p -> LPW_DCW_PTR >> 10) & MASK8);
1943     iom_core_read (iom_unit_idx, addr, (word36 *) & p -> PTW_LPW, __func__);
1944     if ((p -> PTW_LPW & 0740000777747) != 04llu)
1945       sim_warn ("%s: chan %d addr %#o ptw %012llo\n",
1946                 __func__, chan, addr, (unsigned long long)p -> PTW_LPW);
1947   }
1948 
1949 // 'write' means peripheral write; i.e. the peripheral is writing to core after
1950 // reading media.
1951 
1952 void iom_direct_data_service (uint iom_unit_idx, uint chan, word24 daddr, word36 * data,
     /* [previous][next][first][last][top][bottom][index][help] */
1953                               iom_direct_data_service_op op)
1954   {
1955     // The direct data service consists of one core storage cycle (Read Clear, Read
1956     // Restore or Clear Write, Double or Single Precision) using an absolute
1957     // 24-bit address supplied by the channel. This service is used by
1958     // "peripherals" capable of providing addresses from an external source,
1959     // such as the 355 Direct Interface Adapter.
1960 
1961     // The PCW received for the channel contains a Page Table Pointer
1962     // which is used as an absolute address pointing to the Page Table which
1963     // has been set up by software for the Direct Channel. All addresses
1964     // for the Direct Channel will be accessed using this Page Table if paging
1965     // has been requested in the PCW.
1966 
1967     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1968 
1969     if (p -> masked)
1970       return;
1971 
1972     if (p -> PCW_63_PTP && p -> PCW_64_PGE)
1973       {
1974         fetch_DDSPTW (iom_unit_idx, (int) chan, daddr);
1975         daddr = ((uint) getbits36_14 (p -> PTW_DCW, 4) << 10) | (daddr & MASK10);
1976       }
1977 
1978     if (op == direct_store)
1979       iom_core_write (iom_unit_idx, daddr, * data, __func__);
1980     else if (op == direct_load)
1981       iom_core_read (iom_unit_idx, daddr, data, __func__);
1982     else if (op == direct_read_clear)
1983       {
1984         iom_core_read_lock (iom_unit_idx, daddr, data, __func__);
1985         iom_core_write_unlock (iom_unit_idx, daddr, 0, __func__);
1986       }
1987   }
1988 
1989 // 'tally' is the transfer size request by Multics.
1990 // For write, '*cnt' is the number of words in 'data'.
1991 // For read, '*cnt' will be set to the number of words transferred.
1992 // Caller responsibility to allocate 'data' large enough to accommodate
1993 // 'tally' words.
1994 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] */
1995   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
1996 
1997   if (p->masked)
1998     return;
1999 
2000   // tape_ioi_io.pl1  "If the initiate bit is set in the status, no data was
2001   //                   transferred (no tape movement occurred)."
2002   p->initiate = false;
2003 
2004   uint tally = p->DDCW_TALLY;
2005   uint daddr = p->DDCW_ADDR;
2006   if (tally == 0) {
2007     tally = 4096;
2008   }
2009   p->tallyResidue = (word12) tally;
2010 
2011   if (write) {
2012     uint c = * cnt;
2013     while (p->tallyResidue) {
2014       if (c == 0)
2015         break; // read buffer exhausted; returns w/tallyResidue != 0
2016 
2017       if (! IS_IONTP (p)) {
2018         if (p->PCW_63_PTP && p->PCW_64_PGE) {
2019           fetch_IDSPTW (iom_unit_idx, (int) chan, daddr);
2020           word24 addr = ((word24) (getbits36_14 (p -> PTW_DCW, 4) << 10)) | (daddr & MASK10);
2021           iom_core_write (iom_unit_idx, addr, * data, __func__);
2022         } else {
2023           if (daddr > MASK18) { // 256K overflow
2024             sim_warn ("%s 256K ovf\n", __func__); // XXX
2025             daddr &= MASK18;
2026           }
2027 
2028           // If PTP is not set, we are in cm1e or cm2e. Both are 'EXT DCW', so
2029           // we can elide the mode check here.
2030           uint daddr2 = daddr | (uint) p->ADDR_EXT << 18;
2031           iom_core_write (iom_unit_idx, daddr2, * data, __func__);
2032         }
2033       }
2034       daddr ++;
2035       data ++;
2036       p->tallyResidue --;
2037       c --;
2038     }
2039   } else { // read
2040     uint c = 0;
2041     while (p -> tallyResidue) {
2042       if (IS_IONTP (p)) {
2043         * data = 0;
2044       } else {
2045         if (p -> PCW_63_PTP  && p -> PCW_64_PGE) {
2046           fetch_IDSPTW (iom_unit_idx, (int) chan, daddr);
2047           word24 addr = ((word24) (getbits36_14 (p -> PTW_DCW, 4) << 10)) | (daddr & MASK10);
2048           iom_core_read (iom_unit_idx, addr, data, __func__);
2049         } else {
2050           // XXX assuming DCW_ABS
2051           if (daddr > MASK18) { // 256K overflow
2052             sim_warn ("%s 256K ovf\n", __func__); // XXX
2053             daddr &= MASK18;
2054           }
2055 
2056           // If PTP is not set, we are in cm1e or cm2e. Both are 'EXT DCW', so
2057           // we can elide the mode check here.
2058           uint daddr2 = daddr | (uint) p -> ADDR_EXT << 18;
2059           iom_core_read (iom_unit_idx, daddr2, data, __func__);
2060         }
2061       }
2062       daddr ++;
2063       p->tallyResidue --;
2064       data ++;
2065       c ++;
2066     }
2067     * cnt = c;
2068   }
2069 }
2070 
2071 static void update_chan_mode (uint iom_unit_idx, uint chan, bool tdcw)
     /* [previous][next][first][last][top][bottom][index][help] */
2072   {
2073     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2074     if (chan == IOM_CONNECT_CHAN)
2075       {
2076         p -> chanMode = cm1;
2077         return;
2078       }
2079 
2080     if (! tdcw)
2081       {
2082         if (p -> PCW_64_PGE == 0)
2083           {
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 
2100                 if (p -> LPW_23_REL == 0)
2101                   {
2102                     p -> chanMode = cm1;  // AE 0, REL 0
2103                   }
2104                 else
2105                   {
2106                     p -> chanMode = cm3a;  // AE 0, REL 1
2107                   }
2108 
2109               }
2110             else  // AE 1
2111               {
2112 
2113                 if (p -> LPW_23_REL == 0)
2114                   {
2115                     p -> chanMode = cm3b;  // AE 1, REL 0
2116                   }
2117                 else
2118                   {
2119                     p -> chanMode = cm4;  // AE 1, REL 1
2120                   }
2121 
2122               }
2123           }
2124       }
2125     else // tdcw
2126       {
2127         switch (p -> chanMode)
2128           {
2129             case cm1:
2130               if (p -> TDCW_32_PDTA)
2131                 {
2132                   p -> chanMode = cm2;
2133                   break;
2134                 }
2135               if (p -> TDCW_33_PDCW)
2136                 if (p -> TDCW_35_REL)
2137                   p -> chanMode = cm4; // 33, 35
2138                 else
2139                   p -> chanMode = cm3b; // 33, !35
2140               else
2141                 if (p -> TDCW_35_REL)
2142                   p -> chanMode = cm3a; // !33, 35
2143                 else
2144                   p -> chanMode = cm2; // !33, !35
2145               break;
2146 
2147             case cm2:
2148               if (p -> TDCW_33_PDCW)
2149                 if (p -> TDCW_35_REL)
2150                   p -> chanMode = cm4; // 33, 35
2151                 else
2152                   p -> chanMode = cm3b; // 33, !35
2153               else
2154                 if (p -> TDCW_35_REL)
2155                   p -> chanMode = cm3a; // !33, 35
2156                 else
2157                   p -> chanMode = cm2; // !33, !35
2158               break;
2159 
2160             case cm3a:
2161               if (p -> TDCW_33_PDCW)
2162                 p -> chanMode = cm4;
2163               break;
2164 
2165             case cm3b:
2166               if (p -> TDCW_35_REL)
2167                 p -> chanMode = cm4;
2168               break;
2169 
2170             case cm4:
2171               p -> chanMode = cm5;
2172               break;
2173 
2174             case cm5:
2175               break;
2176 
2177             case cm1e:
2178               {
2179                 if (p -> chanMode == cm1e && p -> TDCW_33_EC)
2180                   p -> chanMode = cm2e;
2181               }
2182               break;
2183 
2184             case cm2e:
2185               break;
2186           } // switch
2187       } // tdcw
2188   }
2189 
2190 static void write_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2191   {
2192     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2193 
2194     uint chanLoc = mbxLoc (iom_unit_idx, chan);
2195     iom_core_write (iom_unit_idx, chanLoc + IOM_MBX_LPW, p -> LPW, __func__);
2196     //sim_debug (DBG_DEBUG, & iom_dev,
2197                //"%s: chan %d lpw %012"PRIo64"\n",
2198                //__func__, chan, p -> LPW);
2199     if (chan != IOM_CONNECT_CHAN)
2200       {
2201         iom_core_write (iom_unit_idx, chanLoc + IOM_MBX_LPWX, p -> LPWX, __func__);
2202       }
2203   }
2204 
2205 #ifdef TESTING
2206 void dumpDCW (word36 DCW, word1 LPW_23_REL) {
     /* [previous][next][first][last][top][bottom][index][help] */
2207   static char * charCtrls[4] = {"terminate", "undefined", "proceed", "marker"};
2208   static char * chanCmds[16] = {"record", "undefined", "nondata", "undefined", "undefined", "undefined", "multirecord", "undefined", "character", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined"};
2209   word3 DCW_18_20_CP =      getbits36_3 (DCW, 18);
2210 
2211   if (DCW_18_20_CP == 07) { // IDCW
2212     word6 IDCW_DEV_CMD  =     getbits36_6 (DCW,  0);
2213     word6 IDCW_DEV_CODE =     getbits36_6 (DCW,  6);
2214  /* word6 IDCW_AE =           getbits36_6 (DCW,  12);
2215   * word1 IDCW_EC;
2216   * if (LPW_23_REL)
2217   *   IDCW_EC = 0;
2218   * else
2219   *   IDCW_EC =               getbits36_1 (DCW, 21); */
2220     word2 IDCW_CHAN_CTRL =    getbits36_2 (DCW, 22);
2221     word6 IDCW_CHAN_CMD  =    getbits36_6 (DCW, 24);
2222     word6 IDCW_COUNT     =    getbits36_6 (DCW, 30);
2223     sim_printf ("//   IDCW %012llo\r\n", DCW);
2224     sim_printf ("//     cmd             %02o %s\r\n", IDCW_DEV_CMD, cmdNames[IDCW_DEV_CMD]);
2225     sim_printf ("//     dev code        %02o\r\n", IDCW_DEV_CODE);
2226     sim_printf ("//     ctrl             %o (%s)\r\n", IDCW_CHAN_CTRL, charCtrls[IDCW_CHAN_CTRL]);
2227     sim_printf ("//     chancmd          %o (%s)\r\n", IDCW_CHAN_CMD, IDCW_CHAN_CMD < 16 ? chanCmds[IDCW_CHAN_CMD] : "unknown");
2228     sim_printf ("//     count            %o\r\n", IDCW_COUNT);
2229   } else { // TDCW or DDCW
2230     word18 TDCW_DATA_ADDRESS = getbits36_18 (DCW,  0);
2231     word1  TDCW_31_SEG       = getbits36_1  (DCW, 31);
2232     word1  TDCW_32_PDTA      = getbits36_1  (DCW, 32);
2233     word1  TDCW_33_PDCW      = getbits36_1  (DCW, 33);
2234     word1  TDCW_33_EC        = getbits36_1  (DCW, 33);
2235     word1  TDCW_34_RES       = getbits36_1  (DCW, 34);
2236     word1  TDCW_35_REL       = getbits36_1  (DCW, 35);
2237 
2238     word12 DDCW_TALLY        = getbits36_12 (DCW, 24);
2239     word18 DDCW_ADDR         = getbits36_18 (DCW,  0);
2240     word2  DDCW_22_23_TYPE   = getbits36_2  (DCW, 22);
2241     static char * types[4]   = { "IOTD", "IOTP", "TDCW", "IONTP" };
2242     if (DDCW_22_23_TYPE == 2) {
2243       sim_printf ("//   TDCW (2) %012llo\r\n", DCW);
2244       sim_printf ("//     dcw ptr   %06o\r\n", TDCW_DATA_ADDRESS);
2245       sim_printf ("//     seg            %o\r\n", TDCW_31_SEG);
2246       sim_printf ("//     pdta           %o\r\n", TDCW_32_PDTA);
2247       sim_printf ("//     pdcw           %o\r\n", TDCW_33_PDCW);
2248       sim_printf ("//     ec             %o\r\n", TDCW_33_EC);
2249       sim_printf ("//     res            %o\r\n", TDCW_34_RES);
2250       sim_printf ("//     rel            %o\r\n", TDCW_35_REL);
2251     } else {
2252       sim_printf ("//   %s (%o) %012llo\r\n", types[DDCW_22_23_TYPE], DDCW_22_23_TYPE, DCW);
2253       sim_printf ("//     tally       %04o\r\n", DDCW_TALLY);
2254       sim_printf ("//     addr            %02o\r\n", DDCW_ADDR);
2255       sim_printf ("//     cp               %o\r\n", getbits36_3 (DCW, 18));
2256     }
2257   }
2258 }
2259 
2260 static void dumpLPW (uint iom_unit_idx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
2261   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2262   char updateCase;
2263   if (p->LPW_21_NC)
2264     updateCase = 'c';
2265   else if (p->LPW_22_TAL)
2266     updateCase = 'b';
2267   else
2268     updateCase = 'a';
2269   static char * chanName[64] = {
2270         "0.",           //  0.   0
2271         "1.",           //  1.   1
2272         "Connect",      //  2.   2
2273         "3.",           //  3.   3
2274         "4.",           //  4.   4
2275         "5.",           //  5.   5
2276         "6.",           //  6.   6
2277         "7.",           //  7.   7
2278         "8.",           //  8.  10
2279         "9.",           //  9.  11
2280         "MTP Tape",     // 10.  12
2281         "IPC Disk",     // 11.  13
2282         "MPC Disk",     // 12.  14
2283         "Card reader",  // 13.  15
2284         "Card punch",   // 14.  16
2285         "Printer",      // 15.  17
2286         "FNP",          // 16.  20
2287         "FNP",          // 17.  21
2288         "FNP",          // 18.  22
2289         "FNP",          // 19.  23
2290         "FNP",          // 20.  24
2291         "FNP",          // 21.  25
2292         "FNP",          // 22.  26
2293         "FNP",          // 23.  27
2294         "24.",          // 24.  30
2295         "25.",          // 25.  31
2296         "ABSI",         // 26.  32
2297         "27.",          // 27.  33
2298         "28.",          // 28.  34
2299         "29.",          // 29.  35
2300         "Console",      // 30.  36
2301         "31.",          // 31.  37
2302         "Socket",       // 32.  40
2303         "Socket",       // 33.  41
2304         "Socket",       // 34.  42
2305         "Socket",       // 35.  43
2306         "Socket",       // 36.  44
2307         "Socket",       // 37.  45
2308         "Socket",       // 38.  46
2309         "39.",
2310         "40.",
2311         "41.",
2312         "42.",
2313         "43.",
2314         "44.",
2315         "45.",
2316         "46.",
2317         "47.",
2318         "48.",
2319         "49.",
2320         "50.",
2321         "51.",
2322         "52.",
2323         "53.",
2324         "54.",
2325         "55.",
2326         "56.",
2327         "57.",
2328         "58.",
2329         "59.",
2330         "60.",
2331         "61.",
2332         "62.",
2333         "63."
2334   };
2335   sim_printf ("// %s channel LPW %012llo (case %c)\r\n", chanName[chan], p->LPW, updateCase);
2336   sim_printf ("//   DCW pointer %06o\r\n",      p->LPW_DCW_PTR);
2337   sim_printf ("//     RES              %o\r\n", p->LPW_18_RES);
2338   sim_printf ("//     REL              %o\r\n", p->LPW_19_REL);
2339   sim_printf ("//     AE               %o\r\n", p->LPW_20_AE);
2340   sim_printf ("//     NC 21            %o\r\n", p->LPW_21_NC);
2341   sim_printf ("//     TAL 22           %o\r\n", p->LPW_22_TAL);
2342   sim_printf ("//     REL              %o\r\n", p->LPW_23_REL);
2343   sim_printf ("//     TALLY         %04o\r\n",  p->LPW_TALLY);
2344   sim_printf ("// \r\n");
2345 }
2346 #endif
2347 
2348 static void fetch_and_parse_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2349   {
2350     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2351     uint chanLoc        = mbxLoc (iom_unit_idx, chan);
2352 
2353     iom_core_read (iom_unit_idx, chanLoc + IOM_MBX_LPW, (word36 *) & p -> LPW, __func__);
2354     sim_debug (DBG_DEBUG, & iom_dev,
2355                "%s: lpw %08o %012"PRIo64"\n", __func__, chanLoc + IOM_MBX_LPW, p->LPW);
2356 
2357     p -> LPW_DCW_PTR = getbits36_18 (p -> LPW,  0);
2358     p -> LPW_18_RES  = getbits36_1  (p -> LPW, 18);
2359     p -> LPW_19_REL  = getbits36_1  (p -> LPW, 19);
2360     p -> LPW_20_AE   = getbits36_1  (p -> LPW, 20);
2361     p -> LPW_21_NC   = getbits36_1  (p -> LPW, 21);
2362     p -> LPW_22_TAL  = getbits36_1  (p -> LPW, 22);
2363     p -> LPW_23_REL  = getbits36_1  (p -> LPW, 23);
2364     p -> LPW_TALLY   = getbits36_12 (p -> LPW, 24);
2365 
2366     if (chan == IOM_CONNECT_CHAN)
2367       {
2368         p -> LPWX = 0;
2369         p -> LPWX_BOUND = 0;
2370         p -> LPWX_SIZE = 0;
2371       }
2372     else
2373       {
2374         iom_core_read (iom_unit_idx, chanLoc + IOM_MBX_LPWX, (word36 *) & p -> LPWX, __func__);
2375         p -> LPWX_BOUND = getbits36_18 (p -> LPWX, 0);
2376         p -> LPWX_SIZE  = getbits36_18 (p -> LPWX, 18);
2377       }
2378     update_chan_mode (iom_unit_idx, chan, false);
2379   }
2380 
2381 static void unpack_DCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2382   {
2383     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2384     p -> DCW_18_20_CP =      getbits36_3 (p -> DCW, 18);
2385 
2386     if (IS_IDCW (p)) // IDCW
2387       {
2388         p -> IDCW_DEV_CMD   = getbits36_6 (p -> DCW, 0);
2389         p -> IDCW_DEV_CODE  = getbits36_6 (p -> DCW, 6);
2390         p -> IDCW_AE        = getbits36_6 (p -> DCW, 12);
2391         if (p -> LPW_23_REL)
2392           p -> IDCW_EC      = 0;
2393         else
2394           p -> IDCW_EC      = getbits36_1 (p -> DCW, 21);
2395         if (p -> IDCW_EC)
2396           p -> SEG          = 1; // pat. step 45
2397         p -> IDCW_CHAN_CTRL = getbits36_2 (p -> DCW, 22);
2398         p -> IDCW_CHAN_CMD  = getbits36_6 (p -> DCW, 24);
2399         p -> IDCW_COUNT     = getbits36_6 (p -> DCW, 30);
2400         p->recordResidue = p->IDCW_COUNT;
2401 #ifdef TESTING
2402         sim_debug (DBG_DEBUG, & iom_dev,
2403                    "%s: IDCW %012llo cmd %02o (%s) dev %02o ctrl %o chancmd %o\n",
2404                    __func__, p->DCW, p->IDCW_DEV_CMD, cmdNames[p->IDCW_DEV_CMD],
2405                    p->IDCW_DEV_CODE, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
2406 #endif
2407       }
2408     else // TDCW or DDCW
2409       {
2410         p -> TDCW_DATA_ADDRESS = getbits36_18 (p -> DCW,  0);
2411         p -> TDCW_31_SEG       = getbits36_1  (p -> DCW, 31);
2412         p -> TDCW_32_PDTA      = getbits36_1  (p -> DCW, 32);
2413         p -> TDCW_33_PDCW      = getbits36_1  (p -> DCW, 33);
2414         p -> TDCW_33_EC        = getbits36_1  (p -> DCW, 33);
2415         p -> TDCW_34_RES       = getbits36_1  (p -> DCW, 34);
2416         p -> TDCW_35_REL       = getbits36_1  (p -> DCW, 35);
2417         p -> DDCW_TALLY        = getbits36_12 (p -> DCW, 24);
2418         p -> DDCW_ADDR         = getbits36_18 (p -> DCW,  0);
2419         p -> DDCW_22_23_TYPE   = getbits36_2  (p -> DCW, 22);
2420         sim_debug (DBG_DEBUG, & iom_dev,
2421                    "%s: TDCW/DDCW %012llo tally %04o addr %06o type %o\n",
2422                    __func__, p->DCW, p->DDCW_TALLY, p->DDCW_ADDR, p->DDCW_22_23_TYPE);
2423 // POLTS_TESTING
2424 
2425 
2426 
2427 
2428 
2429 
2430 
2431 
2432 
2433 
2434 
2435 
2436 
2437 
2438 
2439 
2440 
2441 
2442 
2443 
2444       }
2445   }
2446 
2447 static void pack_DCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2448   {
2449     // DCW_18_20_CP is the only field ever changed.
2450     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2451     putbits36_3 ((word36 *) & p -> DCW, 18, p -> DCW_18_20_CP);
2452   }
2453 
2454 static void pack_LPW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2455   {
2456     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2457     putbits36_18 ((word36 *) & p-> LPW,   0, p -> LPW_DCW_PTR);
2458     putbits36_1  ((word36 *) & p-> LPW,  18, p -> LPW_18_RES);
2459     putbits36_1  ((word36 *) & p-> LPW,  19, p -> LPW_19_REL);
2460     putbits36_1  ((word36 *) & p-> LPW,  20, p -> LPW_20_AE);
2461     putbits36_1  ((word36 *) & p-> LPW,  21, p -> LPW_21_NC);
2462     putbits36_1  ((word36 *) & p-> LPW,  22, p -> LPW_22_TAL);
2463     putbits36_1  ((word36 *) & p-> LPW,  23, p -> LPW_23_REL);
2464     putbits36_12 ((word36 *) & p-> LPW,  24, p -> LPW_TALLY);
2465     putbits36_18 ((word36 *) & p-> LPWX,  0, p -> LPWX_BOUND);
2466     putbits36_18 ((word36 *) & p-> LPWX, 18, p -> LPWX_SIZE);
2467   }
2468 
2469 static void fetch_and_parse_PCW (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
2470   {
2471     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2472 
2473     iom_core_read2 (iom_unit_idx, p -> LPW_DCW_PTR, (word36 *) & p -> PCW0, (word36 *) & p -> PCW1, __func__);
2474     p -> PCW_CHAN           = getbits36_6  (p -> PCW1,  3);
2475     p -> PCW_AE             = getbits36_6  (p -> PCW0, 12);
2476     p -> PCW_21_MSK         = getbits36_1  (p -> PCW0, 21);
2477     p -> PCW_PAGE_TABLE_PTR = getbits36_18 (p -> PCW1,  9);
2478     p -> PCW_63_PTP         = getbits36_1  (p -> PCW1, 27);
2479     p -> PCW_64_PGE         = getbits36_1  (p -> PCW1, 28);
2480     p -> PCW_65_AUX         = getbits36_1  (p -> PCW1, 29);
2481     if (p -> PCW_65_AUX)
2482       sim_warn ("PCW_65_AUX\n");
2483     p -> DCW = p -> PCW0;
2484     unpack_DCW (iom_unit_idx, chan);
2485   }
2486 
2487 static void fetch_and_parse_DCW (uint iom_unit_idx, uint chan, UNUSED bool read_only)
     /* [previous][next][first][last][top][bottom][index][help] */
2488   {
2489     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2490     word24 addr         = p -> LPW_DCW_PTR & MASK18;
2491 
2492     sim_debug (DBG_DEBUG, & iom_dev, "%s: addr %08o\n", __func__, addr);
2493     switch (p -> chanMode)
2494       {
2495         // LPW ABS
2496         case cm1:
2497         case cm1e:
2498           {
2499             iom_core_read (iom_unit_idx, addr, (word36 *) & p -> DCW, __func__);
2500           }
2501           break;
2502 
2503         // LPW EXT
2504         case cm2e:
2505           {
2506             // LPXW_BOUND is mod 2; ie. val is * 2
2507             //addr |= ((word24) p -> LPWX_BOUND << 18);
2508             addr += ((word24) p -> LPWX_BOUND << 1);
2509             iom_core_read (iom_unit_idx, addr, (word36 *) & p -> DCW, __func__);
2510           }
2511           break;
2512 
2513         case cm2:
2514         case cm3b:
2515           {
2516             sim_warn ("fetch_and_parse_DCW LPW paged\n");
2517           }
2518           break;
2519 
2520         case cm3a:
2521         case cm4:
2522         case cm5:
2523           {
2524             fetch_LPWPTW (iom_unit_idx, chan);
2525             // Calculate effective address
2526             // PTW 4-17 || LPW 8-17
2527             word24 addr_ = ((word24) (getbits36_14 (p -> PTW_LPW, 4) << 10)) | ((p -> LPW_DCW_PTR) & MASK10);
2528             iom_core_read (iom_unit_idx, addr_, (word36 *) & p -> DCW, __func__);
2529           }
2530           break;
2531       }
2532     unpack_DCW (iom_unit_idx, chan);
2533 
2534     if (IS_IDCW (p))
2535       sim_debug (DBG_DEBUG, & iom_dev,
2536                  "%s: chan %d idcw: dev_cmd %#o dev_code %#o ae %#o ec %d control %#o chan_cmd %#o data %#o\n",
2537                  __func__, p -> PCW_CHAN, p -> IDCW_DEV_CMD, p -> IDCW_DEV_CODE, p -> IDCW_AE,
2538                  p -> IDCW_EC, p -> IDCW_CHAN_CTRL, p -> IDCW_CHAN_CMD, p -> IDCW_COUNT);
2539     else
2540       if (p -> DDCW_22_23_TYPE == 02)
2541         sim_debug (DBG_DEBUG, & iom_dev,
2542                    "%s: chan %d tdcw: address %#o seg %d pdta %d pdcw/ec %d res %d rel %d\n",
2543                    __func__, p -> PCW_CHAN, p -> TDCW_DATA_ADDRESS, p -> TDCW_31_SEG,
2544                    p -> TDCW_32_PDTA, p -> TDCW_33_PDCW, p -> TDCW_34_RES, p -> TDCW_35_REL);
2545       else
2546         sim_debug (DBG_DEBUG, & iom_dev,
2547                    "%s: chan %d ddcw: address %#o char_pos %#o type %#o tally %#o\n",
2548                    __func__, p -> PCW_CHAN, p -> DDCW_ADDR, p -> DCW_18_20_CP,
2549                    p -> DDCW_22_23_TYPE, p -> DDCW_TALLY);
2550   }
2551 
2552 /*
2553  * send_general_interrupt ()
2554  *
2555  * Send an interrupt from the IOM to the CPU.
2556  *
2557  */
2558 
2559 int send_general_interrupt (uint iom_unit_idx, uint chan, enum iomImwPics pic)
     /* [previous][next][first][last][top][bottom][index][help] */
2560   {
2561     uint imw_addr;
2562     uint chan_group    = chan < 32 ? 1 : 0;
2563     uint chan_in_group = chan & 037;
2564 
2565     uint iomUnitNum =
2566       iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & 3u;
2567     uint interrupt_num = iomUnitNum | (chan_group << 2) | ((uint) pic << 3);
2568     // Section 3.2.7 defines the upper bits of the IMW address as
2569     // being defined by the mailbox base address switches and the
2570     // multiplex base address switches.
2571     // However, AN-70 reports that the IMW starts at 01200.  If AN-70 is
2572     // correct, the bits defined by the mailbox base address switches would
2573     // have to always be zero.  We'll go with AN-70.  This is equivalent to
2574     // using bit value 0010100 for the bits defined by the multiplex base
2575     // address switches and zeros for the bits defined by the mailbox base
2576     // address switches.
2577     //imw_addr += 01200;  // all remaining bits
2578     uint pi_base = iom_unit_data[iom_unit_idx].configSwMultiplexBaseAddress & ~3u;
2579     imw_addr = (pi_base << 3) | interrupt_num;
2580 
2581     sim_debug (DBG_DEBUG, & iom_dev,
2582                "%s: IOM %c, channel %d (%#o), level %d; "
2583                "Interrupt %d (%#o).\n",
2584                __func__, iomChar (iom_unit_idx), chan, chan, pic, interrupt_num,
2585                interrupt_num);
2586     word36 imw;
2587     iom_core_read_lock (iom_unit_idx, imw_addr, &imw, __func__);
2588     // The 5 least significant bits of the channel determine a bit to be
2589     // turned on.
2590     //sim_debug (DBG_DEBUG, & iom_dev,
2591                //"%s: IMW at %#o was %012"PRIo64"; setting bit %d\n",
2592                //__func__, imw_addr, imw, chan_in_group);
2593     putbits36_1 (& imw, chan_in_group, 1);
2594     iom_core_write_unlock (iom_unit_idx, imw_addr, imw, __func__);
2595     return scu_set_interrupt (iom_unit_data[iom_unit_idx].invokingScuUnitIdx, interrupt_num);
2596   }
2597 
2598 static void iom_fault (uint iom_unit_idx, uint chan, UNUSED const char * who,
     /* [previous][next][first][last][top][bottom][index][help] */
2599                       iomFaultServiceRequest req,
2600                       iomSysFaults_t signal)
2601   {
2602     sim_warn ("iom_fault %s\n", who);
2603 
2604     // iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2605     // TODO:
2606     // For a system fault:
2607     // Store the indicated fault into a system fault word (3.2.6) in
2608     // the system fault channel -- use a list service to get a DCW to do so
2609     // For a user fault, use the normal channel status mechanisms
2610 
2611     // sys fault masks channel
2612 
2613     // signal gets put in bits 30..35, but we need fault code for 26..29
2614 
2615     // BUG: mostly unimplemented
2616 
2617     // User Faults: Pg 78: "A user fault is an abnormal conditions that can
2618     // be caused by a user program operating in the slave mode in the
2619     // processor."
2620     //  and
2621     // "User faults can be detected by the IOM Central of by a channel. If
2622     // a user fault is detected by the IOM Central, the fault is indicated to
2623     // the channel, and the channel is responsible for reporting the user
2624     // fault as status is its regular status queue.
2625 
2626     // This code only handles system faults
2627     //
2628 
2629     sim_debug (DBG_DEBUG, & iom_dev,
2630                "%s: chan %d %s req %#o signal %#o\n",
2631                __func__, chan, who, req, signal);
2632 
2633     word36 faultWord = 0;
2634     putbits36_9 (& faultWord, 9, (word9) chan);
2635     putbits36_5 (& faultWord, 18, (word5) req);
2636     // IAC, bits 26..29
2637     putbits36_6 (& faultWord, 30, (word6) signal);
2638 
2639     uint chanloc = mbxLoc (iom_unit_idx, IOM_SYSTEM_FAULT_CHAN);
2640 
2641     word36 lpw;
2642     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
2643 
2644     word36 scw;
2645     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
2646 
2647     word36 dcw;
2648     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
2649 
2650     //sim_debug (DBG_DEBUG, & iom_dev,
2651                //"%s: lpw %012"PRIo64" scw %012"PRIo64" dcw %012"PRIo64"\n",
2652                //__func__, lpw, scw, dcw);
2653 
2654     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, faultWord, __func__);
2655 
2656     uint tally = dcw & MASK12;
2657     if (tally > 1)
2658       {
2659         dcw -= 01llu;        //-V536  // tally --
2660         dcw += 01000000llu;  //-V536  // addr ++
2661       }
2662     else
2663       dcw = scw; // reset to beginning of queue
2664     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
2665 
2666     send_general_interrupt (iom_unit_idx, IOM_SYSTEM_FAULT_CHAN, imwSystemFaultPic);
2667   }
2668 
2669 //  0 ok
2670 // -1 fault
2671 // There is a path through the code where no DCW is sent (IDCW && LPW_18_RES)
2672 // Does the -1 return cover that?
2673 
2674 int iom_list_service (uint iom_unit_idx, uint chan,
     /* [previous][next][first][last][top][bottom][index][help] */
2675                            bool * ptro, bool * sendp, bool * uffp)
2676   {
2677     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
2678 
2679 // initialize
2680 
2681     bool isConnChan = chan == IOM_CONNECT_CHAN;
2682     * ptro          = false; // assume not PTRO
2683     bool uff        = false; // user fault flag
2684     bool send       = false;
2685 
2686 // Figure 4.3.1
2687 
2688 // START
2689 
2690     // FIRST SERVICE?
2691 
2692     if (p -> lsFirst)
2693       {
2694         // PULL LPW AND LPW EXT. FROM CORE MAILBOX
2695 
2696         fetch_and_parse_LPW (iom_unit_idx, chan);
2697         p -> wasTDCW = false;
2698         p -> SEG     = 0; // pat. FIG. 2, step 44
2699       }
2700     // else lpw and lpwx are in chanData;
2701 
2702     // CONNECT CHANNEL?
2703 
2704     if (isConnChan)
2705       { // connect channel
2706 
2707         // LPW 21 (NC), 22 (TAL) is {00, 01, 1x}?
2708 
2709         if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 0)
2710               {
2711                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2712                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2713                           iomIllTalCChnFlt);
2714                 return -1;
2715               }
2716 
2717         if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 1)
2718           { // 01
2719 
2720             // TALLY is {0, 1, >1}?
2721 
2722             if (p -> LPW_TALLY == 0)
2723               {
2724                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2725                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2726                           iomIllTalCChnFlt);
2727                 return -1;
2728               }
2729 
2730             if (p -> LPW_TALLY > 1)
2731               { // 00
2732 
2733                 // 256K OVERFLOW?
2734                 if (p -> LPW_20_AE == 0 &&
2735                     (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2736                     01000000llu)
2737                   {
2738                     iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2739                               p -> lsFirst ? iomFsrFirstList : iomFsrList,
2740                               iom256KFlt);
2741                     return -1;
2742                   }
2743               }
2744             else if (p -> LPW_TALLY == 1)
2745               * ptro = true;
2746           }
2747         else // 1x
2748           * ptro = true;
2749 
2750         // PULL PCW FROM CORE
2751 
2752 // B
2753 
2754         fetch_and_parse_PCW (iom_unit_idx, chan); // fills in DCW*
2755 
2756         // PCW 18-20 == 111?
2757         if (IS_NOT_IDCW (p))
2758           {
2759             iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2760                       p -> lsFirst ? iomFsrFirstList : iomFsrList,
2761                       iomNotPCWFlt);
2762             return -1;
2763           }
2764 
2765         // SELECT CHANNEL
2766 
2767         // chan = p -> PCW_CHAN;
2768 
2769 // detect unused slot as fault
2770 // if (no handler in slot)
2771 //  goto FAULT;
2772 
2773        // SEND PCW TO CHANNEL
2774 
2775         // p -> DCW = p -> DCW;
2776         send = true;
2777 
2778         goto D;
2779       }
2780 
2781     // Not connect channel
2782 
2783     // LPW 21 (NC), 22 (TAL) is {00, 01, 1x}?
2784 
2785     if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 0)
2786       {
2787         // XXX see pat. 46-51 re: SEG
2788         // 256K OVERFLOW?
2789         if (p -> LPW_20_AE == 0 &&
2790             (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2791             01000000llu)
2792           {
2793             iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2794                       p -> lsFirst ? iomFsrFirstList : iomFsrList,
2795                       iom256KFlt);
2796             return -1;
2797           }
2798        }
2799     else if (p -> LPW_21_NC == 0 && p -> LPW_22_TAL == 1)
2800       { // 01
2801 A:;
2802         // TALLY is {0, 1, >1}?
2803 
2804         if (p -> LPW_TALLY == 0)
2805           {
2806             uff = true;
2807           }
2808         else if (p -> LPW_TALLY > 1)
2809           { // 00
2810 
2811             // XXX see pat. 46-51 re: SEG
2812             // 256K OVERFLOW?
2813             if (p -> LPW_20_AE == 0 &&
2814                 (((word36) p -> LPW_DCW_PTR) + ((word36) p -> LPW_TALLY)) >
2815                 01000000llu)
2816               {
2817                 iom_fault (iom_unit_idx, IOM_CONNECT_CHAN, __func__,
2818                           p -> lsFirst ? iomFsrFirstList : iomFsrList,
2819                           iom256KFlt);
2820                 return -1;
2821               }
2822           }
2823       }
2824 
2825     // LPW 20? -- LPW_20 checked by fetch_and_parse_DCW
2826 
2827     // PULL DCW FROM CORE
2828     fetch_and_parse_DCW (iom_unit_idx, chan, false);
2829 
2830 // C
2831 
2832     // IDCW ?
2833 
2834     if (IS_IDCW (p))
2835       {
2836 
2837         // LPW_18_RES?
2838 
2839         if (p -> LPW_18_RES)
2840           {
2841             // SET USER FAULT FLAG
2842             uff = true; // XXX Why? uff isn't not examined later.
2843             // send = false; implicit...
2844           }
2845         else
2846           {
2847             // SEND IDCW TO CHANNEL
2848             // p -> DCW = p -> DCW;
2849             send = true;
2850           }
2851         p -> LPWX_SIZE = p -> LPW_DCW_PTR;
2852         goto D;
2853       }
2854 
2855 // Not IDCW
2856 
2857     if (p -> lsFirst)
2858       p -> LPWX_SIZE = p -> LPW_DCW_PTR;
2859 
2860 // pg B16: "If the IOM is paged [yes] and PCW bit 64 is off, LPW bit 23
2861 //          is ignored by the hardware. If bit 64 is set, LPW bit 23 causes
2862 //          the data to become segmented."
2863 // Handled in fetch_and_parse_LPW
2864 
2865     // LPW 23 REL?
2866 
2867     // TDCW ?
2868 
2869     if (IS_TDCW (p))
2870       {
2871         // SECOND TDCW?
2872         if (p -> wasTDCW)
2873           {
2874             uff = true;
2875             goto uffSet;
2876           }
2877         p -> wasTDCW = true;
2878 
2879         // PUT ADDRESS N LPW
2880 
2881         p -> LPW_DCW_PTR = p -> TDCW_DATA_ADDRESS;
2882         // OR TDCW 33, 34, 35 INTO LPW 20, 18, 23
2883         // XXX is 33 bogus? its semantics change based on PCW 64...
2884         // should be okay; pg B21 says that this is correct; implies that the
2885         // semantics are handled in the LPW code. Check...
2886         p -> LPW_20_AE  |= p -> TDCW_33_EC; // TDCW_33_PDCW
2887         p -> LPW_18_RES |= p -> TDCW_34_RES;
2888         p -> LPW_23_REL |= p -> TDCW_35_REL;
2889 
2890 // Pg B21: (TDCW_31_SEG)
2891 // "SEG = This bit furnishes the 19th address bit (MSD) of a TDCW address
2892 //  used for locating the DCW list in a 512 word page table. It will have
2893 //  meaning only in the TDCW where:
2894 //   (a) the DCW list is already paged and the TDCW calls for the
2895 //       DCW [to be] segmented
2896 //  or
2897 //   (b) the data is already segmented and the TDCW calls for the
2898 //       DCW list to be paged
2899 //  or
2900 //   (c) neither data is segmented nor DCW list is paged and the
2901 //       TDCW calls for both.
2902 //  and
2903 //   (d) an auxiliary PTW in not being used.
2904 
2905 //   (a) the DCW list is already paged   --  LPW PAGED: 3b, 4
2906 //       and the TDCW calls for the
2907 //       DCW [list to be] segmented      --  LPW SEG:       5
2908 
2909 //   (b) the data is already segmented   --  DCW SEG:   3a, 4
2910 //       and the TDCW calls for the
2911 //       DCW list to be paged            --  DCW PAGED:  2, 3b
2912 
2913 //   (c) neither data is segmented       -- DCW !SEG     1, 2, 3b
2914 //       nor DCW list is paged           -- LPW !PAGED   1, 2, 3a
2915 //                                       --              1, 2
2916 //       and the TDCW calls for both.    -- DCW SEG & LPW PAGED
2917 //                                                       4
2918 
2919 //put that weird SEG logic in here
2920 
2921         if (p -> TDCW_31_SEG)
2922           sim_warn ("TDCW_31_SEG\n");
2923 
2924         update_chan_mode (iom_unit_idx, chan, true);
2925 
2926         // Decrement tally
2927         p -> LPW_TALLY = (p -> LPW_TALLY - 1u) & MASK12;
2928 
2929         pack_LPW (iom_unit_idx, chan);
2930 
2931         // AC CHANGE ERROR? (LPW 18 == 1 && DCW 33 == 1)
2932 
2933         if (p -> LPW_18_RES && p -> TDCW_33_EC) // same as TDCW_33_PDCW
2934           {
2935             uff = true;
2936             goto uffSet;
2937           }
2938 
2939         goto A;
2940       }
2941 
2942     p -> wasTDCW = false;
2943     // NOT TDCW
2944 
2945     // CP VIOLATION?
2946 
2947     // 43A239854 3.2.3.3 "The byte size, defined by the channel, determines
2948     // what CP values are valid..."
2949 
2950     // If we get here, the DCW is not a IDCW and not a TDCW, therefore
2951     // it must be a DDCW. If the list service knew the sub-word size
2952     // size of the device, it could check the for valid values. Let
2953     // the device handler do that later.
2954 
2955     // if (cp decrepancy)
2956     //   {
2957     //      user_fault_flag = iomCsCpDiscrepancy;
2958     //      goto user_fault;
2959     //   }
2960     // if (cp violation)
2961     //   {
2962     //     uff = true;
2963     //     goto uffSet;
2964     //   }
2965 
2966     // USER FAULT FLAG SET?
2967 
2968     if (uff)
2969       {
2970 uffSet:;
2971         // PUT 7 into DCW 18-20
2972         p -> DCW_18_20_CP = 07u;
2973         pack_DCW (iom_unit_idx, chan);
2974       }
2975 
2976     // WRITE DCW IN [SCRATCH PAD] MAILBOX
2977 
2978     // p -> DVW  = P -> DCW;
2979 
2980     // SEND DCW TO CHANNEL
2981 
2982     // p -> DCW = p -> DCW;
2983     send = true;
2984 
2985     // goto D;
2986 
2987 D:;
2988 
2989     // SEND FLAGS TO CHANNEL
2990 
2991     * uffp  = uff;
2992     * sendp = send;
2993 
2994     // LPW 21 ?
2995 
2996     if (p -> LPW_21_NC == 0) // UPDATE
2997      {
2998        // UPDATE LPW ADDRESS & TALLY
2999        if (isConnChan)
3000          p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 2u) & MASK18;
3001        else
3002          p -> LPW_DCW_PTR = (p -> LPW_DCW_PTR + 1u) & MASK18;
3003        //p -> LPW_TALLY = (p -> LPW_TALLY - 1u) & MASK12;
3004        // ubsan
3005        p->LPW_TALLY = ((word12) (((word12s) p->LPW_TALLY) - 1)) & MASK12;
3006        pack_LPW (iom_unit_idx, chan);
3007      }
3008 
3009     // IDCW OR FIRST LIST
3010     if (p -> DDCW_22_23_TYPE == 07u || p -> lsFirst)
3011       {
3012         // WRITE LPW & LPW EXT. INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3013         // scratch pad
3014         // p -> lpw = p -> lpw
3015         // core
3016         write_LPW (iom_unit_idx, chan);
3017       }
3018     else
3019       {
3020         if (p -> LPW_21_NC == 0 ||
3021             (IS_TDCW (p)))
3022           {
3023             // WRITE LPW INTO BOTH SCRATCHPAD AND CORE MAILBOXES
3024             // scratch pad
3025             // p -> lpw = p -> lpw
3026             // core
3027             write_LPW (iom_unit_idx, chan);
3028           }
3029       }
3030 
3031     p -> lsFirst = false;
3032     // END
3033 
3034     return 0;
3035   }
3036 
3037 // 0 ok
3038 // -1 uff
3039 static int doPayloadChannel (uint iomUnitIdx, uint chan) {
     /* [previous][next][first][last][top][bottom][index][help] */
3040   sim_debug (DBG_DEBUG, & iom_dev, "%s: Payload channel %c%02o\n", __func__, iomChar (iomUnitIdx), chan);
3041 // A dubious assumption being made is that the device code will always
3042 // be in bits 6-12 of the DCw. Normally, the controller would
3043 // decipher the device code and route to the device, but we
3044 // have elided the controllers and must do that ourselves.
3045 
3046 // Loop logic
3047 //
3048 //   The DCW list can be terminated two ways; either by having the
3049 //   tally run out, or a IDCW with control set to zero.
3050 //
3051 //   The device handler will absorb DDCWs
3052 //
3053 // loop until:
3054 //
3055 //  listService sets ptro, indicating that no more DCWs are available. or
3056 //     control is 0, indicating last IDCW
3057 
3058   iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
3059 
3060 #ifdef TESTING
3061   word36 PCW_DCW        = p->DCW;
3062   word1  PCW_LPW_23_REL = p->LPW_23_REL;
3063 #endif
3064   p -> chanMode   = cm1;
3065   p -> LPW_18_RES = 0;
3066   p -> LPW_20_AE  = 0;
3067   p -> LPW_23_REL = 0;
3068 
3069   unpack_DCW (iomUnitIdx, chan);
3070 
3071   p->isPCW = true;
3072 
3073   // Masked is set in doConnectChannel
3074   //p->masked = !!p->PCW_21_MSK;
3075   struct iom_to_ctlr_s * d = & cables->iom_to_ctlr[iomUnitIdx][chan];
3076 
3077 // A device command of 051 in the PCW is only meaningful to the operator console;
3078 // all other channels should ignore it. We use (somewhat bogusly) a chanType of
3079 // chanTypeCPI to indicate the operator console.
3080   if (d->chan_type != chan_type_CPI && p -> IDCW_DEV_CMD == 051) {
3081     p->stati = 04501;
3082     send_terminate_interrupt (iomUnitIdx, chan);
3083     return 0;
3084   }
3085 
3086   if ((!d->in_use) || (!d->iom_cmd)) {
3087     p -> stati = 06000; // t, power off/missing
3088 #ifdef POLTS_TESTING
3089 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 10. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3090 #endif
3091     goto terminate;
3092   }
3093 
3094 // 3.2.2. "Bits 12-17 [of the PCW] contain the address extension which is maintained by
3095 //         the channel for subsequent use by the IOM in generating a 24-bit
3096 //         address for list of data services for the extended address modes."
3097 // see also 3.2.3.1
3098   p -> ADDR_EXT     = p -> PCW_AE;
3099 
3100   p -> lsFirst      = true;
3101 
3102   p -> tallyResidue = 0;
3103   p -> isRead       = true;
3104   p -> charPos      = 0;
3105 // As far as I can tell, initiate false means that an IOTx succeeded in
3106 // transferring data; assume it didn't since that is the most common
3107 // code path.
3108   p -> initiate     = true;
3109   p -> chanStatus   = chanStatNormal;
3110 
3111 //
3112 // Send the PCW's DCW
3113 //
3114   int rc = d->iom_cmd (iomUnitIdx, chan);
3115 
3116   if (rc < 0) {
3117     p -> dev_code = getbits36_6 (p -> DCW, 6);
3118 #ifdef POLTS_TESTING
3119 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 9. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3120 #endif
3121     goto terminate;
3122   }
3123 
3124   if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_MARKER) { // IDCW marker bit set
3125     send_marker_interrupt (iomUnitIdx, (int) chan);
3126   }
3127 
3128   if (rc == IOM_CMD_DISCONNECT) {
3129 #ifdef POLTS_TESTING
3130 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 8. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3131 #endif
3132     goto terminate;
3133   }
3134 
3135   if (p->masked) {
3136 #ifdef POLTS_TESTING
3137 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 7. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3138 #endif
3139     goto terminate;
3140   }
3141 
3142   bool ptro, send, uff;
3143   bool terminate = false;
3144   p->isPCW       = false;
3145 
3146 #ifdef TESTING
3147   bool first = true;
3148 #endif
3149 
3150   (void)terminate;
3151   bool idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3152   do {
3153     int rc2 = iom_list_service (iomUnitIdx, chan, & ptro, & send, & uff);
3154     if (rc2 < 0) {
3155 // XXX set status flags
3156       sim_warn ("%s list service failed\n", __func__);
3157       return -1;
3158     }
3159     if (uff) {
3160       // We get a uff if the LPW tally hit 0
3161 #ifdef POLTS_TESTING
3162 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 6. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3163 #endif
3164       goto terminate;
3165     }
3166     // List service failed to get a DCW
3167     if (! send) {
3168       sim_warn ("%s nothing to send\n", __func__);
3169       return 1;
3170     }
3171 
3172 #ifdef TESTING
3173 # ifdef  POLTS_TESTING
3174 if (iomUnitIdx == 1 && chan == 020)
3175 # endif
3176     if_sim_debug (DBG_TRACE, & iom_dev) {
3177       if (first) {
3178         first = false;
3179         dumpLPW (iomUnitIdx, chan);
3180         sim_printf ("// DCW list dump: \r\n");
3181         dumpDCW (PCW_DCW, PCW_LPW_23_REL);
3182         for (int i = 0; i < 8; i ++)
3183           dumpDCW (M[p->LPW_DCW_PTR - 1 + i], p->LPW_23_REL);
3184         sim_printf ("// \r\n");
3185       }
3186     }
3187 #endif
3188 
3189 // 3.2.3.1 "If EC - Bit 21 = 1, The channel will replace the present address
3190 //          extension with the new address extension in bits 12-17. ... In
3191 //          Multics and NSA systems, EC is inhibited from the payload channel
3192 //          if LPW bit 23 = 1.
3193 
3194     if (IS_IDCW (p)) { // IDCW
3195       idcw_terminate = p -> IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE;
3196       if (p -> LPW_23_REL == 0 && p -> IDCW_EC == 1)
3197         p -> ADDR_EXT = getbits36_6 (p -> DCW, 12);
3198 
3199       p -> tallyResidue = 0;
3200       p -> isRead       = true;
3201       p -> charPos      = 0;
3202       p -> chanStatus   = chanStatNormal;
3203     }
3204 
3205 // Send the DCW list's DCW
3206 
3207     rc2 = d->iom_cmd (iomUnitIdx, chan);
3208 
3209     if (rc2 < 0) {
3210       p -> dev_code = getbits36_6 (p -> DCW, 6);
3211 #ifdef POLTS_TESTING
3212 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 5. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3213 #endif
3214       goto terminate;
3215     }
3216 
3217     if (rc2 == IOM_CMD_DISCONNECT) {
3218       terminate = true;
3219 #ifdef POLTS_TESTING
3220 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 4. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3221 #endif
3222     }
3223 
3224     if (rc2 == IOM_CMD_PENDING) // handler still processing command, don't set
3225       goto pending;             // terminate interrupt.
3226 
3227     // If IDCW and terminate and nondata
3228     if (IS_IDCW (p) && p->IDCW_CHAN_CTRL == CHAN_CTRL_TERMINATE && p->IDCW_CHAN_CMD == CHAN_CMD_NONDATA) {
3229 #ifdef POLTS_TESTING
3230 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 1. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3231 #endif
3232       goto terminate;
3233     }
3234     // If IOTD and last IDCW was terminate
3235     if (IS_IOTD (p) && idcw_terminate && rc2 != IOM_CMD_RESIDUE) {
3236 #ifdef POLTS_TESTING
3237 //if (iomUnitIdx == 1 && chan == 020)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// ctrl == 0 in chan %d (%o) IOTP\n", chan, chan);
3238 if (chan == 014)      if_sim_debug (DBG_TRACE, & iom_dev) sim_printf ("// terminate 2. ctrl == 0 in chan %d (%o) DCW\n", chan, chan);
3239 #endif
3240       goto terminate;
3241     }
3242 
3243     // IOM_CMD_RESIDUE: Continue pushing DCWS until the record residue is 0
3244 
3245     if (IS_NOT_IDCW (p) && rc2 == IOM_CMD_RESIDUE) {
3246       if (p->recordResidue)
3247         p->recordResidue --;
3248       if (p->recordResidue == 0)
3249         goto terminate;
3250     }
3251   } while (! terminate);
3252 
3253 terminate:;
3254 #ifdef POLTS_TESTING
3255 if (iomUnitIdx == 1 && chan == 020) sim_printf ("stati %04o\r\n", p->stati);
3256 #endif
3257   send_terminate_interrupt (iomUnitIdx, chan);
3258   return 0;
3259 
3260 pending:;
3261   return 0;
3262 }
3263 
3264 // doConnectChan ()
3265 //
3266 // Process the "connect channel".  This is what the IOM does when it
3267 // receives a $CON signal.
3268 //
3269 // Only called by iom_interrupt ()
3270 //
3271 // The connect channel requests one or more "list services" and processes the
3272 // resulting PCW control words.
3273 //
3274 
3275 static int doConnectChan (uint iom_unit_idx) {
     /* [previous][next][first][last][top][bottom][index][help] */
3276   // ... the connect channel obtains a list service from the IOM Central.
3277   // During this service the IOM Central will do a double precision read
3278   // from the core, under the control of the LPW for the connect channel.
3279   //
3280   // If the IOM Central does not indicate a PTRO during the list service,
3281   // the connect channel obtains another list service.
3282   //
3283   // The connect channel does not interrupt or store status.
3284   //
3285   // The DCW and SCW mailboxes for the connect channel are not used by
3286   // the IOM.
3287   sim_debug (DBG_DEBUG, & iom_dev, "%s: Connect channel\n", __func__);
3288   iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][IOM_CONNECT_CHAN];
3289   p -> lsFirst = true;
3290 #if defined(TESTING) && defined(POLTS_TESTING)
3291   bool first = true;
3292 #endif
3293   bool ptro, send, uff;
3294   do {
3295     // Fetch the next PCW
3296     int rc = iom_list_service (iom_unit_idx, IOM_CONNECT_CHAN, & ptro, & send, & uff);
3297     if (rc < 0) {
3298       sim_warn ("connect channel connect failed\n");
3299       return -1;
3300     }
3301     if (uff) {
3302       sim_warn ("connect channel ignoring uff\n"); // XXX
3303     }
3304     if (! send) {
3305       sim_warn ("connect channel nothing to send\n");
3306     } else {
3307 #ifdef xTESTING
3308       if_sim_debug (DBG_TRACE, & iom_dev) {
3309         if (first) {
3310           first = false;
3311           sim_printf ("// CIOC %lld\r\n", cpu.cycleCnt);
3312           dumpLPW (iom_unit_idx, IOM_CONNECT_CHAN);
3313           sim_printf ("// PCW %012llo %012llo\r\n",    p->PCW0, p->PCW1);
3314           sim_printf ("//   Chan info     %04o\r\n",   p->PCW_CHAN);
3315           sim_printf ("//   Addr ext        %02o\r\n", p->PCW_AE);
3316           sim_printf ("//   111              %o\r\n",  getbits36_3 (p->PCW0, 18));
3317           sim_printf ("//   M                %o\r\n",  p->PCW_21_MSK);
3318           sim_printf ("//   Chan info %08o\r\n",       getbits36_14 (p->PCW0, 22));
3319           sim_printf ("//   Pg Tbl Ptr  %06o\r\n",     p->PCW_PAGE_TABLE_PTR);
3320           sim_printf ("//   PTP              %o\r\n",  p->PCW_63_PTP);
3321           sim_printf ("//   PGE              %o\r\n",  p->PCW_64_PGE);
3322           sim_printf ("//   AUX              %o\r\n",  p->PCW_65_AUX);
3323           sim_printf ("//\r\n");
3324         }
3325       }
3326 #endif
3327       // Copy the PCW's DCW to the payload channel
3328       iom_chan_data_t * q = & iom_chan_data[iom_unit_idx][p -> PCW_CHAN];
3329 
3330       q -> PCW0               = p -> PCW0;
3331       q -> PCW1               = p -> PCW1;
3332       q -> PCW_CHAN           = p -> PCW_CHAN;
3333       q -> PCW_AE             = p -> PCW_AE;
3334       q -> PCW_PAGE_TABLE_PTR = p -> PCW_PAGE_TABLE_PTR;
3335       q -> PCW_63_PTP         = p -> PCW_63_PTP;
3336       q -> PCW_64_PGE         = p -> PCW_64_PGE;
3337       q -> PCW_65_AUX         = p -> PCW_65_AUX;
3338       q -> PCW_21_MSK         = p -> PCW_21_MSK;
3339       q -> DCW                = p -> DCW;
3340 
3341       sim_debug (DBG_DEBUG, & iom_dev, "%s: PCW %012llo %012llo chan %02o\n", __func__, q->PCW0, q->PCW1, q->PCW_CHAN);
3342 
3343       q -> masked = p -> PCW_21_MSK;
3344       if (q -> masked) {
3345         if (q -> in_use)
3346           sim_warn ("%s: chan %d masked while in use\n", __func__, p -> PCW_CHAN);
3347         q -> in_use = false;
3348         q -> start  = false;
3349       } else {
3350         if (q -> in_use)
3351           sim_warn ("%s: chan %d connect while in use\n", __func__, p -> PCW_CHAN);
3352         q -> in_use = true;
3353         q -> start  = true;
3354 #ifdef IO_THREADZ
3355         setChnConnect (iom_unit_idx, p -> PCW_CHAN);
3356 #else
3357 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3358         doPayloadChannel (iom_unit_idx, p -> PCW_CHAN);
3359 # endif
3360 # ifdef IO_ASYNC_PAYLOAD_CHAN_THREAD
3361         pthread_cond_signal (& iomCond);
3362 # endif
3363 #endif
3364       }
3365     }
3366   } while (! ptro);
3367   return 0; // XXX
3368 }
3369 
3370 /*
3371  * send_marker_interrupt ()
3372  *
3373  * Send a "marker" interrupt to the CPU.
3374  *
3375  * Channels send marker interrupts to indicate normal completion of
3376  * a PCW or IDCW if the control field of the PCW/IDCW has a value
3377  * of three.
3378  */
3379 
3380 int send_marker_interrupt (uint iom_unit_idx, int chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3381   {
3382     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3383       return(0);
3384     status_service (iom_unit_idx, (uint) chan, true);
3385     return send_general_interrupt (iom_unit_idx, (uint) chan, imwMarkerPic);
3386   }
3387 
3388 /*
3389  * send_special_interrupt ()
3390  *
3391  * Send a "special" interrupt to the CPU.
3392  *
3393  */
3394 
3395 int send_special_interrupt (uint iom_unit_idx, uint chan, uint devCode,
     /* [previous][next][first][last][top][bottom][index][help] */
3396                             word8 status0, word8 status1)
3397   {
3398 //if (iom_unit_idx == 0 && chan == 013) sim_printf ("spec int %03o %013o\n", status0, status1);
3399     uint chanloc = mbxLoc (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN);
3400 
3401     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3402       return(0);
3403 
3404 #ifdef LOCKLESS
3405     lock_iom();
3406 #endif
3407 
3408 // Multics uses an 12(8) word circular queue, managed by clever manipulation
3409 // of the LPW and DCW.
3410 // Rather then goes through the mechanics of parsing the LPW and DCW,
3411 // we will just assume that everything is set up the way we expect,
3412 // and update the circular queue.
3413     word36 lpw;
3414     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_LPW, & lpw, __func__);
3415 
3416     word36 scw;
3417     iom_core_read (iom_unit_idx, chanloc + IOM_MBX_SCW, & scw, __func__);
3418 
3419     word36 dcw;
3420     iom_core_read_lock (iom_unit_idx, chanloc + IOM_MBX_DCW, & dcw, __func__);
3421 
3422     word36 status  = 0400000000000;
3423     status        |= (((word36) chan)    & MASK6) << 27;
3424     status        |= (((word36) devCode) & MASK8) << 18;
3425     status        |= (((word36) status0) & MASK8) <<  9;
3426     status        |= (((word36) status1) & MASK8) <<  0;
3427     iom_core_write (iom_unit_idx, (dcw >> 18) & MASK18, status, __func__);
3428 
3429     uint tally = dcw & MASK12;
3430     if (tally > 1)
3431       {
3432         dcw -= 01llu;  // tally --
3433         dcw += 01000000llu; // addr ++
3434       }
3435     else
3436       dcw = scw; // reset to beginning of queue
3437     iom_core_write_unlock (iom_unit_idx, chanloc + IOM_MBX_DCW, dcw, __func__);
3438 
3439 #ifdef LOCKLESS
3440     unlock_iom();
3441 #endif
3442 
3443     send_general_interrupt (iom_unit_idx, IOM_SPECIAL_STATUS_CHAN, imwSpecialPic);
3444     return 0;
3445   }
3446 
3447 /*
3448  * send_terminate_interrupt ()
3449  *
3450  * Send a "terminate" interrupt to the CPU.
3451  *
3452  * Channels send a terminate interrupt after doing a status service.
3453  *
3454  */
3455 
3456 int send_terminate_interrupt (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
3457   {
3458     if (iom_chan_data [iom_unit_idx] [chan] . masked)
3459       return 0;
3460     status_service (iom_unit_idx, chan, false);
3461     if (iom_chan_data [iom_unit_idx] [chan] . in_use == false)
3462       sim_warn ("%s: chan %d not in use\n", __func__, chan);
3463     iom_chan_data [iom_unit_idx] [chan] . in_use = false;
3464     send_general_interrupt (iom_unit_idx, chan, imwTerminatePic);
3465     return 0;
3466   }
3467 
3468 void iom_interrupt (uint scu_unit_idx, uint iom_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
3469   {
3470     sim_debug (DBG_DEBUG, & iom_dev,
3471                "%s: IOM %c starting. [%"PRId64"] %05o:%08o\n",
3472                __func__, iomChar (iom_unit_idx), cpu.cycleCnt, cpu.PPR.PSR, cpu.PPR.IC);
3473 
3474     iom_unit_data[iom_unit_idx].invokingScuUnitIdx = scu_unit_idx;
3475 
3476 #ifdef IO_THREADZ
3477     setIOMInterrupt (iom_unit_idx);
3478     iomDoneWait (iom_unit_idx);
3479 #else
3480     int ret = doConnectChan (iom_unit_idx);
3481 
3482     sim_debug (DBG_DEBUG, & iom_dev,
3483                "%s: IOM %c finished; doConnectChan returned %d.\n",
3484                __func__, iomChar (iom_unit_idx), ret);
3485 #endif
3486     // XXX doConnectChan return value ignored
3487   }
3488 
3489 #ifdef IO_THREADZ
3490 void * chan_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3491   {
3492     uint myid     = (uint) * (int *) arg;
3493     this_iom_idx  = (uint) myid / MAX_CHANNELS;
3494     this_chan_num = (uint) myid % MAX_CHANNELS;
3495 
3496 // Set CPU context to allow sim_debug to work
3497 
3498     set_cpu_idx (0);
3499 
3500     sim_msg ("\rIOM %c Channel %u thread created\r\n", this_iom_idx + 'a', this_chan_num);
3501 # ifdef TESTING
3502     printPtid(pthread_self());
3503 # endif /* ifdef TESTING */
3504     sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
3505 
3506     setSignals ();
3507     while (1)
3508       {
3509 //sim_printf("IOM %c Channel %u thread waiting\n", this_iom_idx + 'a', this_chan_num);
3510         chnConnectWait ();
3511 //sim_printf("IOM %c Channel %u thread running\n", this_iom_idx + 'a', this_chan_num);
3512         doPayloadChannel (this_iom_idx, this_chan_num);
3513         chnConnectDone ();
3514       }
3515   }
3516 
3517 void * iom_thread_main (void * arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3518   {
3519     int myid = * (int *) arg;
3520     this_iom_idx = (uint) myid;
3521 
3522 // Set CPU context to allow sim_debug to work
3523 
3524     set_cpu_idx (0);
3525 
3526     sim_printf("IOM %c thread created\n", 'a' + myid);
3527 
3528     setSignals ();
3529     while (1)
3530       {
3531 //sim_printf("IOM %c thread waiting\n", 'a' + myid);
3532         iomInterruptWait ();
3533 //sim_printf("IOM %c thread running\n", 'a' + myid);
3534         int ret = doConnectChan (this_iom_idx);
3535 
3536         sim_debug (DBG_DEBUG, & iom_dev,
3537                    "%s: IOM %c finished; doConnectChan returned %d.\n",
3538                    __func__, iomChar (myid), ret);
3539         iomInterruptDone ();
3540       }
3541   }
3542 #endif
3543 
3544 /*
3545  * iom_init ()
3546  *
3547  *  Once-only initialization
3548  */
3549 
3550 void iom_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3551   {
3552     //sim_debug (DBG_INFO, & iom_dev, "%s: running.\n", __func__);
3553   }
3554 
3555 #ifdef PANEL68
3556 void do_boot (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3557   {
3558     boot_svc (& boot_channel_unit[0]);
3559   }
3560 #endif
3561 
3562 #if defined(IO_ASYNC_PAYLOAD_CHAN) || defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
3563 void iomProcess (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3564   {
3565     for (uint i = 0; i < N_IOM_UNITS_MAX; i++)
3566       for (uint j = 0; j < MAX_CHANNELS; j++)
3567         {
3568           iom_chan_data_t * p = &iom_chan_data [i] [j];
3569           if (p -> start)
3570             {
3571               p -> start = false;
3572               doPayloadChannel (i, j);
3573             }
3574         }
3575   }
3576 #endif
3577 
3578 char iomChar (uint iomUnitIdx)
     /* [previous][next][first][last][top][bottom][index][help] */
3579   {
3580     return (iom_unit_data[iomUnitIdx].configSwMultiplexBaseAddress & 3) + 'A';
3581   }

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