root/src/dps8/dps8_scu.c

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

DEFINITIONS

This source file includes following definitions.
  1. scu_show_nunits
  2. scu_set_nunits
  3. scu_show_state
  4. scu_show_config
  5. scu_set_config
  6. dump_intr_regs
  7. scu_unit_reset
  8. scu_reset
  9. set_SCU_clock
  10. pcells
  11. deliver_interrupts
  12. scu_smic
  13. scu_sscr
  14. scu_rscr
  15. scu_cioc
  16. scu_set_interrupt
  17. scu_get_highest_intr
  18. scu_reset_unit
  19. scu_init
  20. scu_rmcm
  21. scu_smcm

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: d49ab489-f62e-11ec-9ac1-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-2025 The DPS8M Development Team
  13  *
  14  * This software is made available under the terms of the ICU License.
  15  * See the LICENSE.md file at the top-level directory of this distribution.
  16  *
  17  * ---------------------------------------------------------------------------
  18  *
  19  * This source file may contain code comments that adapt, include, and/or
  20  * incorporate Multics program code and/or documentation distributed under
  21  * the Multics License.  In the event of any discrepancy between code
  22  * comments herein and the original Multics materials, the original Multics
  23  * materials should be considered authoritative unless otherwise noted.
  24  * For more details and historical background, see the LICENSE.md file at
  25  * the top-level directory of this distribution.
  26  *
  27  * ---------------------------------------------------------------------------
  28  */
  29 
  30 //-V536
  31 
  32 /*
  33  * scu.c -- System Controller
  34  *
  35  * See AN70, section 8 and GB61.
  36  *
  37  * There were a few variations of SCs and SCUs:
  38  * SCU -- Series 60 Level 66 Controller
  39  * SC -- Level 68 System Controller
  40  * 4MW SCU -- A later version of the Level 68 SC
  41  *
  42  * SCUs control access to memory.
  43  * Each SCU owns a certain range of absolute memory.
  44  * This emulator allows the CPU to access memory directly however.
  45  * SCUs contain clocks.
  46  * SCUS also contain facilities which allow CPUS and IOMs to communicate.
  47  * CPUs or IOMS request access to memory via the SCU.
  48  * CPUs use the CIOC instr to talk to IOMs and other CPUs via a SCU.
  49  * IOMs use interrupts to ask a SCU to signal a CPU.
  50  * Other Interesting instructions:
  51  * read system controller reg and set system controller reg (RSCR & SSCR)
  52  *
  53  */
  54 
  55 /*
  56  * Physical Details & Interconnection -- AN70, section 8.
  57  *
  58  * SCUs have 8 ports.
  59  * Active modules (CPUs and IOMs) have up to four of their ports
  60  * connected to SCU ports.
  61  *
  62  * The 4MW SCU has eight on/off switches to enable or disable
  63  * the ports.  However, the associated registers allow for
  64  * values of enabled, disabled, and program control.
  65  *
  66  * SCUs have stores (memory banks).
  67  *
  68  * SCUs have four sets of registers controlling interrupts.  Only two
  69  * of these sets, designated "A" and "B" are used.  Each set has:
  70  * Execute interrupt mask register -- 32 bits; enables/disables
  71  * the corresponding execute interrupt cell
  72  * Interrupt mask assignment register -- 9 bits total
  73  * two parts: assigned bit, set of assigned ports (8 bits)
  74  * In Multics, only one CPU will be assigned in either mask
  75  * and no CPU appears in both.   Earlier hardware versions had
  76  * four 10-position rotary switches.  Later hardware versions had
  77  * two 9-position (0..7 and off) rotary switches.
  78  *
  79  * Config panel -- Level 68 6000 SCU
  80  * -- from AM81
  81  * store A and store B
  82  * 3 position rotary switch: on line, maint, off line
  83  * size: 32K, 64K, 128K, 256K
  84  * exec interrupt mask assignment
  85  * four 10-position rotary switches (A through D): off, 0 .. 7, M
  86  * One switch for each program interrupt register
  87  * Assign mask registers to system ports
  88  * Normally assign one mask reg to each CPU
  89  *
  90  *   AM81:
  91  *     "        The EXECUTE INTERRUPT MASK ASSIGNMENT (EIMA) rotary switches
  92  *      determine where interrupts sent to memory are directed.  The four EIMA
  93  *      rotary switches, one for each program interrupt register, are used to
  94  *      assign mask registers to system ports. The normal settings assign one
  95  *      mask register to each CPU configured.
  96  *
  97  *      Each switch assigns mask registers as follows:
  98  *
  99  *          Position
 100  *            OFF     Unassigned
 101  *              0     Assigned to port 0
 102  *                ...
 103  *              7     Assigned to port 7
 104  *              M     Assigned to maintenance panel
 105  *
 106  *      Assignment of a mask register to a system port designates the
 107  *      port as a control port, and that port receives interrupt present
 108  *      signals. Up to four system ports can be designated as control
 109  *      ports. The normal settings assign one mask register to each CPU
 110  *      configured."
 111  *
 112  *
 113  *
 114  * Config panel -- Level 68 System Controller UNIT (4MW SCU)
 115  * -- from AM81
 116  * Store A, A1, B, B1 (online/offline)
 117  * LWR Store Size
 118  * PORT ENABLE
 119  * Eight on/off switches
 120  * Should be on for each port connected to a configured CPU
 121  * mask/port assignment
 122  * Two rotary switches (A & B); set to (off, 0..7)
 123  * See EXEC INTERRUPT on the 6000 SCU
 124  * When booting, one should be set to the port connected to
 125  * the bootload CPU.   The other should be off.
 126  *
 127  * If memory port B of CPU C goes to SCU D, then memory port B of all
 128  * other CPUs *and* IOMs must go to SCU D. -- AN70, 8-4.
 129  *
 130  * The base address of the SCU is the actual memory size * the port
 131  * assignment. -- AN70, 8-6.
 132  *
 133  *  43A239854 6000B Eng. Prod. Spec, 3.2.7 Interrupt Multiplex Word:
 134  *    "The IOM has the ability to set any of the 32 program interrupt
 135  *     cells located in the system controller containing the base address
 136  *     of the IOM. It should be noted that for any given IOM identity
 137  *     switch setting, the IOM can set only 8 of these program interrupt
 138  *     cells."
 139  *
 140  */
 141 
 142 /*
 143  * === Initialization and Booting -- Part 1 -- Operator's view
 144  *
 145  * Booting Instructions (GB61)
 146  * First boot the BCE OS (Bootload command Environment).  See below.
 147  * A config deck is used
 148  * Bootload SCU is the one with a base addr of zero.
 149  * BCE is on a BCE/Multics System tape
 150  * Booted from tape into the system via bootload console
 151 
 152  */
 153 
 154 /*
 155  * 58009906 (DPS8)
 156  * When CPU needs to address the SCU (for a write/read data cycle,
 157  * for example), the ETMCM board int the CU of the CPU issues a $INT
 158  * to the SCU.  This signal is sent ... to the SCAMX active port
 159  * control board in the SCU
 160  */
 161 
 162 // How?  If one of the 32 interrupt cells is set in one of the SCs,
 163 // our processor will have the interrupt present (XIP) line active.
 164 // Perhaps faults are flagged in the same way via the SXC system
 165 // controller command.
 166 
 167 // TEMPORARY
 168 // Each SCU owns a certain range of absolute memory.
 169 // CPUs use the cioc instr to talk to IOMs and other CPUs via a SCU.
 170 // IOMs use interrupts to ask a SCU to signal a CPU.
 171 // read system controller reg and set system controller reg (rscr & sscr)
 172 // Bootload SCU is the one with a base addr of zero.
 173 // 58009906
 174 // When CPU needs to address the SCU (for a write/read data cycle,
 175 // for example), the ETMCM board int the CU of the CPU issues a $INT
 176 // to the SCU.  This signal is sent ... to the SCAMX active port
 177 // control board in the
 178 // -----------------------
 179 // How?  If one of the 32 interrupt cells is set in one of the SCs,
 180 // our processor will have the interrupt present (XIP) line active.
 181 // Perhaps faults are flagged in the same way via the SXC system
 182 // controller command.
 183 
 184 /*
 185  * *** More (new) notes ***
 186  *
 187  * instr rmcm -- read mem controller mask register
 188  * ... for the selected controller, if the processor has a mask register
 189  * assigned ..
 190  * instr smcm -- set  mem controller mask register
 191  * ... for the selected controller, if the processor has a mask register
 192  * assigned, set it to C(AQ)
 193  * instr smic
 194  * turn on interrupt cells (any of 0..31)
 195  * instr cioc -- connect i/o channel, pg 173
 196  * SC addressed by Y sends a connect signal to the port specified
 197  * by C(Y)33,35
 198  * instr rscr & sscr -- Read/Store System Controller Register, pg 170
 199  *
 200  * 32 interrupt cells ... XIP
 201  * mask info
 202  * 8 mask registers
 203  * 58009906
 204  * =============
 205  *
 206  * AM81
 207  * Every active device (CPU, IOM) must be able to access all SCUs
 208  * Every SCU must have the same active device on the same SCU, so
 209  * all SCUs must have the same PORT ENABLE settings
 210  * Every active device must have the same SCU on the same port,
 211  * so all active devices will have the same config panel settings.
 212  * Ports must correspond -- port A on every CPU and IOM must either
 213  * be connected to the same SCU or not connected to any SCU.
 214  * IOMs should be on lower-numbered SCU ports than CPUs.
 215  * Multics can have 16MW words of memory.
 216  * CPUs have 8 ports, a..h.
 217  * SCUs have 8 ports, 0..7.
 218  *
 219  *
 220  * Level 68 6000 SCU Configuration Panel
 221  *   system control and monitor (cont&mon/mon/off)
 222  *   system boot control (on/off)
 223  *   alarm (disable/normal)
 224  *   maintenance panel mode (test/normal)
 225  *   store a
 226  *      mode (offline/maint/online)
 227  *      size (32k, 64k, 128k, 256k)
 228  *   store b
 229  *      mode (offline/maint/online)
 230  *      size (32k, 64k, 128k, 256k)
 231  *   execute interrupt mask assignment
 232  *      (A through D; off/0/1/2/3/4/5/6/7/m)
 233  *   [CAC] I interpret this as CPU [A..D] is connected to my port [0..7]
 234  *   address control
 235  *      lower store (a/b)
 236  *      offset (off, 16k, 32k, 64k)
 237  *      interlace (on/off)
 238  *   cycle port priority (on/off)
 239  *   port control (8 toggles) (enabled/prog cont/disable)
 240  *
 241  * The EXECUTE INTERRUPT MASK ASSIGNMENT (EIMA) rotary switches
 242  * determine where interrupts sent to memory are directed. The four EIMA
 243  * rotary switches, one for each program interrupt register, are used to
 244  * assign mask registers to system ports. The normal settings assign one
 245  * mask register to each CPU configured.
 246  *
 247  *  Assignment of a mask register to a system port designates the port as a
 248  *  control port, and that port receives interrupt present signals. Up to four
 249  *  system ports can be designated as control ports. The normal settings
 250  *  assign one mask register to each cpu configured.
 251  *
 252  *
 253  *
 254  * Configuration rules for Multics:
 255  *
 256  *   1. Each CPU in the system must be connected to each SCU in the system
 257  *
 258  *   2. Each IOM in the system must be connected to each SCU in the system
 259  *
 260  *   3. Each SCU in the system must be connected to every CPU and IOM in the
 261  *      system.
 262  *
 263  *   4. Corresponding ports on all CPUs and IOMs must be connected to the same
 264  *      SCU. For example, port A on every CPU and IOM must be connected to the
 265  *      same SCU or not connected to any SCU.
 266  *
 267  *   5. Corresponding ports on all SCUs must be connected to the same active
 268  *      device (CPU or IOM). For example, if port 0 on any SCU is connected to
 269  *      IOM A, then port 0 on all SCUs must be connected to IOM A.
 270  *
 271  *   6. IOMs should be connected to lower-number SCU ports the CPUs.
 272  *
 273  *   These rules are illustrated in Figure 3-5, where the port numbers for a
 274  *   small Multics system of 2 CPUS, 3 SCUs and 2 IOMs have been indicated
 275  *
 276  *
 277  *
 278  *
 279  *                    -----------------                      -----------------
 280  *                    |               |                      |               |
 281  *                    |     CPU A     |                      |     CPU B     |
 282  *                    |               |                      |               |
 283  *                    -----------------                      -----------------
 284  *                    | A | B | C | D |                      | A | B | C | D |
 285  *                    -----------------                      -----------------
 286  *                      |   |   |                              |   |   |
 287  *                      |   |   |                              |   |   -----------------
 288  *                      |   |   |                              |   |                   |
 289  *                      |   |   -------------------------------)---)----------------   |
 290  *                      |   |                                  |   |               |   |
 291  *   --------------------   -----------------                  |   |               |   |
 292  *   |                                      |                  |   |               |   |
 293  *   |   -----------------------------------)-------------------   |               |   |
 294  *   |   |                                  |                      |               |   |
 295  *   |   |                                  |   --------------------               |   |
 296  *   |   |                                  |   |                                  |   |
 297  * -----------------                      -----------------                      -----------------
 298  * | 7 | 6 | 5 | 4 |                      | 7 | 6 | 5 | 4 |                      | 7 | 6 | 5 | 4 |
 299  * -----------------                      -----------------                      -----------------
 300  * |               |                      |               |                      |               |
 301  * |     SCU C     |                      |     SCU B     |                      |     SCU A     |
 302  * |               |                      |               |                      |               |
 303  * -----------------                      -----------------                      -----------------
 304  * | 3 | 2 | 1 | 0 |                      | 3 | 2 | 1 | 0 |                      | 3 | 2 | 1 | 0 |
 305  * -----------------                      -----------------                      -----------------
 306  *           |   |                                  |   |                                  |   |
 307  *           |   |                                  |   -----------                        |   |
 308  *           |   |                                  |             |                        |   |
 309  *           |   -----------------------------------)---------    |                        |   |
 310  *           |                                      |        |    |                        |   |
 311  *           ----------    --------------------------        |    |                        |   |
 312  *                    |    |                                 |    |                        |   |
 313  *                    |    |   ------------------------------)----)-------------------------   |
 314  *                    |    |   |                             |    |                            |
 315  *                    |    |   |                             |    |  ---------------------------
 316  *                    |    |   |                             |    |  |
 317  *                   -----------------                      -----------------
 318  *                   | A | B | C | D |                      | A | B | C | D |
 319  *                   -----------------                      -----------------
 320  *                   |               |                      |               |
 321  *                   |     IOM A     |                      |     IOM B     |
 322  *                   |               |                      |               |
 323  *                   -----------------                      -----------------
 324  *
 325  *
 326  *
 327  *"During bootload, Multics requires a contiguous section of memory beginning at
 328  * absolute address 0 and sufficiently large to contain all routines and data
 329  * structures used during the first phase of Multics initialization (i.e.
 330  * collection 1).
 331  * The size of the section required varies among Multics release, and it also
 332  * depends on the size of the SST segment, which is dependent on the parameters
 333  * specified by the site on the SST config card. ... However
 334  * 512 KW is adequate for all circumstances. There can be no "holes" in memory
 335  * within this region. Beyond this region, "holes" can exist in memory."
 336  *
 337  *
 338  */
 339 
 340 /*
 341  * From AN70-1 May84, pg 86 (8-6)
 342  *
 343  * RSCR SC_CFG bits 9-11 lower store size
 344  *
 345  * A DPS-8 SCU may have up to four store units attached to it. If this
 346  * is the case, two store units form a pair of units. The size of a
 347  * pair of units (or a single unit) is 32K * 2 ** (lower store size)
 348  * above.
 349  */
 350 
 351 /*
 352  * From AN70-1 May84, pg 86 (8-6)
 353  *
 354  * SCU ADDRESSING
 355  *
 356  *       There are three ways in which an SCU is addressed.  In the
 357  * normal mode of operation (memory reading and writing), an active
 358  * unit (IOM or CPU) translates an absolute address into a memory
 359  * port (on it) and a relative memory address within the memory
 360  * described by the memory port. The active module sends the
 361  * address to the SCU on the proper memory port. If the active
 362  * module is enabled by the port enable mask in the referenced SCU,
 363  * the SCU will take the address given to it and provide the
 364  * necessary memory access.
 365  *
 366  *      The other two ways pertain to reading/setting control
 367  * registers in the SCU itself. For each of these, it is still
 368  * necessary to specify somehow the memory port on the CPU whose SCU
 369  * registers are desired. For the RMCM, SMCM and SMIC instructions,
 370  * this consists of providing a virtual address to the processor for
 371  * which bits 1 and 2 are the memory port desired.
 372  *
 373  *      The rscr and sscr instructions, though key off the final
 374  * absolute address to determine the SCI (or SCU store unit)
 375  * desired. Thus, software needs a way to translate a memory port
 376  * number into an absolute address to reach the SCU. This is done
 377  * with the paged segment scas, generated by int_scas (and
 378  * init_scu). scas has a page corresponding to each SCU and to each
 379  * store unit in each SCU. pmut$rscr and pmut$sscr use the memory
 380  * port number desired to generate a virtual address into scas whose
 381  * absolute address (courtesy of the ptws for sca) just happen to
 382  * describe memory within that SCU.
 383  *
 384  *       The cioc instruction (discussed below) also depends on the
 385  * final absolute address of the target operand to identify the SCU
 386  * to perform the operation. In the case of the cioc instruction,
 387  * though, the has no particular impact in Multics software. All
 388  * target operands for the cioc instruction when referencing IOMs
 389  * are in the low order SCU. When referencing CPUS, the SCU
 390  * performing the connecting has no real bearing.
 391  *
 392  * Inter-module communication
 393  *
 394  *       As mentioned earlier, communication between active modules
 395  * (CPUs and IOMs can only be performed through SCUs.
 396  *
 397  *       CPUs communicate to IOMs and other CPUs via the cioc
 398  * (connect i/o channel) instruction. The operand of the instruction
 399  * is a word in memory. The SCU containing this operand is the SCU
 400  * that performs the connect function. The word fetched from memory
 401  * contains in its low order bits the identity of a port on the SCU
 402  * to which this connection is to be sent. This only succeeds if the
 403  * target port is enabled (port enable mask) on the SCU. When the
 404  * target of the connection is an IOM; this generates a connect strobe
 405  * to the IOM. The IOM examines its mailbox in memory to determine
 406  * its course of action. When the target of the connect is another
 407  * CPU, this generates a connect fault in the target processor. The
 408  * target processor determines what course to follow on the basis
 409  * of information in memory analyzed by software. When a connect is
 410  * sent to a process (including the processor issuing the connect),
 411  * the connect is deferred until the processor stops
 412  * executing inhibited code (instructions with the inhibit bit set).
 413  *
 414  *       Signals sent from an IOM to a CPU are much more involved.
 415  * The basic flow is as follows. The IOM determines an interrupt
 416  * number. (The interrupt number is a five bit value, from 0 to 31.
 417  * The high order bits are the interrupt level.
 418  *
 419  * 0 - system fault
 420  * 1 - terminate
 421  * 2 - marker
 422  * 3 - special
 423  *
 424  * The low order three bits determines the IOM and IOM channel
 425  * group.
 426  *
 427  * 0 - IOM 0 channels 32-63
 428  * 1 - IOM 1 channels 32-63
 429  * 2 - IOM 2 channels 32-63
 430  * 3 - IOM 3 channels 32-63
 431  * 4 - IOM 0 channels 0-31
 432  * 5 - IOM 1 channels 0-31
 433  * 6 - IOM 2 channels 0-31
 434  * 7 - IOM 3 channels 0-31
 435  *
 436  * It also takes the channel number in the group (0-31 meaning
 437  * either channels 0-31 to 32-63) and sets the <channel number>th
 438  * bit in the <interrupt number>th memory location in the interrupt
 439  * mask word (IMW) array in memory. It then generates a word with
 440  * the <interrupt number>th bit set and sends this to the bootload
 441  * SCU with the SC (set execute cells) SCU command. This sets the
 442  * execute interrupt cell register in the SCU and sends an XIP
 443  * (execute interrupt present) signal to various processors
 444  * connected to the SCU. (The details of this are covered in the
 445  * next section.) One of the processors (the first to get to it)
 446  * sends an XEC (execute interrupt cells) SCU command to the SCU who
 447  * generated the XIP signal. The SCU provides the interrupt number
 448  * to the processor, who uses it to determine the address of a fault
 449  * pair in memory for the "fault" caused by this interrupt. The
 450  * processing of the XEC command acts upon the highest priority
 451  * (lowest number) bit in the execute interrupt cell register, and
 452  * also resets this bit in the register.
 453  *
 454  * Interrupts Masks and Assignment
 455  *
 456  *       The mechanism for determining which processors are candidates
 457  * for receiving an interrupt from an IOM is an involved
 458  * topic. First of all, a processor will not be interrupted as long
 459  * as it is executing inhibited instructions (instructions with the
 460  * inhibit bit set). Beyond this, though, lies the question of
 461  * interrupt masks and mask assignment.
 462  *
 463  *       Internal to the SCU are two sets of registers (A and B),
 464  * each set consisting of the execute interrupt mask register and
 465  * the interrupt mask assignment register. Each execute interrupt
 466  * mask register is 32 bits long, with each bit enabling the
 467  * corresponding bit in the execute interrupt cell register. Each
 468  * interrupt mask assignment register has two parts, an assigned bit
 469  * and a set of ports to which it is assigned (8 bits). When a bit
 470  * is set in the execute  interrupt sells register, the SCU ANDs this
 471  * bit with the corresponding bit in each of the execute interrupt
 472  * mask registers. If the corresponding bit of execute interrupt
 473  * mask register A, for example, is on, the SCU then looks at the A
 474  * interrupt mask assignment register. If this register is not
 475  * assigned (enable), no further action takes place in regards to
 476  * the A registers. (The B registers are still considered) (in
 477  * parallel, by the way).) If the register is assigned (enabled)
 478  * then interrupts will be send to all ports (processors) whose
 479  * corresponding bit is set in the interrupt mask assignment
 480  * register. This, only certain interrupts are allowed to be
 481  * signalled at any given time (base on the contents of the execute
 482  * interrupt mask registers) and only certain processors will
 483  * receive these interrupts (as controlled by the interrupt mask
 484  * assignment registers).
 485  *
 486  *       In Multics, only one processor is listed in each of the two
 487  * interrupt mask assignment registers, and no processor appears in
 488  * both. Thus there is a one for one correspondence between
 489  * interrupt masks that are assigned (interrupt mask registers whose
 490  * assigned (enabled) bit is on) and processors who have an
 491  * interrupt mask (SCU port number appears in an interrupt mask
 492  * register). So, at any one time only two processors
 493  * are eligible to receive interrupts. Other processors need not
 494  * worry about masking interrupts.
 495  *
 496  *       The contents of the interrupt mask registers may be
 497  * obtained with the SCU configuration information with the rscr
 498  * instruction and set with the sscr instruction.
 499  *
 500  *  bits   meaning
 501  *
 502  * 00-07   ports assigned to mask A (interrupt mask assignment A)
 503  * 08-08   mask A is unassigned (disabled)
 504  * 36-43   ports assigned to mask B (interrupt mask assignment B)
 505  * 44-44   mask B is unassigned (disabled)
 506  *
 507  *       The contents of a execute interrupt mask register are
 508  * obtained with the rmcm or the rscr instruction and set with the
 509  * smcm or the sscr instruction. The rmcm and smcm instruction only
 510  * work if the processor making the request has a mask register
 511  * assigned to it. If not, rmcm returns zero (no interrupt are
 512  * enabled to it) and a smcm is ignored (actually the port mask
 513  * setting is still done). The rscr and sscr instructions allow the
 514  * examining/setting of the execute interrupt mask register for any
 515  * port on a SCU; these have the same effect as smcm and rmcm if the
 516  * SCU port being referenced does not have a mask assigned to it.
 517  * The format of the data returned by these instructions is as
 518  * follows.
 519  *
 520  *  bits   meaning
 521  * 00-15   execute interrupt mask register 00-15
 522  * 32-35   SCU port mask 0-3
 523  * 36-51   execute interrupt mask register 16-31
 524  * 68-71   SCU port mask 4-7
 525  *
 526  */
 527 
 528 // SCU numbering:
 529 //
 530 // AM81-04, pg 49: "... the ports are listed in order of increasing base
 531 //    address, which corresponds to the order of mem config cards."
 532 // pg 97: "mem port size state ... port as a value (a through h) that
 533 //        corresponds to the number of the active module port to which the
 534 //        system controller is connected.
 535 //
 536 // From this, I conclude;
 537 //   The SCU connected to port A (0) is SCUA, 1 B, 2 C, etc.
 538 //   SCUA starts at address 0, and the SCUs are sorted by increasing addresses.
 539 //
 540 
 541 // ============================================================================
 542 
 543 #include <sys/time.h>
 544 #include "dps8.h"
 545 #include "dps8_sys.h"
 546 #include "dps8_iom.h"
 547 #include "dps8_cable.h"
 548 #include "dps8_cpu.h"
 549 #include "dps8_faults.h"
 550 #include "dps8_scu.h"
 551 #include "dps8_utils.h"
 552 #if defined(THREADZ) || defined(LOCKLESS)
 553 # include "threadz.h"
 554 #endif
 555 
 556 #define DBG_CTR 1
 557 
 558 scu_t scu [N_SCU_UNITS_MAX];
 559 
 560 #define N_SCU_UNITS 1 // Default
 561 
 562 static UNIT scu_unit [N_SCU_UNITS_MAX] = {
 563 #if defined(NO_C_ELLIPSIS)
 564   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 565   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 566   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 567   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 568   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 569   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 570   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 571   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 572 #else
 573   [0 ... N_SCU_UNITS_MAX-1] = {
 574     UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 575   }
 576 #endif
 577 };
 578 
 579 #define UNIT_NUM(uptr) ((uptr) - scu_unit)
 580 
 581 // Hardware configuration switches
 582 
 583 // sscr and other instructions override these settings
 584 
 585 static struct config_switches
 586   {
 587     uint mode;                            // program or manual
 588     uint port_enable [N_SCU_PORTS];       // enable/disable
 589     uint mask_enable [N_ASSIGNMENTS];     // enable/disable
 590     uint mask_assignment [N_ASSIGNMENTS]; // assigned port number
 591     uint lower_store_size;                // In K words, power of 2; 32 - 4096
 592     uint cyclic;                          // 7 bits
 593     uint nea;                             // 8 bits
 594     uint onl;                             // 4 bits
 595     uint interlace;                       // 1 bit
 596     uint lwr;                             // 1 bit
 597   } config_switches [N_SCU_UNITS_MAX];
 598 
 599 enum { MODE_MANUAL = 0, MODE_PROGRAM = 1 };
 600 
 601 unsigned int gtod_warned = 0;
 602 
 603 // ============================================================================
 604 
 605 static t_stat scu_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 606                                UNUSED int val, const UNUSED void * desc)
 607   {
 608     sim_printf("Number of SCU units in system is %d\n", scu_dev.numunits);
 609     return SCPE_OK;
 610   }
 611 
 612 static t_stat scu_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 613                               const char * cptr, UNUSED void * desc)
 614   {
 615     if (! cptr)
 616       return SCPE_ARG;
 617     int n = atoi (cptr);
 618     if (n < 1 || n > N_SCU_UNITS_MAX)
 619       return SCPE_ARG;
 620     scu_dev.numunits = (uint) n;
 621     return SCPE_OK;
 622   }
 623 
 624 static t_stat scu_show_state (UNUSED FILE * st, UNIT *uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
 625                               UNUSED const void * desc)
 626   {
 627 #if defined(TESTING)
 628     cpu_state_t * cpup = _cpup;
 629 #endif
 630     long scu_unit_idx = UNIT_NUM (uptr);
 631     if (scu_unit_idx < 0 || scu_unit_idx >= (int) scu_dev.numunits)
 632       {
 633         sim_debug (DBG_ERR, & scu_dev,
 634                    "scu_show_state: Invalid unit number %ld\n",
 635                    (long) scu_unit_idx);
 636         sim_printf ("error: Invalid unit number %ld\n", (long) scu_unit_idx);
 637         return SCPE_ARG;
 638       }
 639 
 640     sim_printf ("SCU unit number %ld\n", (long) scu_unit_idx);
 641     scu_t * scup = scu + scu_unit_idx;
 642     sim_printf ("    Mode %s\n",
 643                 config_switches[scu_unit_idx].mode ? "PROGRAM" : "MANUAL");
 644 
 645     for (int i = 0; i < N_SCU_PORTS; i ++)
 646       {
 647         struct ports * pp = scup -> ports + i;
 648 
 649         sim_printf ("    Port %d %s dev_idx %d dev_port %d type %s\n",
 650                     i, scup->port_enable[i] ? "ENABLE " : "DISABLE",
 651                     pp->dev_idx, pp->dev_port[XXX_TEMP_SCU_SUBPORT],
 652                     pp->type == ADEV_NONE ? "NONE" :
 653                     pp->type == ADEV_CPU ? "CPU" :
 654                     pp->type == ADEV_IOM ? "IOM" :
 655                     "<enum broken>");
 656       }
 657     for (int i = 0; i < N_ASSIGNMENTS; i ++)
 658       {
 659         //struct interrupts * ip = scup -> interrupts + i;
 660 
 661         sim_printf ("    Cell %c\n", 'A' + i);
 662         sim_printf ("        exec_intr_mask %012o\n",
 663                     scup -> exec_intr_mask [i]);
 664         sim_printf ("        mask_enable %s\n",
 665                     scup -> mask_enable [i] ? "ENABLE" : "DISABLE");
 666         sim_printf ("        mask_assignment %d\n",
 667                     scup -> mask_assignment [i]);
 668         sim_printf ("        cells ");
 669         for (int j = 0; j < N_CELL_INTERRUPTS; j ++)
 670           sim_printf("%d", scup -> cells [j]);
 671         sim_printf ("\n");
 672       }
 673     sim_printf("Lower store size: %d\n", scup -> lower_store_size);
 674     sim_printf("Cyclic: %03o\n",         scup -> cyclic);
 675     sim_printf("NEA: %03o\n",            scup -> nea);
 676     sim_printf("Online: %02o\n",         scup -> onl);
 677     sim_printf("Interlace: %o\n",        scup -> interlace);
 678     sim_printf("Lower: %o\n",            scup -> lwr);
 679     sim_printf("ID: %o\n",               scup -> id);
 680     sim_printf("mode_reg: %06o\n",       scup -> mode_reg);
 681     sim_printf("Elapsed days: %d\n",     scup -> elapsed_days);
 682     sim_printf("Steady clock: %d\n",     scup -> steady_clock);
 683     sim_printf("Bullet time: %d\n",      scup -> bullet_time);
 684     sim_printf("Y2K enabled: %d\n",      scup -> y2k);
 685     return SCPE_OK;
 686   }
 687 
 688 static t_stat scu_show_config (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 689                                UNUSED int val, UNUSED const void * desc)
 690 {
 691 #if defined(TESTING)
 692     cpu_state_t * cpup = _cpup;
 693 #endif
 694     static const char * map [N_SCU_PORTS] =
 695       {
 696         "0", "1", "2", "3", "4", "5", "6", "7"
 697       };
 698     long scu_unit_idx = UNIT_NUM (uptr);
 699     if (scu_unit_idx < 0 || scu_unit_idx >= (int) scu_dev.numunits)
 700       {
 701         sim_debug (DBG_ERR, & scu_dev,
 702                    "scu_show_config: Invalid unit number %ld\n",
 703                    (long) scu_unit_idx);
 704         sim_printf ("error: Invalid unit number %ld\n", (long) scu_unit_idx);
 705         return SCPE_ARG;
 706       }
 707 
 708     sim_printf ("SCU unit number %ld\n", (long) scu_unit_idx);
 709 
 710     struct config_switches * sw = config_switches + scu_unit_idx;
 711 
 712     const char * mode = "<out of range>";
 713     switch (sw -> mode)
 714       {
 715         case MODE_PROGRAM:
 716           mode = "Program";
 717           break;
 718         case MODE_MANUAL:
 719           mode = "Manual";
 720           break;
 721       }
 722 
 723     sim_printf ("Mode:                       %s\n", mode);
 724     sim_printf ("Port Enable:             ");
 725     for (int i = 0; i < N_SCU_PORTS; i ++)
 726       sim_printf (" %3o", sw -> port_enable [i]);
 727     sim_printf ("\n");
 728     for (int i = 0; i < N_ASSIGNMENTS; i ++)
 729       {
 730         sim_printf ("Mask %c:                     %s\n",
 731                     'A' + i,
 732                     sw->mask_enable[i] ? (map[sw->mask_assignment[i]]) : "Off");
 733       }
 734     sim_printf ("Lower Store Size:           %o\n",   sw -> lower_store_size);
 735     sim_printf ("Cyclic:                     %03o\n", sw -> cyclic);
 736     sim_printf ("Non-existent address:       %03o\n", sw -> nea);
 737 
 738     return SCPE_OK;
 739   }
 740 
 741 //
 742 // set scu0 config=<blah> [;<blah>]
 743 //
 744 //    blah =
 745 //           mode=  manual | program
 746 //           mask[A|B] = off | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
 747 //           portN = enable | disable
 748 //           lwrstoresize = 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096
 749 //           cyclic = n
 750 //           nea = n
 751 //
 752 //      o  nea is not implemented; will read as "nea off"
 753 //      o  Multics sets cyclic priority explicitly; config
 754 //         switches are ignored.
 755 //      o  STORE A, A1, B, B1 ONLINE/OFFLINE not implemented;
 756 //         will always read online.
 757 //      o  store size if not enforced; a full memory complement
 758 //         is provided.
 759 //      o  interlace not implemented; will read as 'off'
 760 //      o  LOWER STORE A/B not implemented.
 761 //      o  MASK is 'MASK/PORT ASSIGNMENT' analogous to the
 762 //         'EXECUTE INTERRUPT MASK ASSIGNMENT of a 6000 SCU
 763 
 764 static config_value_list_t cfg_mode_list [] =
 765   {
 766     { "manual",  0 },
 767     { "program", 1 },
 768     { NULL,      0 }
 769   };
 770 
 771 static config_value_list_t cfg_mask_list [] =
 772   {
 773     { "off", -1 },
 774     { NULL,  0  }
 775   };
 776 
 777 static config_value_list_t cfg_able_list [] =
 778   {
 779     { "disable", 0 },
 780     { "enable",  1 },
 781     { NULL,      0 }
 782   };
 783 
 784 static config_value_list_t cfg_size_list [] =
 785   {
 786     { "32",    0 },
 787     { "64",    1 },
 788     { "128",   2 },
 789     { "256",   3 },
 790     { "512",   4 },
 791     { "1024",  5 },
 792     { "2048",  6 },
 793     { "4096",  7 },
 794     { "32K",   0 },
 795     { "64K",   1 },
 796     { "128K",  2 },
 797     { "256K",  3 },
 798     { "512K",  4 },
 799     { "1024K", 5 },
 800     { "2048K", 6 },
 801     { "4096K", 7 },
 802     { "1M",    5 },
 803     { "2M",    6 },
 804     { "4M",    7 },
 805     { NULL,    0 }
 806   };
 807 
 808 static config_value_list_t cfg_on_off [] =
 809   {
 810     { "off",     0 },
 811     { "on",      1 },
 812     { "disable", 0 },
 813     { "enable",  1 },
 814     { NULL,      0 }
 815   };
 816 
 817 static config_list_t scu_config_list [] =
 818   {
 819     /*  0 */ { "mode",         1, 0,               cfg_mode_list },
 820     /*  1 */ { "maska",        0, N_SCU_PORTS - 1, cfg_mask_list },
 821     /*  2 */ { "maskb",        0, N_SCU_PORTS - 1, cfg_mask_list },
 822     /*  3 */ { "port0",        1, 0,               cfg_able_list },
 823     /*  4 */ { "port1",        1, 0,               cfg_able_list },
 824     /*  5 */ { "port2",        1, 0,               cfg_able_list },
 825     /*  6 */ { "port3",        1, 0,               cfg_able_list },
 826     /*  7 */ { "port4",        1, 0,               cfg_able_list },
 827     /*  8 */ { "port5",        1, 0,               cfg_able_list },
 828     /*  9 */ { "port6",        1, 0,               cfg_able_list },
 829     /* 10 */ { "port7",        1, 0,               cfg_able_list },
 830     /* 11 */ { "lwrstoresize", 0, 7,               cfg_size_list },
 831     /* 12 */ { "cyclic",       0, 0177,            NULL          },
 832     /* 13 */ { "nea",          0, 0377,            NULL          },
 833     // mask: 8 a_online, 4 a1_online, 2 b_online, 1, b1_online
 834     /* 14 */ { "onl",          0, 017,             NULL          },
 835     /* 15 */ { "int",          0, 1,               NULL          },
 836     /* 16 */ { "lwr",          0, 1,               NULL          },
 837 
 838     // Hacks
 839 
 840     /* 17 */ { "elapsed_days", 0, 20000,           NULL       },
 841     /* 18 */ { "steady_clock", 0, 1,               cfg_on_off },
 842     /* 19 */ { "bullet_time",  0, 1,               cfg_on_off },
 843     /* 20 */ { "y2k",          0, 1,               cfg_on_off },
 844              { NULL,           0, 0,               NULL       }
 845   };
 846 
 847 static t_stat scu_set_config (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 848                               const char * cptr, UNUSED void * desc)
 849   {
 850 #if defined(TESTING)
 851     cpu_state_t * cpup = _cpup;
 852 #endif
 853     long scu_unit_idx = UNIT_NUM (uptr);
 854     if (scu_unit_idx < 0 || scu_unit_idx >= (int) scu_dev.numunits)
 855       {
 856         sim_debug (DBG_ERR, & scu_dev,
 857                    "scu_set_config: Invalid unit number %ld\n", (long) scu_unit_idx);
 858         sim_printf ("error: scu_set_config: Invalid unit number %ld\n",
 859                     (long) scu_unit_idx);
 860         return SCPE_ARG;
 861       }
 862 
 863     struct config_switches * sw = config_switches + scu_unit_idx;
 864 
 865     config_state_t cfg_state = { NULL, NULL };
 866 
 867     for (;;)
 868       {
 869         int64_t v;
 870         int rc = cfg_parse ("scu_set_config", cptr, scu_config_list,
 871                            & cfg_state, & v);
 872         if (rc == -1) // done
 873           break;
 874 
 875         if (rc == -2) // error
 876           {
 877             cfg_parse_done (& cfg_state);
 878             return SCPE_ARG;
 879           }
 880 
 881         const char * p = scu_config_list [rc].name;
 882         if (strcmp (p, "mode") == 0)
 883           sw -> mode = (uint) v;
 884         else if (strcmp (p, "maska") == 0)
 885           {
 886             if (v == -1)
 887               sw -> mask_enable [0] = false;
 888             else
 889               {
 890                 sw -> mask_enable [0] = true;
 891                 sw -> mask_assignment [0] = (uint) v;
 892               }
 893           }
 894         else if (strcmp (p, "maskb") == 0)
 895           {
 896             if (v == -1)
 897               sw -> mask_enable [1] = false;
 898             else
 899               {
 900                 sw -> mask_enable [1] = true;
 901                 sw -> mask_assignment [1] = (uint) v;
 902               }
 903           }
 904         else if (strcmp (p, "port0") == 0)
 905           sw -> port_enable [0] = (uint) v;
 906         else if (strcmp (p, "port1") == 0)
 907           sw -> port_enable [1] = (uint) v;
 908         else if (strcmp (p, "port2") == 0)
 909           sw -> port_enable [2] = (uint) v;
 910         else if (strcmp (p, "port3") == 0)
 911           sw -> port_enable [3] = (uint) v;
 912         else if (strcmp (p, "port4") == 0)
 913           sw -> port_enable [4] = (uint) v;
 914         else if (strcmp (p, "port5") == 0)
 915           sw -> port_enable [5] = (uint) v;
 916         else if (strcmp (p, "port6") == 0)
 917           sw -> port_enable [6] = (uint) v;
 918         else if (strcmp (p, "port7") == 0)
 919           sw -> port_enable [7] = (uint) v;
 920         else if (strcmp (p, "lwrstoresize") == 0)
 921           sw -> lower_store_size = (uint) v;
 922         else if (strcmp (p, "cyclic") == 0)
 923           sw -> cyclic = (uint) v;
 924         else if (strcmp (p, "nea") == 0)
 925           sw -> nea = (uint) v;
 926         else if (strcmp (p, "onl") == 0)
 927           sw -> onl = (uint) v;
 928         else if (strcmp (p, "int") == 0)
 929           sw -> interlace = (uint) v;
 930         else if (strcmp (p, "lwr") == 0)
 931           sw -> lwr = (uint) v;
 932         else if (strcmp (p, "elapsed_days") == 0)
 933           scu [scu_unit_idx].elapsed_days = (uint) v;
 934         else if (strcmp (p, "steady_clock") == 0)
 935           scu [scu_unit_idx].steady_clock = (uint) v;
 936         else if (strcmp (p, "bullet_time") == 0)
 937           scu [scu_unit_idx].bullet_time = (uint) v;
 938         else if (strcmp (p, "y2k") == 0)
 939           scu [scu_unit_idx].y2k = (uint) v;
 940         else
 941           {
 942             sim_printf ("error: scu_set_config: invalid cfg_parse rc <%d>\n",
 943                          rc);
 944             cfg_parse_done (& cfg_state);
 945             return SCPE_ARG;
 946           }
 947       } // process statements
 948     cfg_parse_done (& cfg_state);
 949     return SCPE_OK;
 950   }
 951 
 952 static MTAB scu_mod [] =
 953   {
 954     {
 955       MTAB_XTD | MTAB_VUN | \
 956       MTAB_NMO | MTAB_VALR,                          /* Mask               */
 957       0,                                             /* Match              */
 958       (char *) "CONFIG",                             /* Print string       */
 959       (char *) "CONFIG",                             /* Match string       */
 960       scu_set_config,                                /* Validation routine */
 961       scu_show_config,                               /* Display routine    */
 962       NULL,                                          /* Value descriptor   */
 963       NULL                                           /* Help               */
 964     },
 965     {
 966       MTAB_XTD | MTAB_VDV | \
 967       MTAB_NMO | MTAB_VALR,                          /* Mask               */
 968       0,                                             /* Match              */
 969       (char *) "NUNITS",                             /* Print string       */
 970       (char *) "NUNITS",                             /* Match string       */
 971       scu_set_nunits,                                /* Validation routine */
 972       scu_show_nunits,                               /* Display routine    */
 973       (char *) "Number of SCU units in the system",  /* Value descriptor   */
 974       NULL                                           /* Help               */
 975     },
 976     {
 977       MTAB_XTD | MTAB_VUN | \
 978       MTAB_NMO | MTAB_VALR,                          /* Mask               */
 979       0,                                             /* Match              */
 980       (char *) "STATE",                              /* Print string       */
 981       (char *) "STATE",                              /* Match string       */
 982       NULL,                                          /* Validation routine */
 983       scu_show_state,                                /* Display routine    */
 984       (char *) "SCU unit internal state",            /* Value descriptor   */
 985       NULL                                           /* Help               */
 986     },
 987     {
 988       MTAB_XTD | MTAB_VUN | \
 989       MTAB_NMO | MTAB_VALR,                          /* Mask               */
 990       0,                                             /* Match              */
 991       (char *) "RESET",                              /* Print string       */
 992       (char *) "RESET",                              /* Match string       */
 993       scu_reset_unit,                                /* Validation routine */
 994       NULL,                                          /* Display routine    */
 995       (char *) "reset SCU unit",                     /* Value descriptor   */
 996       NULL                                           /* Help               */
 997     },
 998     {
 999       0, 0, NULL, NULL, NULL, NULL, NULL, NULL
1000     }
1001   };
1002 
1003 //static t_stat scu_reset (DEVICE *dptr);
1004 
1005 static DEBTAB scu_dt [] =
1006   {
1007     { (char *) "TRACE",  DBG_TRACE,  NULL },
1008     { (char *) "NOTIFY", DBG_NOTIFY, NULL },
1009     { (char *) "INFO",   DBG_INFO,   NULL },
1010     { (char *) "ERR",    DBG_ERR,    NULL },
1011     { (char *) "WARN",   DBG_WARN,   NULL },
1012     { (char *) "DEBUG",  DBG_DEBUG,  NULL },
1013     { (char *) "INTR",   DBG_INTR,   NULL }, // Don't move as it messes up DBG messages
1014     { (char *) "ALL",    DBG_ALL,    NULL },
1015     {  NULL,             0,          NULL }
1016   };
1017 
1018 DEVICE scu_dev =
1019   {
1020     (char *) "SCU",  /* Name                */
1021     scu_unit,        /* Units               */
1022     NULL,            /* Registers           */
1023     scu_mod,         /* Modifiers           */
1024     N_SCU_UNITS,     /* #Units              */
1025     10,              /* Address radix       */
1026     8,               /* Address width       */
1027     1,               /* Address increment   */
1028     8,               /* Data radix          */
1029     8,               /* Data width          */
1030     NULL,            /* Examine routine     */
1031     NULL,            /* Deposit routine     */
1032     & scu_reset,     /* Reset routine       */
1033     NULL,            /* Boot routine        */
1034     NULL,            /* Attach routine      */
1035     NULL,            /* Detach routine      */
1036     NULL,            /* Context             */
1037     DEV_DEBUG,       /* Flags               */
1038     0,               /* Debug control flags */
1039     scu_dt,          /* Debug flag names    */
1040     NULL,            /* Memory size change  */
1041     NULL,            /* Logical name        */
1042     NULL,            /* Help                */
1043     NULL,            /* Attach_help         */
1044     NULL,            /* Help_ctx            */
1045     NULL,            /* Description         */
1046     NULL             /* End                 */
1047   };
1048 
1049 static void dump_intr_regs (char * ctx, uint scu_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
1050   {
1051 #if defined(TESTING)
1052     scu_t * up = scu + scu_unit_idx;
1053     cpu_state_t * cpup = _cpup;
1054 
1055     sim_debug (DBG_DEBUG, & scu_dev,
1056                "%s A: mask %011o enable %o assignment %o\n",
1057                ctx, up -> exec_intr_mask [0], up -> mask_enable [0],
1058                up -> mask_assignment [0]);
1059     sim_debug (DBG_DEBUG, & scu_dev,
1060                "%s B: mask %011o enable %o assignment %o\n",
1061                ctx, up -> exec_intr_mask [1], up -> mask_enable [1],
1062                up -> mask_assignment [1]);
1063 
1064 
1065 
1066 
1067 
1068 
1069 
1070 
1071 
1072 
1073 
1074 
1075 
1076 
1077 
1078 
1079 
1080 
1081 
1082 
1083 
1084 
1085 
1086 
1087 
1088 
1089 
1090 
1091 
1092 
1093 
1094 
1095 
1096 
1097 
1098 
1099 
1100 
1101 
1102 
1103 
1104 
1105 
1106 
1107 
1108 
1109 
1110 #endif
1111    }
1112 
1113 void scu_unit_reset (int scu_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
1114   {
1115     scu_t * up = scu + scu_unit_idx;
1116     struct config_switches * sw = config_switches + scu_unit_idx;
1117 
1118     for (int i = 0; i < N_SCU_PORTS; i ++)
1119       {
1120         up -> port_enable [i] = sw -> port_enable [i];
1121       }
1122 
1123     for (int i = 0; i < N_ASSIGNMENTS; i ++)
1124       {
1125         up -> mask_enable [i]     = sw -> mask_enable [i];
1126         up -> mask_assignment [i] = sw -> mask_assignment [i];
1127       }
1128     up -> lower_store_size = sw -> lower_store_size;
1129     up -> cyclic           = sw -> cyclic;
1130     up -> nea              = sw -> nea;
1131     up -> onl              = sw -> onl;
1132     up -> interlace        = sw -> interlace;
1133     up -> lwr              = sw -> lwr;
1134 
1135 // This is to allow the CPU reset to update the memory map. IAC clears the
1136 // attached SCUs; they clear the attached IOMs.
1137 
1138     for (uint port_num = 0; port_num < N_SCU_PORTS; port_num ++)
1139       {
1140         struct ports * portp = & scu [scu_unit_idx].ports [port_num];
1141         if (portp->type != ADEV_IOM)
1142           continue;
1143         //if (! scu [scu_unit_idx].port_enable [scu_port_num])
1144           //continue;
1145         iom_unit_reset_idx ((uint) portp->dev_idx);
1146       }
1147 
1148 // CAC - These settings were reversed engineer from the code instead
1149 // of from the documentation. In case of issues, try fixing these, not the
1150 // code.
1151 
1152     for (int i = 0; i < N_ASSIGNMENTS; i ++)
1153       {
1154         // XXX Hack for t4d
1155         up -> exec_intr_mask [i] = 037777777777;
1156       }
1157   }
1158 
1159 t_stat scu_reset (UNUSED DEVICE * dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1160   {
1161     // On reset, instantiate the config switch settings
1162 
1163     for (int scu_unit_idx = 0; scu_unit_idx < N_SCU_UNITS_MAX; scu_unit_idx ++)
1164       scu_unit_reset (scu_unit_idx);
1165     return SCPE_OK;
1166   }
1167 
1168 // ============================================================================
1169 
1170 #if defined(THREADZ) || defined(LOCKLESS)
1171 static pthread_mutex_t clock_lock = PTHREAD_MUTEX_INITIALIZER;
1172 #endif
1173 
1174 // The SCU clock is 52 bits long; fits in t_uint64
1175 static uint64 set_SCU_clock (cpu_state_t * cpup, uint scu_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
1176   {
1177 #if defined(THREADZ) || defined(LOCKLESS)
1178     pthread_mutex_lock (& clock_lock);
1179 #endif
1180 
1181 // The emulator supports two clock models: steady and real
1182 // In steady mode the time of day is coupled to the instruction clock,
1183 // allowing reproducible behavior. In real, the clock is
1184 // coupled to the actual time-of-day.
1185 
1186     if (scu [0].steady_clock)
1187       {
1188         // The is a bit of code that is waiting for 5000 ms; this
1189         // fools into going faster
1190 #if defined(NEED_128)
1191         uint128 big = construct_128 (0, cpu.instrCnt);
1192         // Sync up the clock and the TR; see wiki page "CAC 08-Oct-2014"
1193         //big *= 4u;
1194         big = lshift_128 (big, 2);
1195         if (scu [0].bullet_time)
1196           big = multiply_128 (big, construct_128 (0, 10000u));
1197 
1198         //big += scu [0].elapsed_days * 1000000llu * 60llu * 60llu * 24llu;
1199         uint128 days = construct_128 (0, scu[0].elapsed_days);
1200         days         = multiply_128 (days, construct_128 (0, 1000000));
1201         days         = multiply_128 (days, construct_128 (0, 60 * 60 * 24));
1202         big          = add_128 (big, days);
1203 #else
1204         __uint128_t big = cpu.instrCnt;
1205         // Sync up the clock and the TR; see wiki page "CAC 08-Oct-2014"
1206         big *= 4u;
1207         //big /= 100u;
1208         if (scu [0].bullet_time)
1209           big *= 10000;
1210 
1211         big += scu [0].elapsed_days * 1000000llu * 60llu * 60llu * 24llu;
1212 #endif
1213 
1214         // Boot time
1215 
1216         // load_fnp is complaining that FNP core image is more than 5 years old; try
1217         // moving the 'boot time' back to MR12.3 release date. (12/89 according to
1218         // https://www.multicians.org/chrono.html
1219 
1220         // date -d "1990-01-01 00:00:00 -9" +%s
1221         // 631184400
1222         // For debugging MR12.3 and earlier with steady_clock, uncomment --
1223         // uint64 UNIX_secs = 631184400;
1224 
1225         // Otherwise, we'll use the current time as the steady_clock starting point --
1226         uint64 UNIX_secs = (uint64)time(NULL);
1227 
1228 #if defined(NEED_128)
1229         uint64 UNIX_usecs = UNIX_secs * 1000000llu + big.l;
1230 #else
1231         uint64 UNIX_usecs = UNIX_secs * 1000000llu + (uint64) big;
1232 #endif
1233         // now determine uSecs since Jan 1, 1901 ...
1234         uint64 Multics_usecs = 2177452800000000llu + UNIX_usecs;
1235 
1236         // The casting to uint show be okay; both are 64 bit, so if
1237         // user_correction is <0, it will come out in the wash ok.
1238         Multics_usecs += (uint64) scu [scu_unit_idx].user_correction;
1239 
1240         // The get calendar clock function is guaranteed to return
1241         // different values on successive calls.
1242 
1243         if (scu [scu_unit_idx].last_time >= Multics_usecs)
1244           {
1245             sim_debug (DBG_TRACE, & scu_dev, "finagle clock\n");
1246             Multics_usecs = scu [scu_unit_idx].last_time + 1;
1247           }
1248         scu [scu_unit_idx].last_time = Multics_usecs;
1249         goto done;
1250       }
1251 
1252     // The calendar clock consists of a 52-bit register which counts
1253     // microseconds and is readable as a double-precision integer by a
1254     // single instruction from any central processor. This rate is in
1255     // the same order of magnitude as the instruction processing rate of
1256     // the GE-645, so that timing of 10-instruction subroutines is
1257     // meaningful. The register is wide enough that overflow requires
1258     // several tens of years; thus it serves as a calendar containing
1259     // the number of microseconds since 0000 GMT, January 1, 1901
1260     ///  Secs from Jan 1, 1901 to Jan 1, 1970 - 2 177 452 800 Seconds
1261     /// uSecs from Jan 1, 1901 to Jan 1, 1970 - 2 177 452 800 000 000 uSeconds
1262 
1263     struct timeval now;
1264     gettimeofday(& now, NULL);
1265 
1266     if (scu [0].y2k) // Apply clock skew when Y2K mode enabled
1267       {
1268         // Back the clock up to just after the MR12.5 release
1269         // $ date --date='30 years ago' +%s ; date +%s
1270         // 1685451324
1271         // 7738766524
1272         now.tv_sec -= (1685451324 - 738766524); // XXX(jhj): make dynamic!
1273       }
1274     uint64 UNIX_secs  = (uint64) now.tv_sec;
1275     uint64 UNIX_usecs = UNIX_secs * 1000000LL + (uint64) now.tv_usec;
1276 
1277     static uint64 last_UNIX_usecs = 0;
1278     if ( (!sim_quiet) && (UNIX_usecs < last_UNIX_usecs))
1279       {
1280         if (gtod_warned < 11)
1281           {
1282             sim_warn ("\rHost clock went backwards %llu uS!\r\n",
1283                       (unsigned long long)(last_UNIX_usecs - UNIX_usecs));
1284             gtod_warned++;
1285           }
1286         else if (gtod_warned == 11)
1287           {
1288             sim_warn ("\rHost clock went backwards %llu uS!  Suppressing further warnings.\r\n",
1289                       (unsigned long long)(last_UNIX_usecs - UNIX_usecs));
1290             gtod_warned++;
1291           }
1292       }
1293     last_UNIX_usecs = UNIX_usecs;
1294 
1295     // now determine uSecs since Jan 1, 1901 ...
1296     uint64 Multics_usecs = 2177452800000000LL + UNIX_usecs;
1297 
1298     // Correction factor from the set time command
1299 
1300     // The casting to uint show be okay; both are 64 bit, so if
1301     // user_correction is <0, it will come out in the wash ok.
1302     Multics_usecs += (uint64) scu [scu_unit_idx].user_correction;
1303 
1304     if (scu [scu_unit_idx].last_time >= Multics_usecs)
1305         Multics_usecs = scu [scu_unit_idx].last_time + 1;
1306     scu [scu_unit_idx].last_time = Multics_usecs;
1307 
1308 done:
1309 #if defined(THREADZ) || defined(LOCKLESS)
1310     pthread_mutex_unlock (& clock_lock);
1311 #endif
1312 
1313     return scu [scu_unit_idx].last_time;
1314   }
1315 
1316 //static char pcellb [N_CELL_INTERRUPTS + 1];
1317 static char * pcells (uint scu_unit_idx, char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
1318   {
1319     for (uint i = 0; i < N_CELL_INTERRUPTS; i ++)
1320       {
1321         if (scu [scu_unit_idx].cells [i])
1322           buf [i] = '1';
1323         else
1324           buf [i] = '0';
1325       }
1326     buf [N_CELL_INTERRUPTS] = '\0';
1327     return buf;
1328   }
1329 
1330 // Either an interrupt has arrived on a port, or a mask register has
1331 // been updated. Bring the CPU up date on the interrupts.
1332 
1333 // threadz notes:
1334 //
1335 // deliver_interrupts is called either from a CPU instruction or from
1336 // IOM set_general_interrupt on the IOM thread.
1337 //
1338 // potential race conditions:
1339 //   CPU variables: XIP
1340 //   SCU variables: cells, mask_enable, exec_intr_mask, mask assignment
1341 
1342 // Always called with SCU lock set
1343 
1344 static void deliver_interrupts (cpu_state_t * cpup, uint scu_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
1345   {
1346 #if defined(TESTING)
1347     {
1348       cpu_state_t * cpup = _cpup;
1349       sim_debug (DBG_DEBUG, & scu_dev, "deliver_interrupts %o\n", scu_unit_idx);
1350     }
1351 #endif
1352 #if defined(THREADZ) || defined(LOCKLESS)
1353     atomic_thread_fence (memory_order_seq_cst);
1354 #endif /* defined(THREADZ) || defined(LOCKLESS) */
1355     for (uint cpun = 0; cpun < cpu_dev.numunits; cpun ++)
1356       {
1357         cpus[cpun].events.XIP[scu_unit_idx] = false;
1358       }
1359 
1360 // If the CIOC generates marker and terminate interrupts, they will be posted simultaneously.
1361 // Since the interrupts are recognized by priority and terminate has a higher priority then
1362 // marker, if will be delivered first. The following code will deliver marker before terminate.
1363 
1364 #if defined(REORDER)
1365     for (uint jnum = 0; jnum < N_CELL_INTERRUPTS; jnum ++)
1366       {
1367         static const uint reorder[N_CELL_INTERRUPTS] = {
1368            0,  1,  2,  3,  4,  5,  6,  7,
1369           16, 17, 18, 29, 20, 21, 22, 23,
1370            8,  9, 10, 11, 12, 13, 14, 15,
1371           25, 25, 26, 27, 28, 29, 30, 31 };
1372         uint inum = reorder[jnum];
1373         if (! scu [scu_unit_idx].cells [inum])
1374           continue; //
1375         sim_debug (DBG_DEBUG, & scu_dev, "trying to deliver %d\n", inum);
1376         sim_debug (DBG_INTR, & scu_dev,
1377                    "scu %u trying to deliver %d\n", scu_unit_idx, inum);
1378 
1379         for (uint pima = 0; pima < N_ASSIGNMENTS; pima ++) // A, B
1380           {
1381             //sim_debug (DBG_DEBUG, & scu_dev,
1382             //           "trying inum %u pima %u enable %u\n"
1383             //           , inum, pima, scu [scu_unit_idx].mask_enable [pima]);
1384             if (scu [scu_unit_idx].mask_enable [pima] == 0)
1385               continue;
1386             uint mask = scu [scu_unit_idx].exec_intr_mask [pima];
1387             uint port = scu [scu_unit_idx].mask_assignment [pima];
1388             //sim_debug (DBG_DEBUG, & scu_dev,
1389             //           "mask %u port %u type %u cells %o\n",
1390             //           mask, port, scu [scu_unit_idx].ports [port].type,
1391             //           scu [scu_unit_idx].cells [inum]);
1392             if (scu [scu_unit_idx].ports [port].type != ADEV_CPU)
1393               continue;
1394             if ((mask & (1u << (31 - inum))) != 0)
1395               {
1396                 uint sn = 0;
1397                 if (scu[scu_unit_idx].ports[port].is_exp)
1398                   {
1399                     sn = (uint) scu[scu_unit_idx].ports[port].xipmaskval;
1400                     if (sn >= N_SCU_SUBPORTS)
1401                       {
1402                         sim_warn ("XIP mask not set; defaulting to subport 0\n");
1403                         sn = 0;
1404                       }
1405                   }
1406                 if (! cables->scu_to_cpu[scu_unit_idx][port][sn].in_use)
1407                   {
1408                     sim_warn ("bad scu_unit_idx %u\n", scu_unit_idx);
1409                     continue;
1410                   }
1411                 uint cpu_unit_udx = cables->scu_to_cpu[scu_unit_idx][port][sn].cpu_unit_idx;
1412 # if defined(THREADZ) || defined(LOCKLESS)
1413                 // Need to do this before setting XIP to avoid race condition
1414                 // We are about to interrupt a CPU; this is done to either
1415                 //   Multics signalling, such has CAM cache clear
1416                 //   Adding a new CPU
1417                 //   Reading a deleted CPU
1418                 //   Starting an ISOLTS CPU
1419                 // If it is a Multics signal, the target CPU will be marked as 'inMultics';
1420                 // For the other cases, 'inMultics ' will not be set.
1421                 // cpup != NULL means that this interrupt was generated by a CPU, rather
1422                 // than an IOM; we need this to set the calling thread into slave mode.
1423                 if ((! sys.sys_opts.nosync) && cpup && (! cpus[cpu_unit_udx].inMultics)) {
1424                   // The interrupt is to start or restart a CPU.
1425 #  ifdef SYNCTEST
1426                   sim_printf ("CPU %c becomes clock master\n", 'A' + cpu_unit_udx);
1427 #  endif
1428                   becomeClockMaster (cpu_unit_udx);
1429                   // Set this so that the calling thread will join the queue immediately.
1430                   cpu.syncClockModePoll = 0;
1431                   cpu.syncClockModeCache = true;
1432                   __asm volatile ("");
1433                   atomic_thread_fence (memory_order_seq_cst);
1434                 }
1435                 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1436 #  if defined(TESTING)
1437                 HDBGIntrSet (inum, cpu_unit_udx, scu_unit_idx, __func__);
1438 #  endif
1439                 createCPUThread((uint) cpu_unit_udx);
1440 #  if !defined(NO_TIMEWAIT)
1441                 wakeCPU ((uint) cpu_unit_udx);
1442 #  endif
1443                 sim_debug (DBG_DEBUG, & scu_dev,
1444                            "interrupt set for CPU %d SCU %d\n",
1445                            cpu_unit_udx, scu_unit_idx);
1446 # else // ! THREADZ
1447 //if (cpu_unit_udx && ! cpu.isRunning) sim_printf ("starting CPU %c\n", cpu_unit_udx + 'A');
1448 #  if defined(ROUND_ROBIN)
1449                 cpus[cpu_unit_udx].isRunning = true;
1450 #  endif
1451                 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1452 sim_debug (DBG_DEBUG, & scu_dev, "interrupt set for CPU %d SCU %d\n", cpu_unit_udx, scu_unit_idx);
1453                 sim_debug (DBG_INTR, & scu_dev,
1454                            "XIP set for SCU %d\n", scu_unit_idx);
1455 # endif // ! THREADZ
1456               }
1457           }
1458       }
1459 #else // !REORDER
1460     for (uint inum = 0; inum < N_CELL_INTERRUPTS; inum ++)
1461       {
1462         if (! scu [scu_unit_idx].cells [inum])
1463           continue; //
1464         sim_debug (DBG_DEBUG, & scu_dev, "trying to deliver %d\n", inum);
1465         sim_debug (DBG_INTR, & scu_dev,
1466                    "scu %u trying to deliver %d\n", scu_unit_idx, inum);
1467 
1468         for (uint pima = 0; pima < N_ASSIGNMENTS; pima ++) // A, B
1469           {
1470             //sim_debug (DBG_DEBUG, & scu_dev,
1471             //           "trying inum %u pima %u enable %u\n"
1472             //           , inum, pima, scu [scu_unit_idx].mask_enable [pima]);
1473             if (scu [scu_unit_idx].mask_enable [pima] == 0)
1474               continue;
1475             uint mask = scu [scu_unit_idx].exec_intr_mask [pima];
1476             uint port = scu [scu_unit_idx].mask_assignment [pima];
1477             //sim_debug (DBG_DEBUG, & scu_dev,
1478             //           "mask %u port %u type %u cells %o\n",
1479             //           mask, port, scu [scu_unit_idx].ports [port].type,
1480             //           scu [scu_unit_idx].cells [inum]);
1481             if (scu [scu_unit_idx].ports [port].type != ADEV_CPU)
1482               continue;
1483             if ((mask & (1u << (31 - inum))) != 0)
1484               {
1485                 uint sn = 0;
1486                 if (scu[scu_unit_idx].ports[port].is_exp)
1487                   {
1488                     sn = (uint) scu[scu_unit_idx].ports[port].xipmaskval;
1489                     if (sn >= N_SCU_SUBPORTS)
1490                       {
1491                         sim_warn ("XIP mask not set; defaulting to subport 0\n");
1492                         sn = 0;
1493                       }
1494                   }
1495                 if (! cables->scu_to_cpu[scu_unit_idx][port][sn].in_use)
1496                   {
1497                     sim_warn ("bad scu_unit_idx %u\n", scu_unit_idx);
1498                     continue;
1499                   }
1500                 uint cpu_unit_udx = cables->scu_to_cpu[scu_unit_idx][port][sn].cpu_unit_idx;
1501 # if defined(THREADZ) || defined(LOCKLESS)
1502                 // Need to do this before setting XIP to avoid race condition
1503                 // We are about to interrupt a CPU; this is done to either
1504                 //   Multics signalling, such has CAM cache clear
1505                 //   Adding a new CPU
1506                 //   Reading a deleted CPU
1507                 //   Starting an ISOLTS CPU
1508                 // If it is a Multics signal, the target CPU will be marked as 'inMultics';
1509                 // For the other cases, 'inMultics ' will not be set.
1510                 // cpup != NULL means that this interrupt was generated by a CPU, rather
1511                 // than an IOM; we need this to set the calling thread into slave mode.
1512                 if ((! sys_opts.nosync) && cpup && (! cpus[cpu_unit_udx].inMultics)) {
1513                   // The interrupt is to start or restart a CPU.
1514 #  ifdef SYNCTEST
1515                   sim_printf ("CPU %c becomes clock master\n", 'A' + cpu_unit_udx);
1516 #  endif
1517                   becomeClockMaster (cpu_unit_udx);
1518                 }
1519                 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1520 #  if defined(TESTING)
1521                 HDBGIntrSet (inum, cpu_unit_udx, scu_unit_idx, __func__);
1522 #  endif
1523 #  ifdef SYNCTEST
1524 if (cpus[cpu_unit_udx].rcfDelete) sim_printf ("Poking CPU %c in rcfDelete\n", 'A' + cpu_unit_udx);
1525 #  endif
1526                 createCPUThread((uint) cpu_unit_udx);
1527 #  if !defined(NO_TIMEWAIT)
1528                 wakeCPU ((uint) cpu_unit_udx);
1529 #  endif
1530                 sim_debug (DBG_DEBUG, & scu_dev,
1531                            "interrupt set for CPU %d SCU %d\n",
1532                            cpu_unit_udx, scu_unit_idx);
1533 # else // ! THREADZ
1534 //if (cpu_unit_udx && ! cpu.isRunning) sim_printf ("starting CPU %c\n", cpu_unit_udx + 'A');
1535 #  if defined(ROUND_ROBIN)
1536                 cpus[cpu_unit_udx].isRunning = true;
1537 #  endif
1538                 cpus[cpu_unit_udx].events.XIP[scu_unit_idx] = true;
1539 sim_debug (DBG_DEBUG, & scu_dev, "interrupt set for CPU %d SCU %d\n", cpu_unit_udx, scu_unit_idx);
1540                 sim_debug (DBG_INTR, & scu_dev,
1541                            "XIP set for SCU %d\n", scu_unit_idx);
1542 # endif // ! THREADZ
1543               }
1544           }
1545       }
1546 #endif // REORDER
1547   }
1548 
1549 t_stat scu_smic (cpu_state_t * cpup, uint scu_unit_idx, uint UNUSED cpu_unit_udx,
     /* [previous][next][first][last][top][bottom][index][help] */
1550                  uint UNUSED cpu_port_num, word36 rega)
1551   {
1552 #if defined(THREADZ) || defined(LOCKLESS)
1553     lock_scu ();
1554 #endif
1555 // smic can set cells but not reset them...
1556 
1557     if (getbits36_1 (rega, 35))
1558       {
1559         for (uint i = 0; i < 16; i ++)
1560           {
1561             if (getbits36_1 (rega, i))
1562               scu [scu_unit_idx].cells [i + 16] = 1;
1563           }
1564         char pcellb [N_CELL_INTERRUPTS + 1];
1565         sim_debug (DBG_TRACE, & scu_dev,
1566                    "SMIC low: Unit %u Cells: %s\n",
1567                    scu_unit_idx, pcells (scu_unit_idx, pcellb));
1568       }
1569     else
1570       {
1571         for (uint i = 0; i < 16; i ++)
1572           {
1573             if (getbits36_1 (rega, i))
1574               scu [scu_unit_idx].cells [i] = 1;
1575           }
1576         char pcellb [N_CELL_INTERRUPTS + 1];
1577         sim_debug (DBG_TRACE, & scu_dev,
1578                    "SMIC high: Unit %d Cells: %s\n",
1579                    scu_unit_idx, pcells (scu_unit_idx, pcellb));
1580       }
1581 
1582 
1583 
1584 
1585 
1586 
1587 
1588 
1589 
1590 
1591 
1592 
1593 
1594 
1595 
1596 
1597 
1598 
1599 
1600 
1601 
1602 
1603 
1604 
1605 
1606     dump_intr_regs ("smic", scu_unit_idx);
1607     deliver_interrupts (cpup, scu_unit_idx);
1608 #if defined(THREADZ) || defined(LOCKLESS)
1609     unlock_scu ();
1610 #endif
1611     return SCPE_OK;
1612   }
1613 
1614 // system controller and the function to be performed as follows:
1615 //
1616 //  Effective  Function
1617 //  Address
1618 //  y0000x     C(system controller mode register) -> C(AQ)
1619 //  y0001x     C(system controller configuration switches) -> C(AQ)
1620 //  y0002x     C(mask register assigned to port 0) -> C(AQ)
1621 //  y0012x     C(mask register assigned to port 1) -> C(AQ)
1622 //  y0022x     C(mask register assigned to port 2) -> C(AQ)
1623 //  y0032x     C(mask register assigned to port 3) -> C(AQ)
1624 //  y0042x     C(mask register assigned to port 4) -> C(AQ)
1625 //  y0052x     C(mask register assigned to port 5) -> C(AQ)
1626 //  y0062x     C(mask register assigned to port 6) -> C(AQ)
1627 //  y0072x     C(mask register assigned to port 7) -> C(AQ)
1628 //  y0003x     C(interrupt cells) -> C(AQ)
1629 //
1630 //  y0004x
1631 //    or       C(calendar clock) -> C(AQ)
1632 //  y0005x
1633 //
1634 //  y0006x
1635 //    or C(store unit mode register) -> C(AQ)
1636 //  y0007x
1637 //
1638 // where: y = value of C(TPR.CA)0,2 (C(TPR.CA)1,2 for the DPS 8M
1639 // processor) used to select the system controller
1640 // x = any octal digit
1641 //
1642 
1643 t_stat scu_sscr (cpu_state_t * cpup, uint scu_unit_idx, UNUSED uint cpu_unit_udx,
     /* [previous][next][first][last][top][bottom][index][help] */
1644                  UNUSED uint cpu_port_num, word18 addr,
1645                  word36 rega, word36 regq)
1646   {
1647     sim_debug (DBG_DEBUG, & scu_dev, "sscr SCU unit %o\n", scu_unit_idx);
1648 
1649     // Only valid for a 4MW SCU
1650 
1651     if (scu_unit_idx >= scu_dev.numunits)
1652       {
1653 // XXX should this be a store fault?
1654         sim_warn ("%s: scu_unit_idx out of range %d\n",
1655                    __func__, scu_unit_idx);
1656         return SCPE_OK;
1657       }
1658 
1659     // BCE uses clever addressing schemes to select SCUs; it appears we need
1660     // to be more selecting in picking out the function bits;
1661     //uint function = (addr >> 3) & 07777;
1662     uint function = (addr >> 3) & 07;
1663 
1664     // See scs.incl.pl1
1665 
1666     if (config_switches [scu_unit_idx].mode != MODE_PROGRAM)
1667       {
1668         sim_warn ("%s: SCU mode is 'MANUAL', not 'PROGRAM' -- sscr "
1669                    "not allowed to set switches.\n",
1670                    __func__);
1671 // XXX [CAC] Setting an unassigned register generates a STORE FAULT;
1672 // this probably should as well
1673         return SCPE_OK;
1674       }
1675 
1676 // Not used by 4MW
1677 
1678     switch (function)
1679       {
1680         case 00000: // Set system controller mode register
1681           {
1682 #if defined(THREADZ) || defined(LOCKLESS)
1683             lock_scu ();
1684 #endif
1685             scu [scu_unit_idx].id = (word4) getbits36_4 (regq, 50 - 36);
1686             scu [scu_unit_idx].mode_reg = getbits36_18 (regq, 54 - 36);
1687 #if defined(THREADZ) || defined(LOCKLESS)
1688             unlock_scu ();
1689 #endif
1690           }
1691           break;
1692 
1693         case 00001: // Set system controller configuration register
1694                     // (4MW SCU only)
1695           {
1696             sim_debug (DBG_DEBUG, & scu_dev,
1697                        "sscr 1 %d A: %012"PRIo64" Q: %012"PRIo64"\n",
1698                        scu_unit_idx, rega, regq);
1699 #if defined(THREADZ) || defined(LOCKLESS)
1700             lock_scu ();
1701 #endif
1702             scu_t * up = scu + scu_unit_idx;
1703             for (int maskab = 0; maskab < 2; maskab ++)
1704               {
1705                 word9 mask = ((maskab ? regq : rega) >> 27) & 0777;
1706                 if (mask & 01)
1707                   {
1708                     up -> mask_enable [maskab] = 0;
1709                     sim_debug (DBG_DEBUG, & scu_dev,
1710                                "sscr %u mask disable  %d\n",
1711                                scu_unit_idx, maskab);
1712                   }
1713                 else
1714                   {
1715                     up -> mask_enable [maskab] = 1;
1716                     sim_debug (DBG_DEBUG, & scu_dev,
1717                                "sscr %u mask enable  %d\n",
1718                                scu_unit_idx, maskab);
1719                     for (int pn = 0; pn < N_SCU_PORTS; pn ++)
1720                       {
1721                         if ((2 << (N_SCU_PORTS - 1 - pn)) & mask)
1722                           {
1723                             up -> mask_assignment [maskab] = (uint) pn;
1724                             break;
1725                           }
1726                       }
1727 
1728                   }
1729                 sim_debug (DBG_INTR, & scu_dev,
1730                            "SCU%u SSCR1 mask %c enable set to %u assigned to "
1731                            "port %u\n",
1732                            scu_unit_idx, 'a' + maskab, up->mask_enable[maskab],
1733                            up->mask_assignment[maskab]);
1734               }
1735             // AN87-00A, pg 2-5, 2-6 specify which fields are and are not
1736             //  settable.
1737 
1738             //if (up -> lower_store_size != ((rega >> 24) & 07))
1739               //sim_printf ("??? The CPU tried to change the SCU store size\n");
1740             up -> lower_store_size = (rega >> 24) & 07;
1741             up -> cyclic           = (regq >>  8) & 0177;
1742             up -> nea              = (rega >>  6) & 0377;
1743             up -> onl              = (rega >> 20) & 017;
1744             up -> interlace        = (rega >>  5) &  1;
1745             up -> lwr              = (rega >>  4) &  1;
1746             up -> port_enable [0]  = (rega >>  3) & 01;
1747             up -> port_enable [1]  = (rega >>  2) & 01;
1748             up -> port_enable [2]  = (rega >>  1) & 01;
1749             up -> port_enable [3]  = (rega >>  0) & 01;
1750             up -> port_enable [4]  = (regq >>  3) & 01;
1751             up -> port_enable [5]  = (regq >>  2) & 01;
1752             up -> port_enable [6]  = (regq >>  1) & 01;
1753             up -> port_enable [7]  = (regq >>  0) & 01;
1754 
1755 #if defined(THREADZ) || defined(LOCKLESS)
1756             unlock_scu ();
1757 #endif
1758             // XXX A, A1, B, B1, INT, LWR not implemented. (AG87-00A pgs 2-5,
1759             //  2-6)
1760             break;
1761           }
1762 
1763         case 00002: // Set mask register port 0
1764         //case 00012: // Set mask register port 1
1765         //case 00022: // Set mask register port 2
1766         //case 00032: // Set mask register port 3
1767         //case 00042: // Set mask register port 4
1768         //case 00052: // Set mask register port 5
1769         //case 00062: // Set mask register port 6
1770         //case 00072: // Set mask register port 7
1771           {
1772 #if defined(THREADZ) || defined(LOCKLESS)
1773             lock_scu ();
1774 #endif
1775             uint port_num = (addr >> 6) & 07;
1776             sim_debug (DBG_DEBUG, & scu_dev, "Set mask register port %d to "
1777                        "%012"PRIo64",%012"PRIo64"\n",
1778                        port_num, rega, regq);
1779 
1780             // Find mask reg assigned to specified port
1781             int mask_num = -1;
1782             uint n_masks_found = 0;
1783             for (int p = 0; p < N_ASSIGNMENTS; p ++)
1784               {
1785                 //if (scup -> interrupts [p].mask_assign.unassigned)
1786                 if (scu [scu_unit_idx].mask_enable [p] == 0)
1787                   continue;
1788                 //if (scup -> interrupts [p].mask_assign.port == port_num)
1789                 if (scu [scu_unit_idx ].mask_assignment [p] == port_num)
1790                   {
1791                     if (n_masks_found == 0)
1792                       mask_num = p;
1793                     n_masks_found ++;
1794                   }
1795               }
1796 
1797             if (! n_masks_found)
1798               {
1799 // According to bootload_tape_label.alm, this condition is OK
1800                 sim_debug (DBG_WARN, & scu_dev,
1801                            "%s: No masks assigned to cpu on port %d\n",
1802                            __func__, port_num);
1803 #if defined(THREADZ) || defined(LOCKLESS)
1804                 unlock_scu ();
1805 #endif
1806                 return SCPE_OK;
1807               }
1808 
1809             if (n_masks_found > 1)
1810               {
1811                 // Not legal for Multics
1812                 sim_debug (DBG_WARN, & scu_dev,
1813                            "%s: Multiple masks assigned to cpu on port %d\n",
1814                            __func__, port_num);
1815               }
1816 
1817             // See AN87
1818             //scup -> interrupts[mask_num].exec_intr_mask = 0;
1819             scu [scu_unit_idx].exec_intr_mask [mask_num] = 0;
1820             scu [scu_unit_idx].exec_intr_mask [mask_num] |=
1821               ((word32) getbits36_16(rega, 0) << 16);
1822             scu [scu_unit_idx].exec_intr_mask [mask_num] |=
1823               getbits36_16(regq, 0);
1824 
1825 
1826 
1827 
1828 
1829 
1830 
1831 
1832 
1833             sim_debug (DBG_TRACE, & scu_dev,
1834                        "SSCR Set mask unit %u port %u mask_num %u "
1835                        "mask 0x%08x\n",
1836                        scu_unit_idx, port_num, mask_num,
1837                        scu [scu_unit_idx].exec_intr_mask [mask_num]);
1838             dump_intr_regs ("sscr set mask", scu_unit_idx);
1839             scu [scu_unit_idx].mask_enable [mask_num] = 1;
1840             sim_debug (DBG_INTR, & scu_dev,
1841                        "SCU%u SSCR2 exec_intr mask %c set to 0x%08x"
1842                        " and enabled.\n",
1843                        scu_unit_idx, 'a' + mask_num,
1844                        scu[scu_unit_idx].exec_intr_mask[mask_num]);
1845 
1846             deliver_interrupts (cpup, scu_unit_idx);
1847 #if defined(THREADZ) || defined(LOCKLESS)
1848             unlock_scu ();
1849 #endif
1850           }
1851           break;
1852 
1853         case 00003: // Set interrupt cells
1854           {
1855 #if defined(THREADZ) || defined(LOCKLESS)
1856             lock_scu ();
1857 #endif
1858             for (uint i = 0; i < 16; i ++)
1859               {
1860                 scu [scu_unit_idx].cells [i] =
1861                   getbits36_1 (rega, i) ? 1 : 0;
1862                 scu [scu_unit_idx].cells [i + 16] =
1863                   getbits36_1 (regq, i) ? 1 : 0;
1864               }
1865             char pcellb [N_CELL_INTERRUPTS + 1];
1866             sim_debug (DBG_TRACE, & scu_dev,
1867                        "SSCR Set int. cells: Unit %u Cells: %s\n",
1868                        scu_unit_idx, pcells (scu_unit_idx, pcellb));
1869             sim_debug (DBG_INTR, & scu_dev,
1870                        "SCU%u SSCR3  Set int. cells %s\n",
1871                        scu_unit_idx, pcells (scu_unit_idx, pcellb));
1872             dump_intr_regs ("sscr set interrupt cells", scu_unit_idx);
1873             deliver_interrupts (NULL, scu_unit_idx);
1874 #if defined(THREADZ) || defined(LOCKLESS)
1875             unlock_scu ();
1876 #endif
1877           }
1878           break;
1879 
1880         case 00004: // Set calendar clock (4MW SCU only)
1881         case 00005:
1882           {
1883             // AQ: 20-35 clock bits 0-15, 36-71 clock bits 16-51
1884             word16 b0_15   = (word16) getbits36_16 (cpu.rA, 20);
1885             word36 b16_51  = cpu.rQ;
1886             uint64 new_clk = (((uint64) b0_15) << 36) | b16_51;
1887 #if defined(THREADZ) || defined(LOCKLESS)
1888             lock_scu ();
1889 #endif
1890             scu [scu_unit_idx].user_correction =
1891               (int64) (new_clk - set_SCU_clock (cpup, scu_unit_idx));
1892 #if defined(THREADZ) || defined(LOCKLESS)
1893             unlock_scu ();
1894 #endif
1895             //sim_printf ("sscr %o\n", function);
1896           }
1897           break;
1898 
1899         case 00006: // Set unit mode register
1900         case 00007:
1901           // XXX See notes in AL39 sscr re: store unit selection
1902           //sim_printf ("sscr %o\n", function);
1903           sim_warn ("sscr set unit mode register\n");
1904           //return STOP_UNIMP;
1905           return SCPE_OK;
1906 
1907         default:
1908           sim_warn ("sscr unhandled code\n");
1909           //return STOP_UNIMP;
1910           return SCPE_OK;
1911           //sim_printf ("sscr %o\n", function);
1912       }
1913     return SCPE_OK;
1914   }
1915 
1916 t_stat scu_rscr (cpu_state_t * cpup, uint scu_unit_idx, uint cpu_unit_udx, word18 addr,
     /* [previous][next][first][last][top][bottom][index][help] */
1917                  word36 * rega, word36 * regq)
1918   {
1919     // Only valid for a 4MW SCU
1920 
1921     if (scu_unit_idx >= scu_dev.numunits)
1922       {
1923         sim_warn ("%s: scu_unit_idx out of range %d\n",
1924                    __func__, scu_unit_idx);
1925         return SCPE_OK;
1926       }
1927 
1928     // BCE uses clever addressing schemes to select SCUs; it appears we need
1929     // to be more selecting in picking out the function bits;
1930     //uint function = (addr >> 3) & 07777;
1931     uint function = (addr >> 3) & 07;
1932 
1933     //sim_printf ("rscr %o\n", function);
1934 
1935     // See scs.incl.pl1
1936 
1937     switch (function)
1938       {
1939         case 00000: // Read system controller mode register
1940           {
1941             // AN-87
1942             // 0..0 -> A
1943             // 0..0 -> Q 36-49 (0-13)
1944             // ID -> Q 50-53 (14-17)
1945             // MODE REG -> Q 54-71 (18-35)
1946             //
1947             //  ID: 0000  8034, 8035
1948             //      0001  Level 68 SC
1949             //      0010  Level 66 SCU
1950             // CAC: According to scr.incl.pl1. 0010 is a 4MW SCU
1951             // MODE REG: these fields are only used by T&D
1952             * rega = 0;
1953             //* regq = 0000002000000; // ID = 0010
1954             * regq = 0;
1955 #if defined(THREADZ) || defined(LOCKLESS)
1956             lock_scu ();
1957 #endif
1958             putbits36_4 (regq, 50 - 36, scu [scu_unit_idx].id);
1959             putbits36_18 (regq, 54 - 36, scu [scu_unit_idx].mode_reg);
1960 #if defined(THREADZ) || defined(LOCKLESS)
1961             unlock_scu ();
1962 #endif
1963             break;
1964           }
1965 
1966         case 00001: // Read system controller configuration register
1967           {
1968             // AN-87, scr.incl.pl1
1969             //
1970             // SCU:
1971             // reg A:
1972             //   MASK A | SIZE | A | A1 | B | B1 | PORT | 0 | MOD | NEA |
1973             //   INT | LWR | PMR 0-3
1974             // reg Q:
1975             //   MASK B | not used | CYCLIC PRIOR | not used | PMR 4-7
1976             //
1977             //   MASK A/B (9 bits): EIMA switch setting for mask A/B. The
1978             //    assigned port corresponds to the but position within the
1979             //    field. A bit in position 9 indicates that the mask is
1980             //    not assigned.
1981             // From scr.incl.pl1:
1982             // 400 => assigned to port 0
1983             //  .
1984             //  .
1985             // 002 => assigned to port 7
1986             // 001 => mask off */
1987 
1988             //
1989             //  SIZE (3 bits): Size of lower store
1990             //    000 = 32K ... 111 = 4M
1991             //
1992             //  A A1 B B1 (1 bit): store unit A/A1/B/B1 online
1993             //
1994             //  PORT (4 bits): Port number of the SCU port through which
1995             //    the RSCR instruction was received
1996             //
1997             //struct config_switches * sw = config_switches + scu_unit_idx;
1998             sim_debug (DBG_DEBUG, & scu_dev, "rscr 1 %d\n", scu_unit_idx);
1999 #if defined(THREADZ) || defined(LOCKLESS)
2000             lock_scu ();
2001 #endif
2002             scu_t * up = scu + scu_unit_idx;
2003             word9 maskab [2];
2004             for (int i = 0; i < 2; i ++)
2005               {
2006                 if (up -> mask_enable [i])
2007                   {
2008                     maskab [i] = (2 << (N_SCU_PORTS - 1 -
2009                                         up -> mask_assignment [i])) & 0777;
2010                   }
2011                 else
2012                   maskab [i] = 0001;
2013               }
2014 
2015             int scu_port_num = -1; // The port that the rscr instruction was
2016                                    // received on
2017 
2018             for (int pn = 0; pn < N_SCU_PORTS; pn ++)
2019               {
2020                 for (int sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2021                   {
2022                     if (cables->scu_to_cpu[scu_unit_idx][pn][sn].in_use &&
2023                         cables->scu_to_cpu[scu_unit_idx][pn][sn].cpu_unit_idx ==
2024                           cpu_unit_udx)
2025                      {
2026                         scu_port_num = pn;
2027                         goto gotit;
2028                       }
2029                   }
2030               }
2031 gotit:;
2032             if (scu_port_num < 0)
2033               {
2034 #if defined(THREADZ) || defined(LOCKLESS)
2035                 unlock_scu ();
2036 #endif
2037                 sim_warn ("%s: can't find cpu port in the snarl of cables; "
2038                            "scu_unit_no %d, cpu_unit_udx %d\n",
2039                            __func__, scu_unit_idx, cpu_unit_udx);
2040                 return SCPE_OK;
2041               }
2042 
2043             // AN87, pg 2-5
2044             word36 a, q;
2045 
2046             a = 0;
2047 // (data, starting bit position, number of bits, value)
2048             putbits36_9 (& a,  0,  maskab [0]);
2049             putbits36_3 (& a,  9,  (word3) up -> lower_store_size);
2050             putbits36_4 (& a, 12,  (word4) up -> onl); // A, A1, B, B1 online
2051             putbits36_4 (& a, 16,  (word4) scu_port_num);
2052             putbits36_1 (& a, 21,  (word1) config_switches[scu_unit_idx].mode);
2053             putbits36_8 (& a, 22,  (word8) up -> nea);
2054             putbits36_1 (& a, 30,  (word1) up -> interlace);
2055             putbits36_1 (& a, 31,  (word1) up -> lwr);
2056             // XXX INT, LWR not implemented. (AG87-00A pgs 2-5. 2-6)
2057             // interlace <- 0
2058             // lower <- 0
2059             // Looking at scr_util.list, I *think* the port order
2060             // 0,1,2,3.
2061             putbits36_1 (& a, 32,  (word1) up -> port_enable [0]);
2062             putbits36_1 (& a, 33,  (word1) up -> port_enable [1]);
2063             putbits36_1 (& a, 34,  (word1) up -> port_enable [2]);
2064             putbits36_1 (& a, 35,  (word1) up -> port_enable [3]);
2065             * rega = a;
2066 
2067             q = 0;
2068             putbits36_9 (& q,  0,  maskab [1]);
2069             // cyclic prior <- 0
2070             putbits36_7 (& q, 57-36, (word7) up -> cyclic & MASK7);
2071             // Looking at scr_util.list, I *think* the port order
2072             // 0,1,2,3.
2073             putbits36_1 (& q, 32,  (word1) up -> port_enable [4]);
2074             putbits36_1 (& q, 33,  (word1) up -> port_enable [5]);
2075             putbits36_1 (& q, 34,  (word1) up -> port_enable [6]);
2076             putbits36_1 (& q, 35,  (word1) up -> port_enable [7]);
2077             * regq = q;
2078 
2079 #if defined(THREADZ) || defined(LOCKLESS)
2080             unlock_scu ();
2081 #endif
2082             sim_debug (DBG_DEBUG, & scu_dev,
2083                        "rscr 1 %d A: %012"PRIo64" Q: %012"PRIo64"\n",
2084                        scu_unit_idx, * rega, * regq);
2085             break;
2086           }
2087 
2088         case 00002: // mask register
2089           {
2090             uint port_num = (addr >> 6) & MASK3;
2091 #if defined(THREADZ) || defined(LOCKLESS)
2092             lock_scu ();
2093 #endif
2094             scu_t * up = scu + scu_unit_idx;
2095             uint mask_contents = 0;
2096             if (up -> mask_assignment [0] == port_num)
2097               {
2098                 mask_contents = up -> exec_intr_mask [0];
2099               }
2100             else if (up -> mask_assignment [1] == port_num)
2101               {
2102                 mask_contents = up -> exec_intr_mask [1];
2103               }
2104             mask_contents &= MASK32;
2105 
2106             * rega = 0;
2107             putbits36 (rega,  0, 16, (mask_contents >> 16) & MASK16);
2108             putbits36 (rega, 32,  1, up -> port_enable [0]);
2109             putbits36 (rega, 33,  1, up -> port_enable [1]);
2110             putbits36 (rega, 34,  1, up -> port_enable [2]);
2111             putbits36 (rega, 35,  1, up -> port_enable [3]);
2112 
2113             * regq = 0;
2114             putbits36 (rega,  0, 16, (mask_contents >>  0) & MASK16);
2115             putbits36 (regq, 32,  1, up -> port_enable [4]);
2116             putbits36 (regq, 33,  1, up -> port_enable [5]);
2117             putbits36 (regq, 34,  1, up -> port_enable [6]);
2118             putbits36 (regq, 35,  1, up -> port_enable [7]);
2119 
2120 #if defined(THREADZ) || defined(LOCKLESS)
2121             unlock_scu ();
2122 #endif
2123             sim_debug (DBG_TRACE, & scu_dev,
2124                        "RSCR mask unit %u port %u assigns %u %u mask 0x%08x\n",
2125                        scu_unit_idx, port_num, up -> mask_assignment [0],
2126                        up -> mask_assignment [1],
2127                        mask_contents);
2128           }
2129           break;
2130 
2131         case 00003: // Interrupt cells
2132           {
2133 #if defined(THREADZ) || defined(LOCKLESS)
2134             lock_scu ();
2135 #endif
2136             scu_t * up = scu + scu_unit_idx;
2137             // * rega = up -> exec_intr_mask [0];
2138             // * regq = up -> exec_intr_mask [1];
2139             for (uint i = 0; i < N_CELL_INTERRUPTS; i ++)
2140               {
2141                 word1 cell = up -> cells [i] ? 1 : 0;
2142                 if (i < 16)
2143                   putbits36_1 (rega, i, cell);
2144                 else
2145                   putbits36_1 (regq, i - 16, cell);
2146               }
2147 #if defined(THREADZ) || defined(LOCKLESS)
2148             unlock_scu ();
2149 #endif
2150           }
2151           break;
2152 
2153         case 00004: // Get calendar clock (4MW SCU only)
2154         case 00005:
2155           {
2156             uint64 clk = set_SCU_clock (cpup, scu_unit_idx);
2157             cpu.rQ =  clk  & 0777777777777;    // lower 36-bits of clock
2158             cpu.rA = (clk >> 36) & 0177777;    // upper 16-bits of clock
2159 #if defined(TESTING)
2160             HDBGRegAW ("rscr get clock");
2161             HDBGRegQW ("rscr get clock");
2162 #endif
2163           }
2164         break;
2165 
2166         case 00006: // SU Mode register
2167         case 00007: // SU Mode register
2168           {
2169             //sim_printf ("rscr SU Mode Register%o\n", function);
2170 
2171 // Completely undocumented...
2172 //   scr.incl.alm
2173 //"         Structure scr_su
2174 //"
2175 //          equ       scr_su_size,2
2176 //
2177 //
2178 //          equ       scr_su.ZAC_line_word,1
2179 //          equ       scr_su.ZAC_line_shift,30
2180 //          bool      scr_su.ZAC_line_mask,000077
2181 //          equ       scr_su.syndrome_word,1
2182 //          equ       scr_su.syndrome_shift,22
2183 //          bool      scr_su.syndrome_mask,000377
2184 //          equ       scr_su.identification_word,1
2185 //          equ       scr_su.identification_shift,18
2186 //          bool      scr_su.identification_mask,000017
2187 //          equ       scr_su.EDAC_disabled_word,1
2188 //          bool      scr_su.EDAC_disabled,400000   " DL
2189 //          equ       scr_su.MINUS_5_VOLT_margin_word,1
2190 //"         equ       scr_su.MINUS_5_VOLT_margin_shift,11
2191 //          bool      scr_su.MINUS_5_VOLT_margin_mask,000003
2192 //          equ       scr_su.PLUS_5_VOLT_margin_word,1
2193 //          equ       scr_su.PLUS_5_VOLT_margin_shift,9
2194 //          bool      scr_su.PLUS_5_VOLT_margin_mask,000003
2195 //          equ       scr_su.spare_margin_word,1
2196 //          equ       scr_su.spare_margin_shift,7
2197 //          bool      scr_su.spare_margin_mask,000003
2198 //          equ       scr_su.PLUS_19_VOLT_margin_word,1
2199 //"         equ       scr_su.PLUS_19_VOLT_margin_shift,5
2200 //          bool      scr_su.PLUS_19_VOLT_margin_mask,000003
2201 //          equ       scr_su.SENSE_strobe_margin_word,1
2202 //"         equ       scr_su.SENSE_strobe_margin_shift,2
2203 //          bool      scr_su.SENSE_strobe_margin_mask,000003
2204 //"         equ       scr_su.maint_functions_enabled_word,1
2205 //          bool      scr_su.maint_functions_enabled,000001 " DL
2206 
2207 //                 1   1      1   2    2    2       2     3   3        3  3
2208 //   0     6       4   8      9   3    5    7       9     1   2        4  5
2209 //  ------------------------------------------------------------------------------
2210 //  | ZAC | synd | id | EDAC | 0 | -5 | +5 | spare | +19 | 0 | sense | 0 | maint |
2211 //  ------------------------------------------------------------------------------
2212 //       6      8    4      1   4    2    2      2      2   1       2   1       1
2213 
2214 // Okay, it looks safe to return 0.
2215 
2216             * rega = 0;
2217             * regq = 0;
2218           }
2219           break;
2220 
2221         default:
2222           sim_warn ("rscr %o\n", function);
2223           return SCPE_OK;
2224       }
2225     return SCPE_OK;
2226   }
2227 
2228 
2229 
2230 
2231 
2232 int scu_cioc (uint cpu_unit_udx, uint scu_unit_idx, uint scu_port_num,
     /* [previous][next][first][last][top][bottom][index][help] */
2233               uint expander_command, uint sub_mask)
2234   {
2235 
2236 
2237 
2238 #if defined(TESTING)
2239     cpu_state_t * cpup = _cpup;
2240     sim_debug (DBG_DEBUG, & scu_dev,
2241                "scu_cioc: Connect from %o sent to "
2242                "unit %o port %o exp %o mask %03o\n",
2243                cpu_unit_udx, scu_unit_idx, scu_port_num,
2244               expander_command, sub_mask);
2245 #endif
2246 #if defined(THREADZ) || defined(LOCKLESS)
2247     lock_scu ();
2248 #endif
2249     struct ports * portp = & scu [scu_unit_idx].ports [scu_port_num];
2250 
2251     int rc = 0;
2252     if (! scu [scu_unit_idx].port_enable [scu_port_num])
2253       {
2254         sim_debug (DBG_ERR, & scu_dev,
2255                    "scu_cioc: Connect sent to disabled port; dropping\n");
2256         sim_debug (DBG_ERR, & scu_dev,
2257                    "scu_cioc: scu_unit_idx %u scu_port_num %u\n",
2258                    scu_unit_idx, scu_port_num);
2259         rc = 1;
2260         goto done;
2261       }
2262 
2263     if (expander_command == 1) // "set subport enables"
2264       {
2265         for (uint i = 0; i < N_SCU_SUBPORTS; i++)
2266           {
2267             portp->subport_enables [i] = !! (sub_mask & (0200u >> i));
2268           }
2269         goto done;
2270       }
2271 
2272     if (expander_command == 2) // "set xipmask"
2273       {
2274         int cnt = 0;
2275         int val = -1;
2276         for (uint i = 0; i < N_SCU_SUBPORTS; i++)
2277           {
2278             portp->xipmask [i] = !! (sub_mask & (0200u >> i));
2279             if (portp->xipmask [i])
2280               {
2281                 val = (int) i;
2282                 cnt ++;
2283               }
2284           }
2285         if (cnt > 1)
2286           {
2287             sim_warn ("xip mask cnt > 1\n");
2288             val = -1;
2289           }
2290         portp->xipmaskval = val;
2291         goto done;
2292       }
2293 
2294     if (portp -> type == ADEV_IOM)
2295       {
2296         int iom_unit_idx = portp->dev_idx;
2297 #if defined(THREADZ) || defined(LOCKLESS)
2298         unlock_scu ();
2299 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
2300         lock_iom ();
2301         lock_libuv ();
2302 # endif
2303         iom_interrupt (scu_unit_idx, (uint) iom_unit_idx);
2304 # if !defined(IO_ASYNC_PAYLOAD_CHAN) && !defined(IO_ASYNC_PAYLOAD_CHAN_THREAD)
2305         unlock_libuv ();
2306         unlock_iom ();
2307 # endif
2308         return 0;
2309 #else // ! THREADZ
2310         if (sys_opts.iom_times.connect <= 0)
2311           {
2312             iom_interrupt (scu_unit_idx, (uint) iom_unit_idx);
2313             goto done;
2314           }
2315         else
2316           {
2317 //sim_printf ("scu_cioc: Queuing an IOM in %d cycles "
2318 //"(for the connect channel) %u %d\n",
2319 //sys_opts.iom_times.connect, scu_unit_idx, iom_unit_idx);
2320             sim_debug (DBG_INFO, & scu_dev,
2321                        "scu_cioc: Queuing an IOM in %d cycles "
2322                        "(for the connect channel)\n",
2323                        sys_opts.iom_times.connect);
2324             // Stash the iom_interrupt call parameters
2325             iom_dev.units[iom_unit_idx].u3 = (int32) scu_unit_idx;
2326             iom_dev.units[iom_unit_idx].u4 = (int32) iom_unit_idx;
2327             int rc;
2328             if ((rc = sim_activate (& iom_dev.units [iom_unit_idx],
2329                 sys_opts.iom_times.connect)) != SCPE_OK)
2330               {
2331                 sim_warn ("sim_activate failed (%d)\n", rc);
2332                 goto done;
2333               }
2334             goto done;
2335           }
2336 #endif // ! THREADZ
2337       }
2338     else if (portp -> type == ADEV_CPU)
2339       {
2340 
2341 // by subport_enables
2342         if (portp->is_exp)
2343           {
2344             for (uint sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2345               {
2346                 if (portp->subport_enables[sn])
2347                   {
2348                     if (! cables->
2349                             scu_to_cpu[scu_unit_idx][scu_port_num][sn].in_use)
2350                       {
2351                         sim_warn ("Can't find CPU to interrupt\n");
2352                         continue;
2353                       }
2354                     uint cpu_unit_udx = cables->
2355                       scu_to_cpu[scu_unit_idx][scu_port_num][sn].cpu_unit_idx;
2356                     setG7fault ((uint) cpu_unit_udx, FAULT_CON);
2357                   }
2358               }
2359           }
2360         else
2361           {
2362             if (! cables->scu_to_cpu[scu_unit_idx][scu_port_num][0].in_use)
2363               {
2364                 sim_warn ("Can't find CPU to interrupt\n");
2365                 rc = 1;
2366                 goto done;
2367               }
2368             uint cpu_unit_udx =
2369               cables->scu_to_cpu[scu_unit_idx][scu_port_num][0].cpu_unit_idx;
2370             setG7fault ((uint) cpu_unit_udx, FAULT_CON);
2371           }
2372 
2373 
2374 
2375 
2376 
2377 
2378 
2379 
2380 
2381 
2382 
2383 
2384 
2385 
2386 
2387 
2388 
2389 
2390 
2391 
2392 
2393 
2394         goto done;
2395       }
2396     else
2397       {
2398         sim_debug (DBG_ERR, & scu_dev,
2399                    "scu_cioc: Connect sent to not-an-IOM or CPU; dropping\n");
2400         rc = 1;
2401         goto done;
2402       }
2403 done:
2404 #if defined(THREADZ) || defined(LOCKLESS)
2405     unlock_scu ();
2406 #endif
2407     return rc;
2408 }
2409 
2410 // =============================================================================
2411 
2412 // The SXC (set execute cells) SCU command.
2413 
2414 // From AN70:
2415 //  It then generates a word with
2416 // the <interrupt number>th bit set and sends this to the bootload
2417 // SCU with the SC (set execute cells) SCU command.
2418 //
2419 
2420 int scu_set_interrupt (uint scu_unit_idx, uint inum)
     /* [previous][next][first][last][top][bottom][index][help] */
2421   {
2422 #if defined(TESTING)
2423     cpu_state_t * cpup = _cpup;
2424 #endif
2425     const char* moi = "SCU::interrupt";
2426 
2427     if (inum >= N_CELL_INTERRUPTS)
2428       {
2429         sim_debug (DBG_WARN, & scu_dev,
2430                    "%s: Bad interrupt number %d\n", moi, inum);
2431         return 1;
2432       }
2433 
2434 #if defined(THREADZ) || defined(LOCKLESS)
2435     lock_scu ();
2436 #endif
2437     scu [scu_unit_idx].cells [inum] = 1;
2438     dump_intr_regs ("scu_set_interrupt", scu_unit_idx);
2439     deliver_interrupts (NULL, scu_unit_idx);
2440 #if defined(THREADZ) || defined(LOCKLESS)
2441     unlock_scu ();
2442 #endif
2443     return 0;
2444 }
2445 
2446 // Scan a SCU for interrupts from highest to lowest. If an interrupt is
2447 // present, clear it, update the interrupt state bits and return the fault
2448 // pair address for the interrupt (2 * interrupt number). If no interrupt
2449 // is present, return 1.
2450 //
2451 
2452 uint scu_get_highest_intr (uint scu_unit_idx)
     /* [previous][next][first][last][top][bottom][index][help] */
2453   {
2454 #if defined(TESTING)
2455     cpu_state_t * cpup = _cpup;
2456 #endif
2457 #if defined(THREADZ) || defined(LOCKLESS)
2458     lock_scu ();
2459 #endif
2460     // lower numbered cells have higher priority
2461     for (int inum = 0; inum < N_CELL_INTERRUPTS; inum ++)
2462       {
2463         for (uint pima = 0; pima < N_ASSIGNMENTS; pima ++) // A, B
2464           {
2465             if (scu [scu_unit_idx].mask_enable [pima] == 0)
2466               continue;
2467             uint mask = scu [scu_unit_idx].exec_intr_mask [pima];
2468             uint port = scu [scu_unit_idx].mask_assignment [pima];
2469 //            if (scu [scu_unit_idx].ports [port].type != ADEV_CPU ||
2470 //              scu [scu_unit_idx].ports [port].dev_idx != current_running_cpu_idx)
2471             if (scu[scu_unit_idx].ports[port].type != ADEV_CPU ||
2472                 cpus[current_running_cpu_idx].scu_port[scu_unit_idx] != port)
2473               continue;
2474             if (scu [scu_unit_idx].cells [inum] &&
2475                 (mask & (1u << (31 - inum))) != 0)
2476               {
2477                 sim_debug (DBG_TRACE, & scu_dev,
2478                            "scu_get_highest_intr inum %d pima %u mask 0%011o port %u cells 0%011o\n",
2479                            inum, pima, mask, port, scu [scu_unit_idx].cells [inum]);
2480                 scu [scu_unit_idx].cells [inum] = false;
2481                 dump_intr_regs ("scu_get_highest_intr", scu_unit_idx);
2482                 deliver_interrupts (NULL, scu_unit_idx);
2483 #if defined(THREADZ) || defined(LOCKLESS)
2484                 unlock_scu ();
2485 #endif
2486                 return (uint) inum * 2;
2487               }
2488           }
2489       }
2490 #if defined(THREADZ) || defined(LOCKLESS)
2491     unlock_scu ();
2492 #endif
2493     return 1;
2494   }
2495 
2496 t_stat scu_reset_unit (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
2497                        UNUSED const char * cptr,
2498                        UNUSED void * desc)
2499   {
2500     uint scu_unit_idx = (uint) (uptr - scu_unit);
2501     scu_unit_reset ((int) scu_unit_idx);
2502     return SCPE_OK;
2503   }
2504 
2505 void scu_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2506   {
2507     // One time only initializations
2508 
2509     for (int u = 0; u < N_SCU_UNITS_MAX; u ++)
2510       {
2511         for (int p = 0; p < N_SCU_PORTS; p ++)
2512           {
2513             for (int s = 0; s < N_SCU_SUBPORTS; s ++)
2514               {
2515                 scu[u].ports[p].dev_port[s]        = -1;
2516                 scu[u].ports[p].subport_enables[s] = false;
2517                 scu[u].ports[p].xipmask[s]         = false;
2518                 // Invalid value for detecting uninitialized XIP mask.
2519                 scu[u].ports[p].xipmaskval         = N_SCU_SUBPORTS;
2520               }
2521             scu[u].ports[p].type   = ADEV_NONE;
2522             scu[u].ports[p].is_exp = false;
2523           }
2524 
2525         //  ID: 0000  8034, 8035
2526         //      0001  Level 68 SC
2527         //      0010  Level 66 SCU
2528         scu [u].id           = 02l; // 0b0010
2529         scu [u].mode_reg     = 0;   // used by T&D
2530         scu [u].elapsed_days = 0;
2531       }
2532 
2533   }
2534 
2535 t_stat scu_rmcm (uint scu_unit_idx, uint cpu_unit_udx, word36 * rega,
     /* [previous][next][first][last][top][bottom][index][help] */
2536                  word36 * regq)
2537   {
2538 #if defined(TESTING)
2539     cpu_state_t * cpup = _cpup;
2540 #endif
2541     scu_t * up = scu + scu_unit_idx;
2542 
2543     // Assume no mask register assigned
2544     * rega = 0;
2545     * regq = 0;
2546 
2547     // Which port is cpu_unit_udx connected to? (i.e. which port did the
2548     // command come in on?
2549     int scu_port_num = -1; // The port that the rscr instruction was
2550                            // received on
2551 
2552     for (int pn = 0; pn < N_SCU_PORTS; pn ++)
2553       {
2554         for (int sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2555           {
2556             if (cables->scu_to_cpu[scu_unit_idx][pn][sn].in_use &&
2557                 cables->scu_to_cpu[scu_unit_idx][pn][sn].cpu_unit_idx ==
2558                   cpu_unit_udx)
2559               {
2560                 scu_port_num = pn;
2561                 goto gotit;
2562               }
2563           }
2564       }
2565 
2566 gotit:;
2567 
2568     //sim_printf ("rmcm scu_port_num %d\n", scu_port_num);
2569 
2570     if (scu_port_num < 0)
2571       {
2572         sim_warn ("%s: can't find cpu port in the snarl of cables; "
2573                   "scu_unit_no %d, cpu_unit_udx %d\n",
2574                   __func__, scu_unit_idx, cpu_unit_udx);
2575         sim_debug (DBG_ERR, & scu_dev,
2576                    "%s: can't find cpu port in the snarl of cables; "
2577                    "scu_unit_no %d, cpu_unit_udx %d\n",
2578                    __func__, scu_unit_idx, cpu_unit_udx);
2579         // Non 4MWs do a store fault
2580         return SCPE_OK;
2581       }
2582 
2583     // A reg:
2584     //  0          15  16           31  32       35
2585     //    IER 0-15        00000000        PER 0-3
2586     // Q reg:
2587     //  0          15  16           31  32       35
2588     //    IER 16-32       00000000        PER 4-7
2589 
2590     sim_debug (DBG_TRACE, & scu_dev, "rmcm selected scu port %u\n",
2591                scu_port_num);
2592 #if defined(THREADZ) || defined(LOCKLESS)
2593     lock_scu ();
2594 #endif
2595     uint mask_contents = 0;
2596     if (up -> mask_assignment [0] == (uint) scu_port_num)
2597       {
2598         mask_contents = up -> exec_intr_mask [0];
2599         sim_debug (DBG_TRACE, & scu_dev, "rmcm got mask %011o from pima A\n",
2600                    mask_contents);
2601       }
2602     else if (up -> mask_assignment [1] == (uint) scu_port_num)
2603       {
2604         mask_contents = up -> exec_intr_mask [1];
2605         sim_debug (DBG_TRACE, & scu_dev, "rmcm got mask %011o from pima B\n",
2606                    mask_contents);
2607       }
2608     mask_contents &= MASK32;
2609 
2610     * rega = 0;  //-V1048
2611     putbits36_16 (rega,  0, (mask_contents >> 16) & MASK16);
2612     putbits36_1  (rega, 32,  (word1) up -> port_enable [0]);
2613     putbits36_1  (rega, 33,  (word1) up -> port_enable [1]);
2614     putbits36_1  (rega, 34,  (word1) up -> port_enable [2]);
2615     putbits36_1  (rega, 35,  (word1) up -> port_enable [3]);
2616 
2617     * regq = 0;  //-V1048
2618     putbits36_16 (regq,  0, (mask_contents >>  0) & MASK16);
2619     putbits36_1  (regq, 32,  (word1) up -> port_enable [4]);
2620     putbits36_1  (regq, 33,  (word1) up -> port_enable [5]);
2621     putbits36_1  (regq, 34,  (word1) up -> port_enable [6]);
2622     putbits36_1  (regq, 35,  (word1) up -> port_enable [7]);
2623 
2624 #if defined(THREADZ) || defined(LOCKLESS)
2625     unlock_scu ();
2626 #endif
2627     sim_debug (DBG_TRACE, & scu_dev,
2628                "RMCM returns %012"PRIo64" %012"PRIo64"\n",
2629                * rega, * regq);
2630     dump_intr_regs ("rmcm", scu_unit_idx);
2631     return SCPE_OK;
2632   }
2633 
2634 t_stat scu_smcm (uint scu_unit_idx, uint cpu_unit_udx, word36 rega, word36 regq)
     /* [previous][next][first][last][top][bottom][index][help] */
2635   {
2636 #if defined(TESTING)
2637     cpu_state_t * cpup = _cpup;
2638 #endif
2639     sim_debug (DBG_TRACE, & scu_dev,
2640               "SMCM SCU unit %d CPU unit %d A %012"PRIo64" Q %012"PRIo64"\n",
2641                scu_unit_idx, cpu_unit_udx, rega, regq);
2642 
2643     scu_t * up = scu + scu_unit_idx;
2644 
2645     // Which port is cpu_unit_udx connected to? (i.e. which port did the
2646     // command come in on?
2647     int scu_port_num = -1; // The port that the rscr instruction was
2648                            // received on
2649 
2650     for (int pn = 0; pn < N_SCU_PORTS; pn ++)
2651       {
2652         for (int sn = 0; sn < N_SCU_SUBPORTS; sn ++)
2653           {
2654             if (cables->scu_to_cpu[scu_unit_idx][pn][sn].in_use &&
2655                 cables->scu_to_cpu[scu_unit_idx][pn][sn].cpu_unit_idx ==
2656                   cpu_unit_udx)
2657               {
2658                 scu_port_num = pn;
2659                 goto gotit;
2660               }
2661           }
2662       }
2663 gotit:;
2664 
2665     //sim_printf ("rmcm scu_port_num %d\n", scu_port_num);
2666 
2667     if (scu_port_num < 0)
2668       {
2669         sim_warn ("%s: can't find cpu port in the snarl of cables; "
2670                    "scu_unit_no %d, cpu_unit_udx %d\n",
2671                    __func__, scu_unit_idx, cpu_unit_udx);
2672         return SCPE_OK;
2673       }
2674 
2675     sim_debug (DBG_TRACE, & scu_dev, "SMCM SCU port num %d\n", scu_port_num);
2676 
2677     // A reg:
2678     //  0          15  16           31  32       35
2679     //    IER 0-15        00000000        PER 0-3
2680     // Q reg:
2681     //  0          15  16           31  32       35
2682     //    IER 16-32       00000000        PER 4-7
2683 
2684     uint imask =
2685       ((uint) getbits36_16(rega, 0) << 16) |
2686       ((uint) getbits36_16(regq, 0) <<  0);
2687 #if defined(THREADZ) || defined(LOCKLESS)
2688     lock_scu ();
2689 #endif
2690     if (up -> mask_assignment [0] == (uint) scu_port_num)
2691       {
2692         up -> exec_intr_mask [0] = imask;
2693         sim_debug (DBG_TRACE, & scu_dev, "SMCM intr mask 0 set to %011o\n",
2694                    imask);
2695       }
2696     else if (up -> mask_assignment [1] == (uint) scu_port_num)
2697       {
2698         up -> exec_intr_mask [1] = imask;
2699         sim_debug (DBG_TRACE, & scu_dev, "SMCM intr mask 1 set to %011o\n",
2700                    imask);
2701       }
2702 
2703     scu [scu_unit_idx].port_enable [0] = (uint) getbits36_1 (rega, 32);
2704     scu [scu_unit_idx].port_enable [1] = (uint) getbits36_1 (rega, 33);
2705     scu [scu_unit_idx].port_enable [2] = (uint) getbits36_1 (rega, 34);
2706     scu [scu_unit_idx].port_enable [3] = (uint) getbits36_1 (rega, 35);
2707     scu [scu_unit_idx].port_enable [4] = (uint) getbits36_1 (regq, 32);
2708     scu [scu_unit_idx].port_enable [5] = (uint) getbits36_1 (regq, 33);
2709     scu [scu_unit_idx].port_enable [6] = (uint) getbits36_1 (regq, 34);
2710     scu [scu_unit_idx].port_enable [7] = (uint) getbits36_1 (regq, 35);
2711 
2712     dump_intr_regs ("smcm", scu_unit_idx);
2713     deliver_interrupts (NULL, scu_unit_idx);
2714 #if defined(THREADZ) || defined(LOCKLESS)
2715     unlock_scu ();
2716 #endif
2717 
2718     return SCPE_OK;
2719   }

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