/* hp2100_cpu.c: HP 21xx/1000 Central Processing Unit/MEM/MP/DCPC simulator

   Copyright (c) 1993-2016, Robert M. Supnik
   Copyright (c) 2017,      J. David Bryan

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
   AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the names of the authors shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from the authors.

   CPU          2114C/2115A/2116C/2100A/1000-M/E/F Central Processing Unit
                12731A Memory Expansion Module
   DMA1,DMA2    12607B/12578A/12895A Direct Memory Access
   DCPC1,DCPC2  12897B Dual Channel Port Controller
   MP           12581A/12892B Memory Protect

   11-Aug-17    JDB     MEM must be disabled when DMS is disabled
   01-Aug-17    JDB     Changed SET/SHOW CPU [NO]IDLE to use sim_*_idle routines
   22-Jul-17    JDB     Renamed "intaddr" to CIR; added IR
   18-Jul-17    JDB     Added CPU stops
   11-Jul-17    JDB     Moved "hp_enbdis_pair" to hp2100_sys.c
                        Renamed "ibl_copy" to "cpu_ibl"
   10-Jul-17    JDB     Renamed the global routine "iogrp" to "cpu_iog"
   07-Jul-17    JDB     Changed "iotrap" from uint32 to t_bool
   26-Jun-17    JDB     Moved I/O instruction subopcode constants from hp2100_defs.h
   16-May-17    JDB     Changed REG_A, REG_B to REG_X
   19-Apr-17    JDB     SET CPU IDLE now omits idle loop tracing
   04-Apr-17    JDB     Added "cpu_configuration" for symbolic ex/dep validation
                        Rejected model change no longer changes options
   21-Mar-17    JDB     IOP is now illegal on the 1000 F-Series
   27-Feb-17    JDB     Added BBL load for 21xx machines
                        ibl_copy no longer returns a status code
   22-Feb-17    JDB     Added DMA tracing
   21-Feb-17    JDB     Added bus tracing to the I/O dispatcher
   19-Jan-17    JDB     Added CPU tracing
                        Consolidated the memory read and write routines
   05-Aug-16    JDB     Renamed the P register from "PC" to "PR"
   13-May-16    JDB     Modified for revised SCP API function parameter types
   31-Dec-14    JDB     Corrected devdisp data parameters
   30-Dec-14    JDB     Added S-register parameters to ibl_copy
   24-Dec-14    JDB     Added casts for explicit downward conversions
   18-Mar-13    JDB     Removed redundant extern declarations
   05-Feb-13    JDB     HLT instruction handler now relies on sim_vm_fprint_stopped
   09-May-12    JDB     Separated assignments from conditional expressions
   13-Jan-12    JDB     Minor speedup in "is_mapped"
                        Added casts to cpu_mod, dmasio, dmapio, cpu_reset, dma_reset
   07-Apr-11    JDB     Fixed I/O return status bug for DMA cycles
                        Failed I/O cycles now stop on failing instruction
   28-Mar-11    JDB     Tidied up signal handling
   29-Oct-10    JDB     Revised DMA for new multi-card paradigm
                        Consolidated DMA reset routines
                        DMA channels renamed from 0,1 to 1,2 to match documentation
   27-Oct-10    JDB     Changed I/O instructions, handlers, and DMA for revised signal model
                        Changed I/O dispatch table to use DIB pointers
   19-Oct-10    JDB     Removed DMA latency counter
   13-Oct-10    JDB     Fixed DMA requests to enable stealing every cycle
                        Fixed DMA priority for channel 1 over channel 2
                        Corrected comments for "cpu_set_idle"
   30-Sep-08    JDB     Breakpoints on interrupt trap cells now work
   05-Sep-08    JDB     VIS and IOP are now mutually exclusive on 1000-F
   11-Aug-08    JDB     Removed A/B shadow register variables
   07-Aug-08    JDB     Moved hp_setdev, hp_showdev to hp2100_sys.c
                        Moved non-existent memory checks to WritePW
   05-Aug-08    JDB     Fixed mp_dms_jmp to accept lower bound, check write protection
   30-Jul-08    JDB     Corrected DMS violation register set conditions
                        Refefined ABORT to pass address, moved def to hp2100_cpu.h
                        Combined dms and dms_io routines
   29-Jul-08    JDB     JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort
   11-Jul-08    JDB     Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA
   26-Jun-08    JDB     Rewrote device I/O to model backplane signals
                        EDT no longer passes DMA channel
   30-Apr-08    JDB     Enabled SIGNAL instructions, SIG debug flag
   28-Apr-08    JDB     Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE
   24-Apr-08    JDB     Fixed single stepping through interrupts
   20-Apr-08    JDB     Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags
   03-Dec-07    JDB     Memory ex/dep and bkpt type default to current map mode
   26-Nov-07    JDB     Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA
   15-Nov-07    JDB     Corrected MP W5 (JSB) jumper action, SET/SHOW reversal,
                        mp_mevff clear on interrupt with I/O instruction in trap cell
   04-Nov-07    JDB     Removed DBI support from 1000-M (was temporary for RTE-6/VM)
   28-Apr-07    RMS     Removed clock initialization
   02-Mar-07    JDB     EDT passes input flag and DMA channel in dat parameter
   11-Jan-07    JDB     Added 12578A DMA byte packing
   28-Dec-06    JDB     CLC 0 now sends CRS instead of CLC to devices
   26-Dec-06    JDB     Fixed improper IRQ deferral for 21xx CPUs
                        Fixed improper interrupt servicing in resolve
   21-Dec-06    JDB     Added 21xx loader enable/disable support
   16-Dec-06    JDB     Added 2114 and 2115 CPU options.
                        Added support for 12607B (2114) and 12578A (2115/6) DMA
   01-Dec-06    JDB     Added 1000-F CPU option (requires HAVE_INT64)
                        SHOW CPU displays 1000-M/E instead of 21MX-M/E
   16-Oct-06    JDB     Moved ReadF to hp2100_cpu1.c
   12-Oct-06    JDB     Fixed INDMAX off-by-one error in resolve
   26-Sep-06    JDB     Added iotrap parameter to UIG dispatchers for RTE microcode
   12-Sep-06    JDB     iogrp returns NOTE_IOG to recalc interrupts
                        resolve returns NOTE_INDINT to service held-off interrupt
   16-Aug-06    JDB     Added support for future microcode options, future F-Series
   09-Aug-06    JDB     Added double integer microcode, 1000-M/E synonyms
                        Enhanced CPU option validity checking
                        Added DCPC as a synonym for DMA for 21MX simulations
   26-Dec-05    JDB     Improved reporting in dev_conflict
   22-Sep-05    RMS     Fixed declarations (from Sterling Garwood)
   21-Jan-05    JDB     Reorganized CPU option flags
   15-Jan-05    RMS     Split out EAU and MAC instructions
   26-Dec-04    RMS     DMA reset doesn't clear alternate CTL flop (from Dave Bryan)
                        DMA reset shouldn't clear control words (from Dave Bryan)
                        Alternate CTL flop not visible as register (from Dave Bryan)
                        Fixed CBS, SBS, TBS to perform virtual reads
                        Separated A/B from M[0/1] for DMA IO (from Dave Bryan)
                        Fixed bug in JPY (from Dave Bryan)
   25-Dec-04    JDB     Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E)
                        TIMER/EXECUTE/DIAG instructions disabled for 21MX-M
                        T-register reflects changes in M-register when halted
   25-Sep-04    JDB     Moved MP into its own device; added MP option jumpers
                        Modified DMA to allow disabling
                        Modified SET CPU 2100/2116 to truncate memory > 32K
                        Added -F switch to SET CPU to force memory truncation
                        Fixed S-register behavior on 2116
                        Fixed LIx/MIx behavior for DMA on 2116 and 2100
                        Fixed LIx/MIx behavior for empty I/O card slots
                        Modified WRU to be REG_HRO
                        Added BRK and DEL to save console settings
                        Fixed use of "unsigned int16" in cpu_reset
                        Modified memory size routine to return SCPE_INCOMP if
                        memory size truncation declined
   20-Jul-04    RMS     Fixed bug in breakpoint test (reported by Dave Bryan)
                        Back up PC on instruction errors (from Dave Bryan)
   14-May-04    RMS     Fixed bugs and added features from Dave Bryan
                        - SBT increments B after store
                        - DMS console map must check dms_enb
                        - SFS x,C and SFC x,C work
                        - MP violation clears automatically on interrupt
                        - SFS/SFC 5 is not gated by protection enabled
                        - DMS enable does not disable mem prot checks
                        - DMS status inconsistent at simulator halt
                        - Examine/deposit are checking wrong addresses
                        - Physical addresses are 20b not 15b
                        - Revised DMS to use memory rather than internal format
                        - Added instruction printout to HALT message
                        - Added M and T internal registers
                        - Added N, S, and U breakpoints
                        Revised IBL facility to conform to microcode
                        Added DMA EDT I/O pseudo-opcode
                        Separated DMA SRQ (service request) from FLG
   12-Mar-03    RMS     Added logical name support
   02-Feb-03    RMS     Fixed last cycle bug in DMA output (found by Mike Gemeny)
   22-Nov-02    RMS     Added 21MX IOP support
   24-Oct-02    RMS     Fixed bugs in IOP and extended instructions
                        Fixed bugs in memory protection and DMS
                        Added clock calibration
   25-Sep-02    RMS     Fixed bug in DMS decode (found by Robert Alan Byer)
   26-Jul-02    RMS     Restructured extended instructions, added IOP support
   22-Mar-02    RMS     Changed to allocate memory array dynamically
   11-Mar-02    RMS     Cleaned up setjmp/auto variable interaction
   17-Feb-02    RMS     Added DMS support
                        Fixed bugs in extended instructions
   03-Feb-02    RMS     Added terminal multiplexor support
                        Changed PCQ macro to use unmodified PC
                        Fixed flop restore logic (found by Bill McDermith)
                        Fixed SZx,SLx,RSS bug (found by Bill McDermith)
                        Added floating point support
   16-Jan-02    RMS     Added additional device support
   07-Jan-02    RMS     Fixed DMA register tables (found by Bill McDermith)
   07-Dec-01    RMS     Revised to use breakpoint package
   03-Dec-01    RMS     Added extended SET/SHOW support
   10-Aug-01    RMS     Removed register in declarations
   26-Nov-00    RMS     Fixed bug in dual device number routine
   21-Nov-00    RMS     Fixed bug in reset routine
   15-Oct-00    RMS     Added dynamic device number support

   References:
     - 2100A Computer Reference Manual
         (02100-90001, Dec-1971)
     - Model 2100A Computer Installation and Maintenance Manual
         (02100-90002, Aug-1972)
     - HP 1000 M/E/F-Series Computers Technical Reference Handbook
         (5955-0282, Mar-1980)
     - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
         (92851-90001, Mar-1981)
     - HP 1000 M/E/F-Series Computers I/O Interfacing Guide
         (02109-90006, Sep-1980)
     - 12607A Direct Memory Access Operating and Service Manual
         (12607-90002, Jan-1970)
     - 12578A/12578A-01 Direct Memory Access Operating and Service Manual
         (12578-9001, Mar-1972)
     - 12892B Memory Protect Installation Manual
         (12892-90007, Jun-1978)
     - HP 1000 Computer Real-Time Systems
         (5091-4479, August 1992)


   Hewlett-Packard sold the HP 21xx/1000 family of real-time computers from 1966
   through 2000.  There are three major divisions within this family: the 21xx
   core-memory machines, the 1000 (originally 21MX) M/E/F-Series semiconductor-
   memory machines, and the 1000 L/A-Series machines.  All machines are 16-bit
   accumulator-oriented CISC machines running the same base instruction set.  A
   wide range of operating systems run on these machines, from a simple 4K word
   paper-tape-based monitor to a megaword multi-user, multiprogramming disc-
   based system and a multi-user time-shared BASIC system.

   This implementation is a simulator for the 2114, 2115, 2116, 2100, and 1000
   M/E/F-Series machines.  A large variety of CPU options, device interface
   cards, and peripherals are provided.  High-speed I/O transfers are performed
   by Direct Memory Access and Dual-Channel Port Controller options.  This
   simulator does not model the 1000 L/A-Series machines.

   All of the machines support a 15-bit logical address space, addressing a
   maximum of 32 K words, divided into 1K-word pages.  Memory-referencing
   instructions in the base set can directly address the 1024 words of the base
   page (page 0) or the 1024 words of the current page (the page containing the
   instruction).  The instructions in the extended set directly address the
   32768 words in the full logical address space.  The A and B accumulators may
   be addressed as logical addresses 0 and 1, respectively.

   Peripheral devices are connected to the CPU by interface cards installed in
   the I/O card cages present in the CPU and optional I/O extender chassis. Each
   slot in the card cage is assigned an address, called a select code, that may
   be referenced by I/O instructions in the base set.  Select codes range from 0
   to 77 octal, with the first eight select codes reserved for the system,
   providing connections for 56 possible interfaces.

   The 211x machines use a hardwired processor providing 70 basic instructions
   and up to 32K of core memory.  The base instruction set is divided into the
   Memory Reference Group, the Shift-Rotate Group, the Alter-Skip Group, and the
   I/O Group.  SRG instruction words may contain from one to four suboperation
   codes that are executed from left-to-right, and ASG instruction words may
   contain from one to eight suboperations.  An optional Extended Arithmetic
   Unit may be added to the 2115 and 2116 that provides hardware multiply and
   divide, double-load and -store, and double-word shift and rotate
   instructions.

   The 2100 machine uses a microprogrammed processor that provides the 80
   instructions of the base set and the EAU as standard equipment.  Optional
   floating-point microcode adds six two-word single-precision instructions.
   User microprogramming is also supported.  When used as part of an HP 2000
   Time-Shared BASIC system, the CPU designated as the I/O processor may be
   equipped with microcode implementing 18 additional OS accelerator
   instructions.

   The 1000 M/E-Series machines also use microprogrammed processors and extend
   the 2100 instruction set with two new index registers, X and Y, and a new
   Extended Instruction Group consisting of 32 index-register instructions and
   10 word-and-byte-manipulation instructions.  The six 2100 floating-point
   instructions are also standard.  The 1000 F-Series adds a hardware
   floating-point processor with 18 new triple- and quad-word instructions.  A
   number of new optional microcode extensions are available with the
   M/E/F-Series.

   1000 CPUs offer the optional Dynamic Mapping System, which provides memory
   mapping on a page-by-page basis.  The 5-bit page number of a logical memory
   address selects one of 32 ten-bit map registers containing physical page
   numbers.  The ten-bit page number combined with the ten-bit page offset
   yields a 20-bit physical address capable of accessing a location in a
   one-megaword memory.  DMS provides separate maps for system and user
   programs, as well as for the two DCPC channels, and includes microcode that
   implements the 38 Dynamic Mapping Instructions used to manipulate the mapping
   system.

   Optional memory protection is accomplished by dividing the logical address
   space into protected and unprotected parts.  When protection is enabled, any
   attempt to write below the fence separating the two parts is inhibited, and
   an interrupt to the operating system occurs, which aborts the offending user
   program.  If the DMS option is enabled as well, protection is enhanced by
   specifying read and write permissions on a page-by-page basis.

   A note on terminology: the 1000 series of computers was originally called the
   21MX at introduction.  The 21MX (occasionally, 21MXM) corresponds to the 1000
   M-Series, and the 21MXE (occasionally, 21XE) corresponds to the 1000
   E-Series.  The model numbers were changed before the introduction of the 1000
   F-Series, although some internal HP documentation refers to this machine as
   the 21MXF.

   The terms MEM (Memory Expansion Module), MEU (Memory Expansion Unit), DMI
   (Dynamic Mapping Instructions), and DMS (Dynamic Mapping System) are used
   somewhat interchangeably to refer to the logical-to-physical memory address
   translation option provided on the 1000-Series.  DMS consists of the MEM card
   (12731A) and the DMI firmware (13307A).  However, MEM and MEU have been used
   interchangeably to refer to the mapping card, as have DMI and DMS to refer to
   the firmware instructions.


   These CPU hardware registers are present in all machines:

     Name  Width  Description
     ----  -----  ----------------------------------------------
      A     16    accumulator (addressable as memory location 0)
      B     16    accumulator (addressable as memory location 1)
      P     15    program counter
      S     16    switch and display register
      M     15    memory address register
      T     16    memory data register
      E      1    extend flag (carry out)
      O      1    overflow flag

   The 1000 Series adds these CPU hardware registers:

     Name  Width  Description
     ----  -----  ----------------------------------------------
      X     16    index register
      Y     16    index register

   The data types supported by the base instruction set are:

     - 8-bit unsigned byte
     - 16-bit unsigned integer
     - 16-bit two's-complement integer
     - 32-bit two's-complement integer
     - 32-bit two's-complement floating point

   Multi-word values are stored in memory with the most-significant words in the
   lowest addresses.  Bytes are stored in memory with the most-significant byte
   in the upper half of the 16-bit word and the least-significant byte in the
   lower half.

   The instruction set is fairly irregular -- a legacy of its original
   implementation in hardware in the 2116 and the accretion of microprogrammed
   instructions in the 2100 and 1000 CPUs.  Initially, there were five base-set
   instruction groups:

     1. Memory-Reference Group (MRG)
     2. Shift-Rotate Group (SRG)
     3. Alter-Skip Group (ASG)
     4. I/O Group (IOG)
     5. Macroinstruction Group (MAC)

   All of the instructions added after the 2116 are in the Macroinstruction
   Group.

   The 2116 offered two hardware options that extended the instruction set.  The
   first is the 12579A Extended Arithmetic Unit.  The second is the 2152A
   Floating Point Processor, which is interfaced through, and therefore
   requires, the EAU.  The EAU adds 10 instructions including integer multiply
   and divide and double-word loads, stores, shifts, and rotates.  The FPP adds
   30 floating-point arithmetic, trigonometric, logarithmic, and exponential
   instructions.  (The 2116 FFP is not simulated.)

   The base set groups are decoded from bits 15-12 and 10, as follows:

     15  14-12  10  Group  Address Ranges
     --  -----  --  -----  -------------------------------
      x   nnn    x   MRG   010000-077777 and 110000-177777
      0   000    0   SRG   000000-001777 and 004000-005777
      0   000    1   ASG   002000-003777 and 006000-007777
      1   000    1   IOG   102000-103777 and 106000-107777
      1   000    0   MAC   100000-101777 and 104000-105777

   Where:

     x = don't care
     n = any combination other than all zeros

   The MAC group is subdivided into the Extended Arithmetic Group (EAG) and the
   User Instruction Group (UIG), based on bits 11, 9, and 8, as follows:

     11   9   8  Group  Address Range
     --  --  --  -----  -------------
      0   0   0  EAG    100000-100377
      0   0   1  EAG    100400-100777
      0   1   0  EAG    101000-101377
      0   1   1  UIG-1  101400-101777
      1   0   0  EAG    104000-104377
      1   0   1  EAG    104400-104777
      1   1   0  UIG-0  105000-105377
      1   1   1  UIG-1  105400-105777

   All of the 2116 FPP instructions are in the UIG sets: 3 use 10144x opcodes
   and the rest use 1050xx and 1054xx opcodes.  The 2100 decodes only UIG-0
   instructions, whereas the 1000s use both UIG sets.  In particular, the
   105740-105777 range is used by the 1000 Extended Instruction Group (EIG),
   which is part of the 1000-Series base set.

   The 21xx and 1000 M/E/F-Series machines do not trap unimplemented
   instructions.  In general, unimplemented EAG instructions cause erroneous
   execution, and unimplemented UIG instructions execute as NOP.  However, there
   are machine-to-machine variations, and some unimplemented instructions
   execute as other, defined instructions.

   The instruction set groups are encoded as follows:

      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | I |    mem op     | P |            memory address             |  MRG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | I |  mem op   | R | P |            memory address             |  MRG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     I = direct/indirect (0/1)
     R = A/B register (0/1)
     P = base/current page (0/1)

   The "mem ops" are encoded as follows:

     14-11  Mnemonic  Action
     -----  --------  ----------------------------------------
     0010     AND     A = A & M [MA]
     0011     JSB     M [MA] = P, P = MA + 1
     0100     XOR     A = A ^ M [MA]
     0101     JMP     P = MA
     0110     IOR     A = A | M [MA]
     0111     ISZ     M [MA] = M [MA] + 1, skip if M [MA] == 0
     1000     ADA     A = A + M [MA]
     1001     ADB     B = B + M [MA]
     1010     CPA     skip if A != M [MA]
     1011     CPB     skip if B != M [MA]
     1100     LDA     A = M [MA]
     1101     LDB     B = M [MA]
     1110     STA     M [MA] = A
     1111     STB     M [MA] = B

   Bits 15 and 10 encode the type of access, as follows:

     15,10  Access Type            Action
     -----  ---------------------  --------------------------
      0,0   base page direct       MA = I <9:0>
      0,1   current page direct    MA = P <14:0>'I <9:0>
      1,0   base page indirect     MA = M [I <9:0>]
      1,1   current page indirect  MA = M [P <14:10>'I <9:0>]


      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | 0   0   0 | R | 0 | E |   op 1    | C | E | S |   op 2    |  SRG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     R = A/B register (0/1)
     E = disable/enable op
     C = CLE
     S = SL*


      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | 0   0   0 | R | 1 | r op  | e op  | E | S | L | I | Z | V |  ASG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     R = A/B register (0/1)
     E = SEZ
     S = SS*
     L = SL*
     I = IN*
     Z = SZ*
     V = RSS


      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 1 | 0   0   0 | R | 1 | H |  I/O op   |      select code      |  IOG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     R = A/B register (0/1)
     H = hold/clear flag (0/1)

   An I/O group instruction controls the device specified by the select code.
   Depending on the opcode, the instruction may set or clear the device flag,
   start or stop I/O, or read or write data.


      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 1 | 0   0   0 |   | 0 |    eau op     | 0   0   0   0   0   0 |  EAU
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 1 | 0   0   0 |   | 0 | eau shift/rotate op   |  shift count  |  EAU
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   MAC ops decode when bits 15-12 and 10 are 1 000 0.  Bits 11 and 9-0 determine
   the specific EAU instruction.


      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 1 | 0   0   0 | R | 0   1 |      module       |   operation   |  UIG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     R = A/B register (0/1)


   In simulation, I/O devices are modelled by substituting software states for
   I/O backplane signals.  The set of signals generated by I/O instructions and
   DMA cycles is dispatched to the target device for action.  Backplane signals
   are processed sequentially.  For example, the "STC sc,C" instruction
   generates the "set control" and the "clear flag" signals that are processed
   in that order.

   CPU interrupt signals are modelled as three parallel arrays:

     - device request priority as bit vector dev_prl [2] [31..0]
     - device interrupt requests as bit vector dev_irq [2] [31..0]
     - device service requests as bit vector dev_srq [2] [31..0]

   Each array forms a 64-bit vector, with bits 0-31 of the first element
   corresponding to select codes 00-37 octal, and bits 0-31 of the second
   element corresponding to select codes 40-77 octal.

   The HP 21xx/1000 interrupt structure is based on the PRH, PRL, IRQ, and IAK
   signals.  PRH indicates that no higher-priority device is interrupting. PRL
   indicates to lower-priority devices that a given device is not interrupting.
   IRQ indicates that a given device is requesting an interrupt.  IAK indicates
   that the given device's interrupt request is being acknowledged.

   PRH and PRL form a hardware priority chain that extends from interface to
   interface on the backplane.  We model just PRL, as PRH is calculated from the
   PRLs of higher-priority devices.

   Typical I/O devices have a flag, flag buffer, and control flip-flops.  If a
   device's flag, flag buffer, and control bits are set, and the device is the
   highest priority on the interrupt chain, it requests an interrupt by
   asserting IRQ.  When the interrupt is acknowledged with IAK, the flag buffer
   is cleared, preventing further interrupt requests from that device. The
   combination of flag and control set blocks interrupts from lower priority
   devices.

   Service requests are used to trigger the DMA service logic.  Setting the
   device flag typically also sets SRQ, although SRQ may be calculated
   independently.


   The simulator provides three stop conditions related to instruction execution
   that may be enabled with a SET CPU STOP=<stop> command:

     <stop>  Action
     ------  ------------------------------------------
     UNIMPL  stop on an unimplemented instruction
     UNDEF   stop on an undefined instruction
     UNSC    stop on an access to an unused select code
     IOERR   stop on an unreported I/O error

   If an enabled stop condition is detected, execution ceases with the
   instruction pending, and control returns to the SCP prompt.  When simulation
   stops, execution may be resumed in two ways.  If the cause of the stop has
   not been remedied and the stop has not been disabled, resuming execution with
   CONTINUE, STEP, GO, or RUN will cause the stop to occur again.  Alternately,
   specifying the "-B" switch with any of the preceding commands will resume
   execution while bypassing the stop for the current instruction.

   The UNIMPL option stops the simulator if execution is attempted of an
   instruction provided by a firmware option that is not currently installed
   (e.g., a DAD instruction when the double-integer firmware is not installed)
   or of an opcode provided by an installed option but not assigned to an
   instruction (e.g., opcode 105335 from the double-integer firmware).
   Bypassing the stop will execute the instruction as a NOP (no-operation).

   The UNDEF option stops the simulator if execution is attempted of an
   instruction containing a decoded reserved bit pattern other than that defined
   in the Operating and Reference manual for the CPU.  For example, opcodes
   101700 and 105700 are not listed as DMS instructions, but they execute as
   XMM instructions, rather than as NOP.  The intent of this stop is to catch
   instructions containing reserved fields with values that change the meaning
   of those instructions.  Bypassing the stop will decode and execute the
   instruction in the same manner as the selected CPU.

   The UNSC option stops the simulator if an I/O instruction addresses a select
   code that is not assigned to an enabled device (equivalent to an empty
   hardware I/O backplane slot).  Bypassing the stop will read the floating
   S-bus or I/O-bus for LIA/B and MIA/B instructions or do nothing for all other
   instructions.

   The IOERR option stops the simulator if an I/O error condition exists for a
   device that does not report this status to the CPU.  For example, the paper
   tape reader device (PTR) does not report "no tape loaded" status, and the
   processor interconnect device (IPL) does not report "cable disconnected."  In
   both cases, I/O to the device will simply hang with no indication of the
   problem.  Enabling the IOERR option will stop the simulator with an error
   indication for these devices.

   In addition, a simulation stop will occur if an indirect addressing chain
   exceeds the maximum length specified by a SET CPU INDIR=<limit> command.
   Memory addresses may be indirect to indicate that the values point to the
   target addresses rather than contain the target addresses.  The target of an
   indirect address may itself be indirect, and the CPU follows this chain of
   addresses until it finds a direct address.  Indirect addressing is typically
   only one or two levels deep, but if the chain loops back on itself (e.g., if
   an indirect address points at itself), then instruction execution will hang.

   The limit may be set to any number of levels up to 32,768.  This is the
   absolute maximum number of levels that can be created without an infinite
   loop -- each location in memory points to the next one except for the last,
   which contains the target value.  In practice, anything over a few levels
   likely represents a programming error.  The default setting is 16 levels.


   In addition to the CPU, this module simulates the 12578A/12607B/12895A Direct
   Memory Access and 12897B Dual-Channel Port Controller devices (hereafter,
   "DMA").  These controllers permit the CPU to transfer data directly between
   an I/O device and memory on a cycle-stealing basis.  Depending on the CPU,
   the device interface, and main memory speed, DMA is capable of transferring
   data blocks from 1 to 32,768 words in length at rates between 500,000 and
   1,000,000 words per second.  The 2114 supports a single DMA channel.  All
   other CPUs support two DMA channels.

   DMA is programmed by setting three control words via two select codes: 2 and
   6 for channel 1, and 3 and 7 for channel 2.  During simultaneous transfers,
   channel 1 has priority over channel 2.  Otherwise, the channels are
   identical. Channel programming involves setting three control words, as
   follows:

   SC 06 Control Word 1 format:

      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | S | B | C | -   -   -   -   -   -  -  |  device select code   |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     S = assert STC during each cycle
     B = enable byte packing and unpacking (12578A only)
     C = assert CLC at the end of the block transfer

   SC 02 Control Word 2/3 format:

      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | D |                  starting memory address                  | word 2
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |                      negative word count                      | word 3
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     D = transfer direction is out of/into memory (0/1)

   Control word 2 is stored if the control flip-flop of select code 2 is clear,
   i.e., if the OTA/B is preceded by CLC; control word 3 is stored if the
   flip-flop is set by a preceding STC.

   The 12607B supports 14-bit addresses and 13-bit word counts.  The 12578A
   supports 15-bit addresses and 14-bit word counts.  The 12895A and 12897B
   support 15-bit addresses and 16-bit word counts.

   DMA is started by setting the control flip-flop on select code 6.  DMA
   completion is indicated when the flag flip-flop sets on select code 8, which
   causes an interrupt if enabled.


   This module also simulates the 12581A/12892B Memory Protect devices for the
   2116 and 1000 M/E/F-Series, respectively, and the memory protect feature that
   is standard equipment for the 2100.  MP is addressed via select code 5 and
   provides a fence register that holds the address of the start of unprotected
   memory and a violation register that holds the address of the instruction
   that has caused a memory protect interrupt, as follows:

      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 |          starting address of unprotected memory           | fence
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 |               violating instruction address               | violation
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   After setting the fence regiater with an OTA 5 or OTB 5 instruction, MP is
   enabled by an STC 5.


   This module also simulates the 12731A Memory Expansion Module for the 1000
   M/E/F-Series machines.  The MEM provides mapping of the 32 1024-word logical
   memory pages into a one-megaword physical memory.  Four separate maps are
   provided: system, user, DCPC port A, and DCPC port B.  The MEM is controlled
   by the associated Dynamic Mapping System instructions and contains status and
   violation registers, as follows:

   MEM Status Register:

      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | I | M | E | U | P | B |        base page fence address        |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     I = MEM disabled/enabled (0/1) at last interrupt
     M = System/user map (0/1) selected at last interrupt
     E = MEM disabled/enabled (0/1) currently
     U = System/user map (0/1) selected currently
     P = Protected mode disabled/enabled (0/1) currently
     B = Base-page portion mapped (0/1 = above/below the fence)

   MEM Violation Register:

      15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | R | W | B | P | -   -   -   - | S | E | M |    map address    |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     R = Read violation
     W = Write violation
     B = Base-page violation
     P = Privileged instruction violation
     S = ME bus disabled/enabled (0/1) at violation
     E = MEM disabled/enabled (0/1) at violation
     M = System/user map (0/1) selected at violation


   The CPU simulator provides extensive tracing capabilities that may be enabled
   with the SET DEBUG <filename> and SET CPU DEBUG=<trace> commands.  The trace
   options that may be specified are:

     Trace  Action
     -----  -------------------------------------------
     INSTR  trace instructions executed
     DATA   trace memory data accesses
     FETCH  trace memory instruction fetches
     REG    trace registers
     OPND   trace instruction operands
     EXEC   trace matching instruction execution states

   A section of an example trace is:

     >>CPU instr: S 0002 05735  103101  CLO
     >>CPU fetch: S 0002 05736  000036    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177777, B 000000, X 177777, Y 000000, e o i
     >>CPU instr: S 0002 05736  000036  SLA,ELA
     >>CPU fetch: S 0002 05737  102101    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177776, B 000000, X 177777, Y 000000, E o i
     >>CPU instr: S 0002 05737  102101  STO
     >>CPU fetch: S 0002 05740  002400    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177776, B 000000, X 177777, Y 000000, E O i
     >>CPU instr: S 0002 05755  102100  STF 0
     >>CPU fetch: S 0002 05756  102705    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177777, B 177777, X 177777, Y 000000, E O I
     >>CPU instr: S 0002 05756  102705  STC 5
     >>CPU fetch: S 0002 05757  105736    instruction fetch
     >>CPU   reg: P **** 01011  042200    A 177777, B 177777, X 177777, Y 000000, E O I
     >>CPU instr: S 0002 05757  105736  UJP 2111
     >>CPU fetch: S 0002 05760  002111    instruction fetch
     >>CPU fetch: U 0001 02111  026111    instruction fetch
     >>CPU   reg: P **** 01011  042200    A 177777, B 177777, X 177777, Y 000000, E O I
     >>CPU instr: U 0001 02111  026111  JMP 2111
     >>CPU instr: U 0001 02111  000011  interrupt
     >>CPU fetch: S 0000 00011  115013    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177777, B 177777, X 177777, Y 000000, E O I
     >>CPU   reg: - **** *****  ******    MPF 000000, MPV 002111, MES 163011, MEV 030000
     >>CPU instr: S 0000 00011  115013  JSB 1013,I
     >>CPU  data: S 0000 01013  005557    data read
     >>CPU  data: S 0002 05557  002111    data write
     >>CPU fetch: S 0002 05560  103100    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177777, B 177777, X 177777, Y 000000, E O I
     >>CPU instr: S 0002 05560  103100  CLF 0
     >>CPU fetch: S 0002 05561  105714    instruction fetch
     >>CPU   reg: - **** 01011  042200    A 177777, B 177777, X 177777, Y 000000, E O i
     >>CPU  exec: ********************
     >>CPU   reg: P **** 01567  000000    A 100036, B 000100, X 000100, Y 074000, E o I
     >>CPU instr: U 0220 07063  105240  .PMAP
     >>CPU  data: U 0000 01776  000227    unprotected read
     >>CPU  data: U 0227 76100  000233    data read
     >>CPU  opnd: * **** 07065  105240    return location is P+2 (no error)
     >>CPU fetch: U 0220 07065  127055    instruction fetch
     >>CPU   reg: P **** 01567  000000    A 100037, B 000101, X 000100, Y 074000, e o I

   The INSTR option traces instruction executions and interrupts.  Each
   instruction is printed in symbolic form before it is executed.

   The DATA option traces reads from and writes to memory.  Each access is
   classified by its usage type as "data" (using the current or alternate map
   with access protection) or "unprotected" (using a specified map without
   protection).

   The FETCH option traces instruction fetches from memory.  Reads of the
   additional words in a multiword instruction, such as the target address of a
   DLD (double load) instruction, are also classified as fetches.

   The REG option traces register values.  Two sets of registers are printed.
   After executing each instruction, the working registers (A, B, E, O, S, and,
   for 1000 CPUs, X and Y) and the state of the interrupt system (on or off) are
   printed.  After executing an instruction that may alter the Memory Protect or
   Memory Expansion Module state, the MP fence and violation registers, the MEM
   status and violation registers, and the current protection state are printed.

   The OPND option traces operand values.  Some instructions that take memory
   and register operands that are difficult to decode from DATA or REG traces
   present the operand values in a higher-level format.  The operand data and
   value presented are specific to the instruction; see the instruction executor
   comments for details.

   The EXEC option traces the execution of instructions that match
   user-specified criteria.  When a match occurs, all CPU trace options are
   turned on for the duration of the execution of the matched instruction.  The
   prior trace settings are restored when a match fails.  This option allows
   detailed tracing of specified instructions while minimizing the log file size
   compared to a full instruction trace.

   The various trace formats are interpreted as follows:

     >>CPU instr: U 0045 10341  016200  LDA 11200
                  ~ ~~~~ ~~~~~  ~~~~~~  ~~~~~~~~~
                  |   |    |       |       |
                  |   |    |       |       +-- instruction mnemonic
                  |   |    |       +---------- octal data (instruction opcode)
                  |   |    +------------------ octal logical address (P register)
                  |   +----------------------- octal physical page number
                  +--------------------------- memory map (S/U/- system/user/disabled)

     >>CPU instr: U 0045 10341  000011  interrupt
                  ~ ~~~~ ~~~~~  ~~~~~~  ~~~~~~~~~
                  |   |    |       |       |
                  |   |    |       |       +-- interrupt classification
                  |   |    |       +---------- octal device number (CIR register)
                  |   |    +------------------ octal logical address at interrupt (P register)
                  |   +----------------------- octal physical page number at interrupt
                  +--------------------------- memory map (S/U/- system/user/disabled)

     >>CPU fetch: - 0000 10341  016200    instruction fetch
     >>CPU  data: U 0013 01200  123003    data read
     >>CPU  data: S 0013 01200  017200    unprotected write
                  ~ ~~~~ ~~~~~  ~~~~~~    ~~~~~~~~~~~~~~~~~
                  |   |    |       |         |
                  |   |    |       |         +-- memory access classification
                  |   |    |       +------------ octal data (memory contents)
                  |   |    +-------------------- octal logical address (effective address)
                  |   +------------------------- octal physical page number
                  +----------------------------- memory map (S/U/A/B/- system/user/port A/port B/disabled)

     >>CPU   reg: P .... 01535  040013    A 123003, B 001340, X 000000, Y 000000, e O I
                  ~ ~~~~ ~~~~~  ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                  |   |    |       |         |
                  |   |    |       |         +-- A, B, X, Y, E, O, interrupt system registers
                  |   |    |       |             (lower/upper case = 0/1 or off/on)
                  |   |    |       +------------ S register
                  |   |    +-------------------- MEM fence
                  |   +-------------------------
                  +----------------------------- protection state (P/- protected/unprotected)

     >>CPU   reg: P .... .....  ......    MPF 00000, MPV 000000, MES 000000, MEV 000000
                  ~ ~~~~ ~~~~~  ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                  |   |    |       |         |
                  |   |    |       |         +-- memory protect fence and violation registers
                  |   |    |       |             memory expansion status and violation registers
                  |   |    |       +------------
                  |   |    +--------------------
                  |   +-------------------------
                  +----------------------------- protection state (P/- protected/unprotected)



     >>CPU  opnd: . .... 36002  101475    return location is P+3 (error EM21)
     >>CPU  opnd: . .... 22067  105355    entry is for a dynamic mapping violation
                         ~~~~~  ~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                           |       |         |
                           |       |         +-- operand-specific value
                           |       +------------ operand-specific octal data
                           +-------------------- octal logical address (P register)


   Implementation notes:

    1. The simulator is fast enough, compared to the run-time of the longest
       instructions, for interruptibility not to matter.  However, the HP
       diagnostics explicitly test interruptibility in the EIS and DMS
       instructions and in long indirect address chains.  Accordingly, the
       simulator does "just enough" to pass these tests.  In particular, if an
       interrupt is pending but deferred at the beginning of an interruptible
       instruction, the interrupt is taken at the appropriate point; but there
       is no testing for new interrupts during execution (that is, the event
       timer is not called).
*/



#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu1.h"



/* External data */

extern DIB clk_dib;                                     /* CLK DIB for idle check */
extern DIB ptr_dib;                                     /* PTR DIB for BBL configuration */

extern const BOOT_ROM ptr_rom, dq_rom, ms_rom, ds_rom;  /* boot ROMs for cpu_boot routine */


/* CPU program constants */


/* Command line switches */

#define ALL_MAPMODES    (SWMASK ('S') | SWMASK ('U') | SWMASK ('P') | SWMASK ('Q'))


/* RTE base-page addresses */

static const uint32 xeqt = 0001717;             /* XEQT address */
static const uint32 tbg  = 0001674;             /* TBG address */

/* DOS base-page addresses */

static const uint32 m64  = 0000040;             /* constant -64 address */
static const uint32 p64  = 0000067;             /* constant +64 address */


/* CPU global SCP data definitions */

REG *sim_PC = NULL;                             /* the pointer to the P register */


/* CPU global data structures */


/* CPU registers */

HP_WORD ABREG [2] = { 0, 0};                    /* A and B registers */
HP_WORD PR = 0;                                 /* P register */
HP_WORD SR = 0;                                 /* S register */
HP_WORD MR = 0;                                 /* M register */
HP_WORD TR = 0;                                 /* T register */
HP_WORD XR = 0;                                 /* X register */
HP_WORD YR = 0;                                 /* Y register */
uint32  E  = 0;                                 /* E register */
uint32  O  = 0;                                 /* O register */

HP_WORD IR  = 0;                                /* Instruction Register */
HP_WORD CIR = 0;                                /* Central Interrupt Register */


/* CPU global state */

FLIP_FLOP ion = CLEAR;                          /* interrupt enable */
t_bool    ion_defer = FALSE;                    /* interrupt defer */

t_stat    cpu_ss_unimpl   = SCPE_OK;            /* status return for unimplemented instruction execution */
t_stat    cpu_ss_undef    = SCPE_OK;            /* status return for undefined instruction execution */
t_stat    cpu_ss_unsc     = SCPE_OK;            /* status return for I/O to an unassigned select code */
t_stat    cpu_ss_ioerr    = SCPE_OK;            /* status return for an unreported I/O error */
t_stat    cpu_ss_inhibit  = SCPE_OK;            /* CPU stop inhibition mask */
UNIT      *cpu_ioerr_uptr = NULL;               /* pointer to a unit with an unreported I/O error */

uint16    pcq [PCQ_SIZE] = { 0 };               /* PC queue (must be 16-bits wide for REG array entry) */
uint32    pcq_p = 0;                            /* PC queue ptr */
REG       *pcq_r = NULL;                        /* PC queue reg ptr */

uint32    cpu_configuration;                    /* the current CPU option set and model */
uint32    cpu_speed = 1;                        /* the CPU speed, expressed as a multiplier of a real machine */

uint32 dev_prl [2] = { ~0u, ~0u };              /* device priority low bit vector */
uint32 dev_irq [2] = {  0u,  0u };              /* device interrupt request bit vector */
uint32 dev_srq [2] = {  0u,  0u };              /* device service request bit vector */


/* Main memory global state */

MEMORY_WORD *M = NULL;                          /* pointer to allocated memory */


/* Memory Expansion Unit global state */

uint32  dms_enb = 0;                            /* dms enable */
uint32  dms_ump = 0;                            /* dms user map */
HP_WORD dms_sr  = 0;                            /* dms status reg */


/* CPU local state */

static HP_WORD saved_MR = 0;                    /* M-register value between SCP commands */
static uint32  fwanxm   = 0;                    /* first word addr of nx mem */
static uint32  jsb_plb  = 2;                    /* protected lower bound for JSB */

static uint32  exec_mask      = 0;              /* the current instruction execution trace mask */
static uint32  exec_match     = D16_UMAX;       /* the current instruction execution trace matching value */
static uint32  indirect_limit = 16;             /* the indirect chain length limit */


/* Memory Expansion Unit local state */

static HP_WORD dms_vr = 0;                              /* dms violation reg */
static uint16  dms_map [MAP_NUM * MAP_LNT] = { 0 };     /* dms maps (must be 16-bits wide for REG array entry) */


/* CPU local data structures */


/* Interrupt deferral table (1000 version) */

static t_bool defer_tab [] = {                  /* deferral table, indexed by I/O sub-opcode */
    FALSE,                                      /*   soHLT */
    TRUE,                                       /*   soFLG */
    TRUE,                                       /*   soSFC */
    TRUE,                                       /*   soSFS */
    FALSE,                                      /*   soMIX */
    FALSE,                                      /*   soLIX */
    FALSE,                                      /*   soOTX */
    TRUE                                        /*   soCTL */
    };


/* Basic Binary Loader */

static const BOOT_ROM bbl = {
    0107700, 0063770, 0106501, 0004010,
    0002400, 0006020, 0063771, 0073736,
    0006401, 0067773, 0006006, 0027717,
    0107700, 0102077, 0027700, 0017762,
    0002003, 0027712, 0003104, 0073774,
    0017762, 0017753, 0070001, 0073775,
    0063775, 0043772, 0002040, 0027751,
    0017753, 0044000, 0000000, 0002101,
    0102000, 0037775, 0037774, 0027730,
    0017753, 0054000, 0027711, 0102011,
    0027700, 0102055, 0027700, 0000000,
    0017762, 0001727, 0073776, 0017762,
    0033776, 0127753, 0000000, 0103710,
    0102310, 0027764, 0102510, 0127762,
    0173775, 0153775, 0100100, 0177765,
    0000000, 0000000, 0000000, 0000000
    };

#define BBL_FWA             072                 /* BBL location to store negative FWA */


/* CPU features table.

   The feature table is used to validate CPU feature changes within the subset
   of features supported by a given CPU.  Features in the typical list are
   enabled when the CPU model is selected.  If a feature appears in the typical
   list but NOT in the optional list, then it is standard equipment and cannot
   be disabled.  If a feature appears in the optional list, then it may be
   enabled or disabled as desired by the user.
*/

struct FEATURE_TABLE {                          /* CPU model feature table: */
    uint32      typ;                            /*  - typical features */
    uint32      opt;                            /*  - optional features */
    uint32      maxmem;                         /*  - maximum memory */
    };

static struct FEATURE_TABLE cpu_features [] = {         /* features indexed by CPU model */
  { UNIT_DMA | UNIT_MP,                                 /*   UNIT_2116 */
    UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU,
    32 * 1024
    },

  { UNIT_DMA,                                           /*   UNIT_2115 */
    UNIT_PFAIL | UNIT_DMA | UNIT_EAU,
    8 * 1024
    },

  { UNIT_DMA,                                           /*   UNIT_2114 */
    UNIT_PFAIL | UNIT_DMA,
    16 * 1024 },

  { 0, 0, 0                                             /*   unused model */
    },

  { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU,         /*   UNIT_2100 */
    UNIT_DMA   | UNIT_FP | UNIT_IOP | UNIT_FFP,
    32 * 1024
    },

  { 0, 0, 0                                             /*   unused model */
    },
  { 0, 0, 0                                             /*   unused model */
    },
  { 0, 0, 0                                             /*   unused model */
    },

  { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /*   UNIT_1000_M */
    UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |
    UNIT_IOP   | UNIT_FFP | UNIT_DS,
    1024 * 1024
    },

  { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /*   UNIT_1000_E */
    UNIT_PFAIL | UNIT_DMA | UNIT_MP  | UNIT_DMS |
    UNIT_IOP   | UNIT_FFP | UNIT_DBI | UNIT_DS  | UNIT_EMA_VMA,
    1024 * 1024
    },

  { UNIT_MP  | UNIT_DMA | UNIT_EAU | UNIT_FP |          /*   UNIT_1000_F */
    UNIT_FFP | UNIT_DBI | UNIT_DMS,
    UNIT_PFAIL | UNIT_DMA | UNIT_MP     | UNIT_DMS |
    UNIT_VIS   | UNIT_DS  | UNIT_SIGNAL | UNIT_EMA_VMA,
    1024 * 1024
    }
  };


/* CPU local SCP support routine declarations */

static IOHANDLER cpuio;
static IOHANDLER ovflio;
static IOHANDLER pwrfio;

static t_stat cpu_examine (t_value *eval, t_addr address, UNIT *uptr, int32 switches);
static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches);

static t_stat cpu_reset (DEVICE *dptr);
static t_stat cpu_boot  (int32  unitno, DEVICE *dptr);

static t_stat set_stops    (UNIT *uptr, int32 option,    CONST char *cptr, void *desc);
static t_stat set_size     (UNIT *uptr, int32 new_size,  CONST char *cptr, void *desc);
static t_stat set_model    (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc);
static t_stat set_option   (UNIT *uptr, int32 option,    CONST char *cptr, void *desc);
static t_stat clear_option (UNIT *uptr, int32 option,    CONST char *cptr, void *desc);
static t_stat set_loader   (UNIT *uptr, int32 enable,    CONST char *cptr, void *desc);
static t_stat set_exec     (UNIT *uptr, int32 option,    CONST char *cptr, void *desc);

static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat show_exec  (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc);


/* CPU local utility routine declarations */

static t_stat  ea                  (HP_WORD IR, HP_WORD *address, uint32 irq);
static HP_WORD srg_uop             (HP_WORD value, HP_WORD operation);
static t_stat  machine_instruction (HP_WORD IR, t_bool iotrap, uint32 irq_pending, uint32 *idle_save);
static t_bool  check_deferral      (uint32 irq_sc);
static uint32  map_address         (HP_WORD logical, int32 switches);
static t_bool  mem_is_empty        (uint32 starting_address);


/* Memory Expansion Unit local utility routine declarations */

static t_bool is_mapped (uint32 address);
static uint32 meu_map   (HP_WORD address, uint32 map, HP_WORD prot);


/* CPU SCP data structures */


/* Device information blocks */

static DIB cpu_dib = {                          /* CPU select code 0 */
    &cpuio,                                     /*   device interface */
    CPU,                                        /*   select code */
    0                                           /*   card index */
    };

static DIB ovfl_dib = {                         /* Overflow select code 1 */
    &ovflio,                                    /*   device interface */
    OVF,                                        /*   select code */
    0                                           /*   card index */
    };

static DIB pwrf_dib = {                         /* Power Fail select code 4 */
    &pwrfio,                                    /*   device interface */
    PWR,                                        /*   select code */
    0                                           /*   card index */
    };


/* Unit list.

   The CPU unit holds the main memory capacity.


   Implementation notes:

    1. The unit structure must be global for other modules to access the unit
       flags, which describe the installed options, and to obtain the memory
       size via the MEMSIZE macro, which references the "capac" field.
*/

UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK, 0) };


/* Register list.

   The CPU register list exposes the machine registers for user inspection and
   modification.


   Implementation notes:

    1. All registers that reference variables of type HP_WORD must have the
       REG_FIT flag for proper access if HP_WORD is a 16-bit type.

    2. The REG_X flag indicates that the register may be displayed in symbolic
       form.
*/

static REG cpu_reg [] = {
/*    Macro   Name       Location            Radix  Width   Offset       Depth                Flags       */
/*    ------  ---------  ------------------  -----  -----  --------  -----------------  ----------------- */
    { ORDATA (P,         PR,                         15)                                                  },
    { ORDATA (A,         AR,                         16),                               REG_X             },
    { ORDATA (B,         BR,                         16),                               REG_X             },
    { ORDATA (M,         MR,                         15)                                                  },
    { ORDATA (T,         TR,                         16),                               REG_RO | REG_X    },
    { ORDATA (X,         XR,                         16),                               REG_X             },
    { ORDATA (Y,         YR,                         16),                               REG_X             },
    { ORDATA (S,         SR,                         16),                               REG_X             },
    { FLDATA (E,         E,                                   0)                                          },
    { FLDATA (O,         O,                                   0)                                          },
    { ORDATA (CIR,       CIR,                         6)                                                  },

    { FLDATA (ION,       ion,                                 0)                                          },
    { FLDATA (ION_DEFER, ion_defer,                           0)                                          },
    { FLDATA (DMSENB,    dms_enb,                             0)                                          },
    { FLDATA (DMSCUR,    dms_ump,                          VA_N_PAG)                                      },

    { ORDATA (DMSSR,     dms_sr,                     16)                                                  },
    { ORDATA (DMSVR,     dms_vr,                     16)                                                  },
    { BRDATA (DMSMAP,    dms_map,              8,    16,             MAP_NUM * MAP_LNT)                   },

    { ORDATA (IOPSP,     iop_sp,                     16)                                                  },
    { BRDATA (PCQ,       pcq,                  8,    15,             PCQ_SIZE),         REG_CIRC | REG_RO },

    { ORDATA (IR,        IR,                         16),                               REG_HRO           },
    { ORDATA (PCQP,      pcq_p,                       6),                               REG_HRO           },
    { ORDATA (JSBPLB,    jsb_plb,                    32),                               REG_HRO           },
    { ORDATA (SAVEDMR,   saved_MR,                   32),                               REG_HRO           },
    { ORDATA (FWANXM,    fwanxm,                     32),                               REG_HRO           },
    { ORDATA (CONFIG,    cpu_configuration,          32),                               REG_HRO           },

    { ORDATA (WRU,       sim_int_char,                8),                               REG_HRO           },
    { ORDATA (BRK,       sim_brk_char,                8),                               REG_HRO           },
    { ORDATA (DEL,       sim_del_char,                8),                               REG_HRO           },

    { BRDATA (PRL,       dev_prl,              8,    32,                 2),            REG_HRO           },
    { BRDATA (IRQ,       dev_irq,              8,    32,                 2),            REG_HRO           },
    { BRDATA (SRQ,       dev_srq,              8,    32,                 2),            REG_HRO           },
    { NULL }
    };


/* Modifier list.


   Implementation notes:

    1. The 21MX monikers are deprecated in favor of the 1000 designations.  See
       the "HP 1000 Series Naming History" on the back inside cover of the
       Technical Reference Handbook.

    2. Each CPU option requires three modifiers.  The two regular modifiers
       control the setting and printing of the option, while the extended
       modifier controls clearing the option.  The latter is necessary because
       the option must be checked before confirming the change, and so the
       option value must be passed to the validation routine.
*/

static MTAB cpu_mod [] = {
/*    Mask Value       Match Value  Print String  Match String  Validation     Display      Descriptor        */
/*    ---------------  -----------  ------------  ------------  -------------  -----------  ----------------- */
    { UNIT_MODEL_MASK, UNIT_2116,   "",           "2116",       &set_model,    &show_model, (void *) "2116"   },
    { UNIT_MODEL_MASK, UNIT_2115,   "",           "2115",       &set_model,    &show_model, (void *) "2115"   },
    { UNIT_MODEL_MASK, UNIT_2114,   "",           "2114",       &set_model,    &show_model, (void *) "2114"   },
    { UNIT_MODEL_MASK, UNIT_2100,   "",           "2100",       &set_model,    &show_model, (void *) "2100"   },
    { UNIT_MODEL_MASK, UNIT_1000_E, "",           "1000-E",     &set_model,    &show_model, (void *) "1000-E" },
    { UNIT_MODEL_MASK, UNIT_1000_E, NULL,         "21MX-E",     &set_model,    &show_model, (void *) "1000-E" },
    { UNIT_MODEL_MASK, UNIT_1000_M, "",           "1000-M",     &set_model,    &show_model, (void *) "1000-M" },
    { UNIT_MODEL_MASK, UNIT_1000_M, NULL,         "21MX-M",     &set_model,    &show_model, (void *) "1000-M" },

#if defined (HAVE_INT64)
    { UNIT_MODEL_MASK, UNIT_1000_F, "",           "1000-F",     &set_model,    &show_model, (void *) "1000-F" },
#endif

    { UNIT_EAU,        UNIT_EAU,    "EAU",        "EAU",        &set_option,   NULL,        NULL              },
    { UNIT_EAU,        0,           "no EAU",     NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_EAU,     NULL,        "NOEAU",      &clear_option, NULL,        NULL              },

    { UNIT_FP,         UNIT_FP,     "FP",         "FP",         &set_option,   NULL,        NULL              },
    { UNIT_FP,         0,           "no FP",      NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_FP,      NULL,        "NOFP",       &clear_option, NULL,        NULL              },

    { UNIT_IOP,        UNIT_IOP,    "IOP",        "IOP",        &set_option,   NULL,        NULL              },
    { UNIT_IOP,        0,           "no IOP",     NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_IOP,     NULL,        "NOIOP",      &clear_option, NULL,        NULL              },

    { UNIT_DMS,        UNIT_DMS,    "DMS",        "DMS",        &set_option,   NULL,        NULL              },
    { UNIT_DMS,        0,           "no DMS",     NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_DMS,     NULL,        "NODMS",      &clear_option, NULL,        NULL              },

    { UNIT_FFP,        UNIT_FFP,    "FFP",        "FFP",        &set_option,   NULL,        NULL              },
    { UNIT_FFP,        0,           "no FFP",     NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_FFP,     NULL,        "NOFFP",      &clear_option, NULL,        NULL              },

    { UNIT_DBI,        UNIT_DBI,    "DBI",        "DBI",        &set_option,   NULL,        NULL              },
    { UNIT_DBI,        0,           "no DBI",     NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_DBI,     NULL,        "NODBI",      &clear_option, NULL,        NULL              },

    { UNIT_EMA_VMA,    UNIT_EMA,    "EMA",        "EMA",        &set_option,   NULL,        NULL              },
    { MTAB_XDV,        UNIT_EMA,     NULL,        "NOEMA",      &clear_option, NULL,        NULL              },

    { UNIT_EMA_VMA,    UNIT_VMAOS,  "VMA",        "VMA",        &set_option,   NULL,        NULL              },
    { MTAB_XDV,        UNIT_VMAOS,   NULL,        "NOVMA",      &clear_option, NULL,        NULL              },

    { UNIT_EMA_VMA,    0,           "no EMA/VMA", NULL,         &set_option,   NULL,        NULL              },

#if defined (HAVE_INT64)
    { UNIT_VIS,        UNIT_VIS,    "VIS",        "VIS",        &set_option,   NULL,        NULL              },
    { UNIT_VIS,        0,           "no VIS",     NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_VIS,     NULL,        "NOVIS",      &clear_option, NULL,        NULL              },

    { UNIT_SIGNAL,     UNIT_SIGNAL, "SIGNAL",     "SIGNAL",     &set_option,   NULL,        NULL              },
    { UNIT_SIGNAL,     0,           "no SIGNAL",  NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_SIGNAL,  NULL,        "NOSIGNAL",   &clear_option, NULL,        NULL              },
#endif

/* Future microcode support.
    { UNIT_DS,         UNIT_DS,     "DS",         "DS",         &set_option,   NULL,        NULL              },
    { UNIT_DS,         0,           "no DS",      NULL,         NULL,          NULL,        NULL              },
    { MTAB_XDV,        UNIT_DS,      NULL,        "NODS",       &clear_option, NULL,        NULL              },
*/

/*    Entry Flags             Value     Print String  Match String     Validation     Display         Descriptor */
/*    -------------------  -----------  ------------  ---------------  -------------  --------------  ---------- */
    { MTAB_XDV,                 0,      "IDLE",       "IDLE",          &sim_set_idle, &sim_show_idle, NULL       },
    { MTAB_XDV,                 0,      NULL,         "NOIDLE",        &sim_clr_idle, NULL,           NULL       },

    { MTAB_XDV,                 1,      NULL,         "LOADERENABLE",  &set_loader,   NULL,           NULL       },
    { MTAB_XDV,                 0,      NULL,         "LOADERDISABLE", &set_loader,   NULL,           NULL       },

    { MTAB_XDV,               4 * 1024, NULL,         "4K",            &set_size,     NULL,           NULL       },
    { MTAB_XDV,               8 * 1024, NULL,         "8K",            &set_size,     NULL,           NULL       },
    { MTAB_XDV,              12 * 1024, NULL,         "12K",           &set_size,     NULL,           NULL       },
    { MTAB_XDV,              16 * 1024, NULL,         "16K",           &set_size,     NULL,           NULL       },
    { MTAB_XDV,              24 * 1024, NULL,         "24K",           &set_size,     NULL,           NULL       },
    { MTAB_XDV,              32 * 1024, NULL,         "32K",           &set_size,     NULL,           NULL       },
    { MTAB_XDV,              64 * 1024, NULL,         "64K",           &set_size,     NULL,           NULL       },
    { MTAB_XDV,             128 * 1024, NULL,         "128K",          &set_size,     NULL,           NULL       },
    { MTAB_XDV,             256 * 1024, NULL,         "256K",          &set_size,     NULL,           NULL       },
    { MTAB_XDV,             512 * 1024, NULL,         "512K",          &set_size,     NULL,           NULL       },
    { MTAB_XDV,            1024 * 1024, NULL,         "1024K",         &set_size,     NULL,           NULL       },

    { MTAB_XDV | MTAB_NMO,      1,      "STOPS",      "STOP",          &set_stops,    &show_stops,    NULL       },
    { MTAB_XDV,                 0,      NULL,         "NOSTOP",        &set_stops,    NULL,           NULL       },
    { MTAB_XDV | MTAB_NMO,      2,      "INDIR",      "INDIR",         &set_stops,    &show_stops,    NULL       },

    { MTAB_XDV | MTAB_NMO,      1,      "EXEC",       "EXEC",          &set_exec,     &show_exec,     NULL       },
    { MTAB_XDV,                 0,      NULL,         "NOEXEC",        &set_exec,     NULL,           NULL       },

    { MTAB_XDV | MTAB_NMO,      0,      "SPEED",      NULL,            NULL,          &show_speed,    NULL       },

    { 0 }
    };


/* Debugging trace list */

static DEBTAB cpu_deb [] = {
    { "INSTR", TRACE_INSTR },                   /* trace instruction executions */
    { "DATA",  TRACE_DATA  },                   /* trace memory data accesses */
    { "FETCH", TRACE_FETCH },                   /* trace memory instruction fetches */
    { "REG",   TRACE_REG   },                   /* trace register values */
    { "OPND",  TRACE_OPND  },                   /* trace instruction operands */
    { "EXEC",  TRACE_EXEC  },                   /* trace matching instruction execution states */
    { "NOOS",  DEBUG_NOOS  },                   /* RTE-6/VM will not use OS firmware */
    { NULL,    0 }
    };


/* Simulation stop list.

   The simulator can be configured to detect certain machine instruction
   conditions and stop execution when one of them occurs.  Stops may be enabled
   or disabled individually with these commands:

     SET CPU STOP=<option>[;<option]
     SET CPU NOSTOP=<option>[;<option]

   The CPU stop table is used to parse the commands and set the appropriate
   variables to enable or disable the stops.


   Implementation notes:

    1. To avoid the testing of stop conditions at run time, they are implemented
       by setting individual stop status variables either to the appropriate
       stop code (if enabled) or to SCPE_OK (if disabled).  This allows the
       affected routines to return the status value unconditionally and cause
       either a simulator stop or continued execution without a run-time test.

    2. SCPE_IOERR is not actually returned for unreported I/O errors.  Instead,
       it is simply a flag that a stop code specific to the detected error
       should be returned.

    3. To permit stops to be bypassed for one instruction execution, routines
       use the STOP macro to return the value of the applicable stop variable
       ANDed with the complement of the value of the "cpu_ss_inhibit" variable.
       The latter is set in the instruction prelude to SS_INHIBIT (i.e., all
       ones) if a bypass is requested or to SCPE_OK (i.e., all zeros) if not,
       and is reset to SCPE_OK after each instruction execution.  The effect is
       that SCPE_OK is returned instead of a simulator stop if a stop condition
       occurs when a bypass is specified.  This action depends on the value of
       SCPE_OK being zero (which is guaranteed).
*/

typedef struct {
    const char  *name;                          /* stop name */
    t_stat      *status;                        /* pointer to the stop status variable */
    t_stat      value;                          /* stop status return value */
    } STOPTAB;

static STOPTAB cpu_stop [] = {
    { "UNIMPL", &cpu_ss_unimpl, STOP_UNIMPL },  /* stop on an unimplemented instruction */
    { "UNDEF",  &cpu_ss_undef,  STOP_UNDEF  },  /* stop on an undefined instruction */
    { "UNSC",   &cpu_ss_unsc,   STOP_UNSC   },  /* stop on I/O to an unassigned select code */
    { "IOERR",  &cpu_ss_ioerr,  SCPE_IOERR  },  /* stop on an unreported I/O error */
    { NULL,     NULL,           0           }
    };


/* Device descriptor */

DEVICE cpu_dev = {
    "CPU",                                      /* device name */
    &cpu_unit,                                  /* unit array */
    cpu_reg,                                    /* register array */
    cpu_mod,                                    /* modifier array */
    1,                                          /* number of units */
    8,                                          /* address radix */
    PA_N_SIZE,                                  /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    16,                                         /* data width */
    &cpu_examine,                               /* examine routine */
    &cpu_deposit,                               /* deposit routine */
    &cpu_reset,                                 /* reset routine */
    &cpu_boot,                                  /* boot routine */
    NULL,                                       /* attach routine */
    NULL,                                       /* detach routine */
    &cpu_dib,                                   /* device information block pointer */
    DEV_DEBUG,                                  /* device flags */
    0,                                          /* debug control flags */
    cpu_deb,                                    /* debug flag name table */
    NULL,                                       /* memory size change routine */
    NULL                                        /* logical device name */
    };



/* Memory program constants */

static const char map_indicator [] = {          /* MEU map indicator, indexed by map type */
    'S',                                        /*   System */
    'U',                                        /*   User   */
    'A',                                        /*   Port_A */
    'B'                                         /*   Port_B */
    };


/* Memory global data structures */


/* Memory access classification table */

typedef struct {
    uint32      debug_flag;                     /* the debug flag for tracing */
    const char  *name;                          /* the classification name */
    } ACCESS_PROPERTIES;

static const ACCESS_PROPERTIES access [] = {    /* indexed by ACCESS_CLASS */
/*    debug_flag    name                */
/*    ------------  ------------------- */
    { TRACE_FETCH,  "instruction fetch" },      /*   instruction fetch */
    { TRACE_DATA,   "data"              },      /*   data access */
    { TRACE_DATA,   "data"              },      /*   data access, alternate map */
    { TRACE_DATA,   "unprotected"       },      /*   data access, system map */
    { TRACE_DATA,   "unprotected"       },      /*   data access, user map */
    { TRACE_DATA,   "dma"               },      /*   DMA channel 1, port A map */
    { TRACE_DATA,   "dma"               }       /*   DMA channel 2, port B map */
    };



/* DMA program constants */

#define DMA_CHAN_COUNT  2                       /* number of DMA channels */

#define DMA_OE          020000000000u           /* byte packing odd/even flag */
#define DMA1_STC        0100000u                /* DMA - issue STC */
#define DMA1_PB         0040000u                /* DMA - pack bytes */
#define DMA1_CLC        0020000u                /* DMA - issue CLC */
#define DMA2_OI         0100000u                /* DMA - output/input */

typedef enum { ch1, ch2 } CHANNEL;              /* channel number */

#define DMA_1_REQ       (1 << ch1)              /* channel 1 request */
#define DMA_2_REQ       (1 << ch2)              /* channel 2 request */

typedef struct {
    FLIP_FLOP control;                          /* control flip-flop */
    FLIP_FLOP flag;                             /* flag flip-flop */
    FLIP_FLOP flagbuf;                          /* flag buffer flip-flop */
    FLIP_FLOP xferen;                           /* transfer enable flip-flop */
    FLIP_FLOP select;                           /* register select flip-flop */

    HP_WORD   cw1;                              /* device select */
    HP_WORD   cw2;                              /* direction, address */
    HP_WORD   cw3;                              /* word count */
    uint32    packer;                           /* byte-packer holding reg */
    } DMA_STATE;


/* DMA global state */

DMA_STATE dma [DMA_CHAN_COUNT];                 /* per-channel state */


/* DMA local data structures */

static const BITSET_NAME dma_cw1_names [] = {   /* DMA control word 1 names */
    "STC",                                      /*   bit 15 */
    "byte packing",                             /*   bit 14 */
    "CLC"                                       /*   bit 13 */
    };

static const BITSET_FORMAT dma_cw1_format =          /* names, offset, direction, alternates, bar */
    { FMT_INIT (dma_cw1_names, 13, msb_first, no_alt, append_bar) };


/* DMA local SCP support routine declarations */

static IOHANDLER dmapio;
static IOHANDLER dmasio;
static t_stat    dma_reset (DEVICE *dptr);


/* DMA local utility routine declarations */

static t_stat dma_cycle (CHANNEL chan, ACCESS_CLASS class);
static uint32 calc_dma  (void);


/* DMA SCP data structures */


/* Device information blocks */

static DIB dmap1_dib = {
    &dmapio,                                    /* device interface */
    DMA1,                                       /* select code */
    ch1                                         /* card index */
    };

static DIB dmas1_dib = {
    &dmasio,                                    /* device interface */
    DMALT1,                                     /* select code */
    ch1                                         /* card index */
    };

static DIB dmap2_dib = {
    &dmapio,                                    /* device interface */
    DMA2,                                       /* select code */
    ch2                                         /* card index */
    };

static DIB dmas2_dib = {
    &dmasio,                                    /* device interface */
    DMALT2,                                     /* select code */
    ch2                                         /* card index */
    };


/* Unit lists */

static UNIT dma1_unit = { UDATA (NULL, 0, 0) };

static UNIT dma2_unit = { UDATA (NULL, 0, 0) };


/* Register lists */

static REG dma1_reg [] = {
/*    Macro   Name     Location            Width  Flags */
/*    ------  -------  ------------------  -----  ----- */
    { FLDATA (XFR,     dma [ch1].xferen,     0)         },
    { FLDATA (CTL,     dma [ch1].control,    0)         },
    { FLDATA (FLG,     dma [ch1].flag,       0)         },
    { FLDATA (FBF,     dma [ch1].flagbuf,    0)         },
    { FLDATA (CTL2,    dma [ch1].select,     0)         },
    { ORDATA (CW1,     dma [ch1].cw1,       16)         },
    { ORDATA (CW2,     dma [ch1].cw2,       16)         },
    { ORDATA (CW3,     dma [ch1].cw3,       16)         },
    { FLDATA (BYTE,    dma [ch1].packer,    31)         },
    { ORDATA (PACKER,  dma [ch1].packer,     8),  REG_A },
    { NULL }
    };

static REG dma2_reg [] = {
/*    Macro   Name     Location            Width  Flags */
/*    ------  -------  ------------------  -----  ----- */
    { FLDATA (XFR,     dma [ch2].xferen,     0)         },
    { FLDATA (CTL,     dma [ch2].control,    0)         },
    { FLDATA (FLG,     dma [ch2].flag,       0)         },
    { FLDATA (FBF,     dma [ch2].flagbuf,    0)         },
    { FLDATA (CTL2,    dma [ch2].select,     0)         },
    { ORDATA (CW1,     dma [ch2].cw1,       16)         },
    { ORDATA (CW2,     dma [ch2].cw2,       16)         },
    { ORDATA (CW3,     dma [ch2].cw3,       16)         },
    { FLDATA (BYTE,    dma [ch2].packer,    31)         },
    { ORDATA (PACKER,  dma [ch2].packer,     8),  REG_A },
    { NULL }
    };


/* Debugging trace list */

static DEBTAB dma_deb [] = {
    { "CMD",   TRACE_CMD   },                   /* trace interface or controller commands */
    { "CSRW",  TRACE_CSRW  },                   /* trace interface control, status, read, and write actions */
    { "SR",    TRACE_SR    },                   /* trace service requests received */
    { "DATA",  TRACE_DATA  },                   /* trace memory data accesses */
    { "IOBUS", TRACE_IOBUS },                   /* trace I/O bus signals and data words received and returned */
    { NULL,    0 }
    };


/* Device descriptors */

DEVICE dma1_dev = {
    "DMA1",                                     /* device name */
    &dma1_unit,                                 /* unit array */
    dma1_reg,                                   /* register array */
    NULL,                                       /* modifier array */
    1,                                          /* number of units */
    8,                                          /* address radix */
    1,                                          /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    16,                                         /* data width */
    NULL,                                       /* examine routine */
    NULL,                                       /* deposit routine */
    &dma_reset,                                 /* reset routine */
    NULL,                                       /* boot routine */
    NULL,                                       /* attach routine */
    NULL,                                       /* detach routine */
    &dmap1_dib,                                 /* device information block pointer */
    DEV_DISABLE | DEV_DEBUG,                    /* device flags */
    0,                                          /* debug control flags */
    dma_deb,                                    /* debug flag name table */
    NULL,                                       /* memory size change routine */
    NULL                                        /* logical device name */
    };

DEVICE dma2_dev = {
    "DMA2",                                     /* device name */
    &dma2_unit,                                 /* unit array */
    dma2_reg,                                   /* register array */
    NULL,                                       /* modifier array */
    1,                                          /* number of units */
    8,                                          /* address radix */
    1,                                          /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    16,                                         /* data width */
    NULL,                                       /* examine routine */
    NULL,                                       /* deposit routine */
    &dma_reset,                                 /* reset routine */
    NULL,                                       /* boot routine */
    NULL,                                       /* attach routine */
    NULL,                                       /* detach routine */
    &dmap2_dib,                                 /* device information block pointer */
    DEV_DISABLE | DEV_DEBUG,                    /* device flags */
    0,                                          /* debug control flags */
    dma_deb,                                    /* debug flag name table */
    NULL,                                       /* memory size change routine */
    NULL                                        /* logical device name */
    };

static DEVICE *dma_dptrs [] = {
    &dma1_dev,
    &dma2_dev
    };



/* Memory Protect program constants */

#define UNIT_V_MP_JSB   (UNIT_V_UF + 0)         /* MP jumper W5 */
#define UNIT_V_MP_INT   (UNIT_V_UF + 1)         /* MP jumper W6 */
#define UNIT_V_MP_SEL1  (UNIT_V_UF + 2)         /* MP jumper W7 */
#define UNIT_MP_JSB     (1 << UNIT_V_MP_JSB)    /* 1 = W5 is out */
#define UNIT_MP_INT     (1 << UNIT_V_MP_INT)    /* 1 = W6 is out */
#define UNIT_MP_SEL1    (1 << UNIT_V_MP_SEL1)   /* 1 = W7 is out */

#define MP_TEST(va)     (mp_control && ((va) >= 2) && ((va) < mp_fence))


/* Memory Protect global state */

FLIP_FLOP mp_control = CLEAR;                   /* MP control flip-flop */
FLIP_FLOP mp_mevff   = CLEAR;                   /* memory expansion violation flip-flop */
HP_WORD   mp_fence   = 0;                       /* MP fence register  */
HP_WORD   mp_viol    = 0;                       /* MP violation register */
HP_WORD   iop_sp     = 0;                       /* iop stack reg */
HP_WORD   err_PC     = 0;                       /* error PC */

jmp_buf   save_env;                             /* MP abort handler */
t_bool    mp_mem_changed;                       /* TRUE if the MP or MEM registers have been altered */


/* Memory Protect local state */

static FLIP_FLOP mp_flag        = CLEAR;        /* MP flag flip-flop */
static FLIP_FLOP mp_flagbuf     = CLEAR;        /* MP flag buffer flip-flop */
static FLIP_FLOP mp_evrff       = SET;          /* enable violation register flip-flop */
static t_bool    is_1000        = FALSE;        /* TRUE if the CPU is a 1000 M/E/F-Series */
static char      meu_indicator;                 /* last map access indicator (S | U | A | B | -) */
static uint32    meu_page;                      /* last physical page number accessed */


/* Memory Protect local SCP support routine declarations */

static IOHANDLER protio;
static t_stat    mp_reset (DEVICE *dptr);


/* Memory Protect SCP data structures */


/* Device information block */

static DIB mp_dib = {
    &protio,                                    /*   device interface */
    PRO,                                        /*   select code */
    0                                           /*   card index */
    };


/* Unit list.


   Implementation notes:

    1. The default flags correspond to the following jumper settings: JSB in,
       INT in, SEL1 out.
*/

static UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) };


/* Register list */

static REG mp_reg [] = {
/*    Macro   Name  Location     Width */
/*    ------  ----  -----------  ----- */
    { FLDATA (CTL,  mp_control,    0)  },
    { FLDATA (FLG,  mp_flag,       0)  },
    { FLDATA (FBF,  mp_flagbuf,    0)  },
    { ORDATA (FR,   mp_fence,     15)  },
    { ORDATA (VR,   mp_viol,      16)  },
    { FLDATA (EVR,  mp_evrff,      0)  },
    { FLDATA (MEV,  mp_mevff,      0)  },
    { NULL }
    };


/* Modifier list */

static MTAB mp_mod [] = {
/*    Mask Value     Match Value   Print String     Match String  Validation  Display  Descriptor */
/*    -------------  ------------  ---------------  ------------  ----------  -------  ---------- */
    { UNIT_MP_JSB,   UNIT_MP_JSB,  "JSB (W5) out",  "JSBOUT",     NULL,       NULL,    NULL       },
    { UNIT_MP_JSB,   0,            "JSB (W5) in",   "JSBIN",      NULL,       NULL,    NULL       },
    { UNIT_MP_INT,   UNIT_MP_INT,  "INT (W6) out",  "INTOUT",     NULL,       NULL,    NULL       },
    { UNIT_MP_INT,   0,            "INT (W6) in",   "INTIN",      NULL,       NULL,    NULL       },
    { UNIT_MP_SEL1,  UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT",    NULL,       NULL,    NULL       },
    { UNIT_MP_SEL1,  0,            "SEL1 (W7) in",  "SEL1IN",     NULL,       NULL,    NULL       },
    { 0 }
    };


/* Device descriptor */

DEVICE mp_dev = {
    "MP",                                       /* device name */
    &mp_unit,                                   /* unit array */
    mp_reg,                                     /* register array */
    mp_mod,                                     /* modifier array */
    1,                                          /* number of units */
    8,                                          /* address radix */
    1,                                          /* address width */
    1,                                          /* address increment */
    8,                                          /* data radix */
    16,                                         /* data width */
    NULL,                                       /* examine routine */
    NULL,                                       /* deposit routine */
    &mp_reset,                                  /* reset routine */
    NULL,                                       /* boot routine */
    NULL,                                       /* attach routine */
    NULL,                                       /* detach routine */
    &mp_dib,                                    /* device information block pointer */
    DEV_DISABLE | DEV_DIS,                      /* device flags */
    0,                                          /* debug control flags */
    NULL,                                       /* debug flag name table */
    NULL,                                       /* memory size change routine */
    NULL                                        /* logical device name */
    };



/* I/O system program constants */

static const BITSET_NAME inbound_names [] = {   /* Inbound signal names, in IOSIGNAL order */
    "PON",                                      /*   000000000001 */
    "ENF",                                      /*   000000000002 */
    "IOI",                                      /*   000000000004 */
    "IOO",                                      /*   000000000010 */
    "SKF",                                      /*   000000000020 */
    "SFS",                                      /*   000000000040 */
    "SFC",                                      /*   000000000100 */
    "STC",                                      /*   000000000200 */
    "CLC",                                      /*   000000000400 */
    "STF",                                      /*   000000001000 */
    "CLF",                                      /*   000000002000 */
    "EDT",                                      /*   000000004000 */
    "CRS",                                      /*   000000010000 */
    "POPIO",                                    /*   000000020000 */
    "IAK",                                      /*   000000040000 */
    "SIR"                                       /*   000000100000 */
    };

static const BITSET_FORMAT inbound_format =     /* names, offset, direction, alternates, bar */
    { FMT_INIT (inbound_names, 0, lsb_first, no_alt, no_bar) };


/* I/O instruction sub-opcodes */

#define soHLT           0                       /* halt */
#define soFLG           1                       /* set/clear flag */
#define soSFC           2                       /* skip on flag clear */
#define soSFS           3                       /* skip on flag set */
#define soMIX           4                       /* merge into A/B */
#define soLIX           5                       /* load into A/B */
#define soOTX           6                       /* output from A/B */
#define soCTL           7                       /* set/clear control */


/* I/O system local data structures */

static DIB *dibs [MAXDEV + 1] = {               /* index by select code for I/O instruction dispatch */
    &cpu_dib,                                   /*   select code 00 = interrupt system */
    &ovfl_dib                                   /*   select code 01 = overflow register */
    };

static DEVICE *devs [MAXDEV + 1] = {            /* index by select code for I/O dispatch tracing */
    &cpu_dev,                                   /*   select code 00 = interrupt system */
    &cpu_dev                                    /*   select code 01 = overflow register */
    };


/* I/O system local utility routine declarations */

static void   io_initialize (void);
static uint32 io_dispatch   (uint32 select_code, IOCYCLE signal_set, HP_WORD data);



/* CPU global SCP support routines */


/* Execute CPU instructions.

   This is the instruction decode routine for the HP 21xx/1000 simulator.  It is
   called from the simulator control program (SCP) to execute instructions in
   simulated memory, starting at the simulated program counter.  It runs until
   the status to be returned is set to a value other than SCPE_OK.

   On entry, P points to the instruction to execute, and the "sim_switches"
   global contains any command-line switches included with the run command.  On
   exit, P points at the next instruction to execute.

   Execution is divided into four phases.

   First, the instruction prelude configures the simulation state to resume
   execution.  This involves verifying that there are no device conflicts (e.g.,
   two devices with the same select code) and initializing the I/O state.  These
   actions accommodate reconfiguration of the I/O device settings and program
   counter while the simulator was stopped.  The prelude also responds to one
   command-line switch: if "-B" is specified, the current set of simulation stop
   conditions is bypassed for the first instruction executed.

   Second, the memory protect abort mechanism is set up.  MP aborts utilize the
   "setjmp/longjmp" mechanism to transfer control out of the instruction
   executors without returning through the call stack.  This allows an
   instruction to be aborted part-way through execution when continuation is
   impossible due to a memory access violation.

   Third, the instruction execution loop decodes instructions and calls the
   individual executors in turn until a condition occurs that prevents further
   execution.  Examples of such conditions include execution of a HLT
   instruction, a user stop request (CTRL+E) from the simulation console, a
   recoverable device error (such as an improperly formatted tape image), a
   user-specified breakpoint, and a simulation stop condition (such as execution
   of an unimplemented instruction).  The execution loop also polls for I/O
   events and device interrupts, and runs DMA channel cycles.  During
   instruction execution, the IR register contains the currently executing
   instruction, and the P register points to the memory location containing the
   next instruction.

   Fourth, the instruction postlude updates the simulation state in preparation
   for returning to the SCP command prompt.  Devices that maintain an internal
   state different from their external state, such as the MEM status and
   violation registers, are updated so that their internal and external states
   are fully consistent.  This ensures that the state visible to the user during
   the simulation stop is correct.  It also ensures that the program counter
   points correctly at the next instruction to execute upon resumption.


   In hardware, if the Memory Protect accessory is installed and enabled, I/O
   operations to select codes other than 01 are prohibited.  Also, in
   combination with the MPCK micro-order, MP validates the M-register contents
   (memory address) against the memory protect fence.  If a violation occurs, an
   I/O instruction or memory write is inhibited, and a memory read returns
   invalid data.

   In simulation, an instruction executor that detects an MP violation calls the
   MP_ABORT macro, passing the violation address as the parameter.  This
   executes a "longjmp" to the abort handler, which is outside of and precedes
   the instruction execution loop.  The value passed to "longjmp" is a 32-bit
   integer containing the logical address of the instruction causing the
   violation.  MP_ABORT should only be called if "mp_control" is SET, as aborts
   do not occur if MP is turned off.

   An MP interrupt (SC 05) is qualified by "ion" but not by "ion_defer".  If the
   interrupt system is off when an MP violation is detected, the violating
   instruction will be aborted, even though no interrupt occurs.  In this case,
   neither the flag nor the flag buffer are set, and EVR is not cleared.


   The instruction execution loop starts by checking for event timer expiration.
   If one occurs, the associated event service routine is called, and if it was
   successful, the DMA service requests and interrupt requests are recalculated.

   DMA cycles are requested by an I/O card asserting its SRQ signal.  If a DMA
   channel is programmed to respond to that card's select code, a DMA cycle will
   be initiated.  A DMA cycle consists of a memory cycle and an I/O cycle.
   These cycles are synchronized with the control processor on the 21xx CPUs. On
   the 1000s, memory cycles are asynchronous, while I/O cycles are synchronous.
   Memory cycle time is about 40% of the I/O cycle time.

   With properly designed interface cards, DMA is capable of taking consecutive
   I/O cycles.  On all machines except the 1000 M-Series, a DMA cycle freezes
   the CPU for the duration of the cycle.  On the M-Series, a DMA cycle freezes
   the CPU if it attempts an I/O cycle (including IAK) or a directly-interfering
   memory cycle.  An interleaved memory cycle is allowed.  Otherwise, the
   control processor is allowed to run.  Therefore, during consecutive DMA
   cycles, the M-Series CPU will run until an IOG instruction is attempted,
   whereas the other CPUs will freeze completely.

   All DMA cards except the 12607B provide two independent channels.  If both
   channels are active simultaneously, channel 1 has priority for I/O cycles
   over channel 2.

   Most I/O cards assert SRQ no more than 50% of the time.  A few buffered
   cards, such as the 12821A and 13175A Disc Interfaces, are capable of
   asserting SRQ continuously while filling or emptying the buffer.  If SRQ for
   channel 1 is asserted continuously when both channels are active, then no
   channel 2 cycles will occur until channel 1 completes.

   Interrupt recognition is controlled by three state variables: "ion",
   "ion_defer", and "intrq".  "ion" corresponds to the INTSYS flip-flop in the
   1000 CPU, "ion_defer" corresponds to the INTEN flip-flop, and "intrq"
   corresponds to the NRMINT flip-flop.  STF 00 and CLF 00 set and clear INTSYS,
   turning the interrupt system on and off.  Micro-orders ION and IOFF set and
   clear INTEN, deferring or allowing certain interrupts.  An IRQ signal from a
   device, qualified by the corresponding PRL signal, will set NRMINT to request
   a normal interrupt; an IOFF or IAK will clear it.

   Under simulation, "ion" is controlled by STF/CLF 00.  "ion_defer" is set or
   cleared as appropriate by the individual instruction simulators.  "intrq" is
   set to the successfully interrupting device's select code, or to zero if
   there is no qualifying interrupt request.

   Presuming PRL is set to allow priority to an interrupting device:

    1. Power fail (SC 04) may interrupt if "ion_defer" is clear; this is not
       conditional on "ion" being set.

    2. Memory protect (SC 05) may interrupt if "ion" is set; this is not
       conditional on "ion_defer" being clear.

    3. Parity error (SC 05) may interrupt always; this is not conditional on
       "ion" being set or "ion_defer" being clear.

    4. All other devices (SC 06 and up) may interrupt if "ion" is set and
       "ion_defer" is clear.

   Qualification with "ion" is performed by "calc_int", except for case 2, which
   is qualified by the MP abort handler above (because qualification occurs on
   the MP card, rather than in the CPU).  Therefore, we need only qualify by
   "ion_defer" here.

   At instruction fetch time, a pending interrupt request will be deferred if
   the previous instruction was a JMP indirect, JSB indirect, STC, CLC, STF,
   CLF, or was executing from an interrupt trap cell. In addition, the following
   instructions will cause deferral on the 1000 series: SFS, SFC, JRS, DJP, DJS,
   SJP, SJS, UJP, and UJS.

   On the HP 1000, the request is always deferred until after the current
   instruction completes.  On the 21xx, the request is deferred unless the
   current instruction is an MRG instruction other than JMP or JMP,I or JSB,I.
   Note that for the 21xx, SFS and SFC are not included in the deferral
   criteria.


   When a status other than SCPE_OK is returned from an instruction executor or
   event service routine, the instruction execution loop exits into the
   instruction postlude.  The set of debug trace flags is restored if it had
   been changed by an active execution trace or idle trace suppression.  This
   ensures that the simulation stop does not exit with the flags set improperly.
   If the simulation stopped for a programmed halt, the 21xx binary loader area
   is protected in case it had been unprotected to run the loader.  The DMS
   status and violation registers and the program counter queue pointer are
   updated to present the proper values to the user interface.  The default
   breakpoint type is updated to reflect the current MEU state (disabled, system
   map enabled, or user map enabled).  Finally, the P register is reset if the
   current instruction is to be reexecuted on reentry (for example, on an
   unimplemented instruction stop).


   Implementation notes:

    1. While the Microsoft VC++ "setjmp" documentation says, "All variables
       (except register variables) accessible to the routine receiving control
       contain the values they had when longjmp was called," the ISO C99
       standard says, "All accessible objects have values...as of the time the
       longjmp function was called, except that the values of objects of
       automatic storage duration that are local to the function containing the
       invocation of the corresponding setjmp macro that do not have
       volatile-qualified type and have been changed between the setjmp
       invocation and longjmp call are indeterminate."

       Therefore, the "exec_save" and "idle_save" variables are marked static
       to ensure that they are reloaded after a longjmp caused by a memory
       protect abort (they are not marked volatile to save redundant reloads
       within the instruction execution loop).  Also, "status" and "exec_test"
       are set before reentering the instruction loop after an abort.  This is
       done solely to reassure the compiler that the values are not clobbered,
       even though in both cases the values are reestablished after an abort
       before they are used.

    2. The protected lower bound address for the JSB instruction depends on the
       W5 jumper setting.  If W5 is in, then the lower bound is 2, allowing JSBs
       to the A and B registers.  If W5 is out, then the lower bound is 0, just
       as with JMP.  The protected lower bound is set during the instruction
       prelude and tested during JSB address validation.

    3. The -P switch is removed from the set of command line switches to ensure
       that internal calls to the device reset routines are not interpreted as
       "power-on" resets.

    4. The "longjmp" handler is used both for MP and MEM violations.  The MEV
       flip-flop will be clear for the former and set for the latter.  The MEV
       violation register will be updated by "dms_upd_vr" only if the call is
       NOT for an MEM violation; if it is, then the register has already been
       set and should not be disturbed.

    5. For an MP/MEM abort, the violation address is passed via "longjmp" to
       enable the MEM violation register to be updated.  The "longjmp" routine
       will not pass a value of 0; it is converted internally to 1.  This is OK,
       because only the page number of the address value is used, and locations
       0 and 1 are both on page 0.

    6. A CPU freeze is simulated by skipping instruction execution during the
       current loop cycle.

    7. If both DMA channels have SRQ asserted, priority is simulated by skipping
       the channel 2 cycle if channel 1's SRQ is still asserted at the end of
       its cycle.  If it is not, then channel 2 steals the next cycle from the
       CPU.

    8. The 1000 M-Series allows some CPU processing concurrently with
       continuous DMA cycles, whereas all other CPUs freeze.  The processor
       freezes if an I/O cycle is attempted, including an interrupt
       acknowledgement.  Because some microcode extensions (e.g., Access IOP,
       RTE-6/VM OS) perform I/O cycles, advance detection of I/O cycles is
       difficult.  Therefore, we freeze all processing for the M-Series as well.

    9. EXEC tracing is active when exec_save is non-zero.  "exec_save" saves the
       current state of the trace flags when an EXEC trace match occurs.  For
       this to happen, at least TRACE_EXEC must be set, so "exec_save" will be
       set non-zero when a match is active.

   10. The execution trace (TRACE_EXEC) match test is performed in two parts to
       display the register values both before and after the instruction
       execution.  Consequently, the enable test is done before the register
       trace, and the disable test is done after.

   11. A simulation stop bypass is inactivated after the first instruction
       execution by the expedient of setting the stop inhibition mask to the
       execution status result.  This must be SCPE_OK (i.e., zero) for execution
       to continue, which removes the stop inhibition.  If a non-zero status
       value is returned, then the inhibition mask will be set improperly, but
       that is irrelevant, as execution will stop in this case.
*/

t_stat sim_instr (void)
{
static const char *const register_values [] = {         /* register values, indexed by EOI concatenation */
    "e o i",
    "e o I",
    "e O i",
    "e O I",
    "E o i",
    "E o I",
    "E O i",
    "E O I"
    };

static const char mp_value [] = {                       /* memory protection value, indexed by mp_control */
    '-',
    'P'
    };

static const char *const register_formats [] = {        /* CPU register formats, indexed by is_1000 */
    REGA_FORMAT "  A %06o, B %06o, ",                   /*   is_1000 = FALSE format */
    REGA_FORMAT "  A %06o, B %06o, X %06o, Y %06o, "    /*   is_1000 = TRUE  format */
    };

static const char *const mp_mem_formats [] = {                  /* MP/MEM register formats, indexed by is_1000 */
    REGB_FORMAT "  MPF %06o, MPV %06o\n",                       /*   is_1000 = FALSE format */
    REGB_FORMAT "  MPF %06o, MPV %06o, MES %06o, MEV %06o\n"    /*   is_1000 = TRUE  format */
    };

static uint32 exec_save;                                /* the trace flag settings saved by an EXEC match */
static uint32 idle_save;                                /* the trace flag settings saved by an idle match */
int    abortval;
uint32 intrq, dmarq;                                    /* set after setjmp */
t_bool exec_test;                                       /* set after setjmp */
t_bool iotrap;                                          /* set after setjmp */
t_stat status;                                          /* set after setjmp */


/* Instruction prelude */

if (sim_switches & SWMASK ('B'))                        /* if a simulation stop bypass was requested */
    cpu_ss_inhibit = SS_INHIBIT;                        /*   then inhibit stops for the first instruction */
else                                                    /* otherwise */
    cpu_ss_inhibit = SCPE_OK;                           /*   clear the inhibition mask */

sim_switches &= ~SWMASK ('P');                          /* clear the power-on switch to prevent interference */

if (hp_device_conflict ())                              /* if device assignment is inconsistent */
    return SCPE_STOP;                                   /*   then inhibit execution */

io_initialize ();                                       /* set up the I/O data structures */
cpu_ioerr_uptr = NULL;                                  /*   and clear the I/O error unit pointer */

exec_save = 0;                                          /* clear the EXEC match */
idle_save = 0;                                          /*   and idle match trace flags */

jsb_plb = (mp_unit.flags & UNIT_MP_JSB) ? 0 : 2;        /* set the protected lower bound for JSB */

mp_mem_changed = TRUE;                                  /* request an initial MP/MEM trace */


/* Memory Protect abort processor */

abortval = setjmp (save_env);                           /* set abort hdlr */

if (abortval) {                                         /* memory protect abort? */
    dms_upd_vr (abortval);                              /* update violation register (if not MEV) */

    if (ion)                                            /* interrupt system on? */
        protio (dibs [PRO], ioENF, 0);                  /* set flag */
    }

dmarq = calc_dma ();                                    /* initial recalc of DMA masks */
intrq = calc_int ();                                    /* initial recalc of interrupts */

status = SCPE_OK;                                       /* clear the status */
exec_test = FALSE;                                      /*   and the execution test flag */


/* Instruction execution loop */

do {                                                    /* execute instructions until halted */
    err_PC = PR;                                        /* save P for error recovery */

    if (sim_interval <= 0) {                            /* event timeout? */
        status = sim_process_event ();                  /* process event service */

        if (status != SCPE_OK)                          /* service failed? */
            break;                                      /* stop execution */

        dmarq = calc_dma ();                            /* recalc DMA reqs */
        intrq = calc_int ();                            /* recalc interrupts */
        }


    if (dmarq) {                                        /* if a DMA service request is pending */
        if (dmarq & DMA_1_REQ) {                        /*   then if the request is for channel 1 */
            status = dma_cycle (ch1, DMA_Channel_1);    /*     then do one DMA cycle using the port A map */

            if (status == SCPE_OK)                      /* cycle OK? */
                dmarq = calc_dma ();                    /* recalc DMA requests */
            else
                break;                                  /* cycle failed, so stop */
            }

        if ((dmarq & (DMA_1_REQ | DMA_2_REQ)) == DMA_2_REQ) {   /* DMA channel 1 idle and channel 2 request? */
            status = dma_cycle (ch2, DMA_Channel_2);            /* do one DMA cycle using port B map */

            if (status == SCPE_OK)                      /* cycle OK? */
                dmarq = calc_dma ();                    /* recalc DMA requests */
            else
                break;                                  /* cycle failed, so stop */
            }

        if (dmarq)                                      /* DMA request still pending? */
            continue;                                   /* service it before instruction execution */

        intrq = calc_int ();                            /* recalc interrupts */
        }

    if (intrq && ion_defer)                             /* if an interrupt is pending but deferred */
        ion_defer = check_deferral (intrq);             /*   then check that the deferral is applicable */


    if (intrq && !ion_defer) {                          /* if an interrupt request is pending and not deferred */
        if (sim_brk_summ &&                             /* any breakpoints? */
            sim_brk_test (intrq, SWMASK ('E') |         /* unconditional or right type for DMS? */
              (dms_enb ? SWMASK ('S') : SWMASK ('N')))) {
            status = STOP_BRKPNT;                       /* stop simulation */
            break;
            }

        CIR = (HP_WORD) intrq;                          /* save int addr in CIR */
        intrq = 0;                                      /* clear request */
        ion_defer = TRUE;                               /* defer interrupts */
        iotrap = TRUE;                                  /* mark as I/O trap cell instr */

        if (idle_save != 0) {                           /* if idle loop tracing is suppressed */
            cpu_dev.dctrl = idle_save;                  /*   then restore the saved trace flag set */
            idle_save = 0;                              /*     and indicate that we are out of the idle loop */
            }

        if (TRACING (cpu_dev, TRACE_INSTR)) {
            meu_map (PR, dms_ump, NOPROT);              /* reset the indicator and page */

            tprintf (cpu_dev, cpu_dev.dctrl,
                     DMS_FORMAT "interrupt\n",
                     meu_indicator, meu_page,
                     PR, CIR);
            }

        if (dms_enb)                                    /* dms enabled? */
            dms_sr = dms_sr | MST_ENBI;                 /* set in status */
        else                                            /* not enabled */
            dms_sr = dms_sr & ~MST_ENBI;                /* clear in status */

        if (dms_ump) {                                  /* user map enabled at interrupt? */
            dms_sr = dms_sr | MST_UMPI;                 /* set in status */
            dms_ump = SMAP;                             /* switch to system map */
            }
        else                                            /* system map enabled at interrupt */
            dms_sr = dms_sr & ~MST_UMPI;                /* clear in status */

        mp_mem_changed = TRUE;                          /* set the MP/MEM registers changed flag */

        IR = ReadF (CIR);                               /* get trap cell instruction */

        io_dispatch (CIR, ioIAK, IR);                   /* acknowledge interrupt */

        if (CIR != PRO)                                 /* not MP interrupt? */
            protio (dibs [CIR], ioIAK, IR);             /* send IAK for device to MP too */
        }

    else {                                              /* normal instruction */
        iotrap = FALSE;                                 /* not a trap cell instruction */

        if (sim_brk_summ &&                             /* any breakpoints? */
            sim_brk_test (PR, SWMASK ('E') |            /* unconditional or */
                              (dms_enb ?                /*   correct type for DMS state? */
                                (dms_ump ?
                                  SWMASK ('U') : SWMASK ('S')) :
                                SWMASK ('N')))) {
            status = STOP_BRKPNT;                       /* stop simulation */
            break;
            }

        if (mp_evrff)                                   /* violation register enabled */
            mp_viol = PR;                               /* update with current P */

        IR = ReadF (PR);                                /* fetch instr */
        PR = (PR + 1) & VAMASK;
        ion_defer = FALSE;
        }


    if (TRACING (cpu_dev, TRACE_EXEC | TRACE_REG)) {    /* if execution or register tracing is enabled */
        if (cpu_dev.dctrl & TRACE_EXEC)                 /*   then if tracing execution */
            exec_test = (IR & exec_mask) == exec_match; /*     then the execution test succeeds if */
                                                        /*       the next instruction matches the test criteria */

        if (cpu_dev.dctrl & TRACE_EXEC                  /* if execution tracing is enabled */
          && exec_save == 0                             /*   and is currently inactive */
          && exec_test) {                               /*     and the matching test succeeds */
            exec_save = cpu_dev.dctrl;                  /*       then save the current trace flag set */
            cpu_dev.dctrl |= TRACE_ALL;                 /*         and turn on full tracing */
            }

        if (cpu_dev.dctrl & TRACE_REG) {                /* if register tracing is enabled */
            hp_trace (&cpu_dev, TRACE_REG,              /*   then output the working registers */
                      register_formats [is_1000],
                      mp_value [mp_control],
                      dms_sr & MST_FENCE,
                      SR, AR, BR, XR, YR);

            fputs (register_values [E << 2 | O << 1 | ion], sim_deb);
            fputc ('\n', sim_deb);

            if (mp_mem_changed) {                       /* if the MP/MEM registers have been altered */
                hp_trace (&cpu_dev, TRACE_REG,          /*   then output the register values */
                          mp_mem_formats [is_1000],
                          mp_value [mp_control],
                          mp_fence, mp_viol, dms_sr, dms_vr);

                mp_mem_changed = FALSE;                 /* clear the MP/MEM registers changed flag */
                }
            }

        if (cpu_dev.dctrl & TRACE_EXEC                          /* if execution tracing is enabled */
          && exec_save != 0                                     /*   and is currently active */
          && ! exec_test) {                                     /*     and the matching test fails */
            cpu_dev.dctrl = exec_save;                          /*       then restore the saved debug flag set */
            exec_save = 0;                                      /*         and indicate that tracing is disabled */

            hp_trace (&cpu_dev, TRACE_EXEC, EXEC_FORMAT "\n");  /* add a separator to the trace log */
            }
        }

    if (TRACING (cpu_dev, TRACE_INSTR)) {               /* if instruction tracing is enabled */
        hp_trace (&cpu_dev, TRACE_INSTR,                /*   then output the address and opcode */
                  DMS_FORMAT,
                  meu_indicator, meu_page,
                  MR, IR);

        sim_eval [0] = IR;                              /* save the (first) instruction word in the eval array */

        if (fprint_cpu (sim_deb, MR, sim_eval, 0, CPU_Trace) > SCPE_OK) /* print the mnemonic; if that fails */
            fprint_val (sim_deb, sim_eval [0], cpu_dev.dradix,          /*   then print the numeric */
                        cpu_dev.dwidth, PV_RZRO);                       /*     value again */

        fputc ('\n', sim_deb);                          /* end the trace with a newline */
        }


    sim_interval = sim_interval - 1;                    /* count the instruction */

    status = machine_instruction (IR, iotrap, intrq,    /* execute one machine instruction */
                                  &idle_save);

    if (status == NOTE_IOG) {                           /* I/O instr exec? */
        dmarq = calc_dma ();                            /* recalc DMA masks */
        intrq = calc_int ();                            /* recalc interrupts */
        status = SCPE_OK;                               /* continue */
        }

    else if (status == NOTE_INDINT) {                   /* intr pend during indir? */
        PR = err_PC;                                    /* back out of inst */
        status = SCPE_OK;                               /* continue */
        }

    cpu_ss_inhibit = status;                            /* clear the simulation stop inhibition mask */
    }
while (status == SCPE_OK);                              /* loop until halted */


/* Instruction postlude */

if (intrq && ion_defer)                                 /* if an interrupt is pending but deferred */
    ion_defer = check_deferral (intrq);                 /*   then check that the deferral is applicable */

if (exec_save != 0) {                                   /* if EXEC tracing is active */
    cpu_dev.dctrl = exec_save;                          /*   then restore the saved trace flag set */
    hp_trace (&cpu_dev, TRACE_EXEC, EXEC_FORMAT "\n");  /*     and add a separator to the trace log */
    }

else if (idle_save != 0)                                /* otherwise if idle tracing is suppressed */
    cpu_dev.dctrl = idle_save;                          /*   then restore the saved trace flag set */

saved_MR = MR;                                          /* save for T cmd update */

if (status == STOP_HALT)                                /* programmed halt? */
    set_loader (NULL, FALSE, NULL, NULL);               /* disable loader (after T is read) */
else if (status <= STOP_RERUN)                          /* simulation stop */
    PR = err_PC;                                        /* back out instruction */

dms_upd_sr ();                                          /* update dms_sr */
dms_upd_vr (MR);                                        /* update dms_vr */
pcq_r->qptr = pcq_p;                                    /* update pc q ptr */

if (dms_enb)                                            /* DMS enabled? */
    if (dms_ump)                                        /* set default */
        sim_brk_dflt = SWMASK ('U');                    /*   breakpoint type */
    else                                                /*     to current */
        sim_brk_dflt = SWMASK ('S');                    /*       map mode */
else                                                    /* DMS disabled */
    sim_brk_dflt = SWMASK ('N');                        /* set breakpoint type to non-DMS */

tprintf (cpu_dev, cpu_dev.dctrl,
         DMS_FORMAT "simulation stop: %s\n",
         meu_indicator, meu_page,
         MR, TR, sim_error_text (status));

return status;                                          /* return status code */
}


/* VM command post-processor

   Update T register to contents of memory addressed by M register.


   Implementation notes:

    1. The T register must be changed only when M has changed.  Otherwise, if T
       is updated after every command, then T will be set to zero if M points
       into the protected loader area of the 21xx machines, e.g., after a HLT
       instruction in the loader reenables loader protection.
*/

void cpu_post_cmd (t_bool from_scp)
{
if (MR != saved_MR) {                                   /* M changed since last update? */
    saved_MR = MR;
    TR = mem_fast_read (MR, dms_ump);                   /* sync T with new M */
    }
return;
}



/* CPU global utility routines */


/* Install an initial binary loader into memory.

   This routine copies the initial binary loader contained in "rom" to the last
   64 words of main memory, limited by a 32K memory size.  If "dev" contains a
   select code of an I/O interface (i.e., select code 10 or above), this routine
   will configure the I/O instructions in the IBL to the supplied select code.
   On exit, P will be set to point at the first word of the loader, and S will
   be altered as directed by the "sr_clear" and "sr_set" masks.

   If I/O configuration is requested, each instruction in the IBL is examined as
   it is copied to memory.  If the instruction is a non-HLT I/O instruction
   referencing a select code >= 10, the select code will be reset by subtracting
   10 and adding the value of the select code supplied in the "dev" parameter.
   This permits configuration of IBLs that address two- or three-card
   interfaces.  Passing a "dev" value of 0 will inhibit configuration.

   As an example, passing a "dev" value of 24 octal will alter these
   instructions as follows:

         IBL      Configured
     Instruction  Instruction  Note
     -----------  -----------  ------------------------------
       OTA 10       OTA 24     Normal configuration
       LIA 11       LIA 25     Second card configuration
       STC  6       STC  6     DCPC configuration not changed
       HLT 11       HLT 11     Halt instruction not changed

   If configuration is performed, the routine will also alter the next-to-last
   word unconditionally as above.  This word is assumed to contain a DCPC
   control word; it is configured to reference the supplied select code.

   Finally, the routine sets the last word to the two's-complement of the
   starting address of the IBL.  This value may be used by the loader to check
   that it will not be overwritten by loaded data.
*/

void cpu_ibl (const BOOT_ROM rom, int32 dev, HP_WORD sr_clear, HP_WORD sr_set)
{
int32 i;
MEMORY_WORD wd;

set_loader (NULL, TRUE, NULL, NULL);                   /* enable loader (ignore errors) */

PR = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK;              /* start at mem top */

for (i = 0; i < IBL_LNT; i++) {                         /* copy bootstrap */
    wd = rom [i];                                       /* get word */

    if (dev >= VARDEV                                   /* if reconfiguration is desired */
      && (wd & I_NMRMASK) == I_IO                       /*   and this is an I/O instruction */
      && (wd & I_DEVMASK) >= VARDEV                     /*   and the referenced select code is >= 10B */
      && I_GETIOOP (wd) != soHLT)                       /*   and it's not a halt instruction */
        M [PR + i] = (wd + (dev - VARDEV)) & DMASK;     /*     then reconfigure the select code */

    else                                                /* otherwise */
        M [PR + i] = wd;                                /*   copy the instruction verbatim */
    }

if (dev >= VARDEV)                                      /* if reconfiguration was performed */
    M [PR + IBL_DPC] = M [PR + IBL_DPC] + dev - VARDEV  /*   then patch the DMA control word */
                         & DMASK;

M [PR + IBL_END] = (~PR + 1) & DMASK;                   /* fill in start of boot */

SR = (SR & sr_clear) | sr_set;                          /* modify the S register as indicated */

return;
}


/* Execute an I/O instruction.

   If memory protect is enabled, and the instruction is not in a trap cell, then
   HLT instructions are illegal and will cause a memory protect violation.  If
   jumper W7 (SEL1) is in, then all other I/O instructions are legal; if W7 is
   out, then only I/O instructions to select code 1 are legal, and I/O to other
   select codes will cause a violation.

   If the instruction is allowed, then the I/O signal corresponding to the
   instruction is determined, and the state of the interrupt deferral flag is
   set.  The signal is then dispatched to the device simulator indicated by the
   target select code.  The return value is split into status and data values,
   with the latter containing the SKF signal state or data to be returned in the
   A or B registers.


   Implementation notes:

    1. If the H/C (hold/clear flag) bit is set, then the ioCLF signal is added
       to the base signal set derived from the I/O instruction.

    2. ioNONE is dispatched for HLT instructions because although HLT does not
       assert any backplane signals, the H/C bit may be set.  If it is, then the
       result will be to dispatch ioCLF.

    3. Device simulators return either ioSKF or ioNONE in response to an SFC or
       SFS signal.  ioSKF means that the instruction should skip.  Because
       device simulators return the "data" parameter value by default, we
       initialize that parameter to ioNONE to ensure that a simulator that does
       not implement SFC or SFS does not skip, which is the correct action for
       an interface that does not drive the SKF signal.

    4. STF/CLF and STC/CLC share sub-opcode values and must be further decoded
       by the state of instruction register bits 9 and 11, respectively.

    5. We return NOTE_IOG for normal status instead of SCPE_OK to request that
       interrupts be recalculated at the end of the instruction (execution of
       the I/O group instructions can change the interrupt priority chain).  We
       do this in preference to calling the recalculation routines directly, as
       some extended firmware instructions call this routine multiple times, and
       there is no point in recalculating until all calls are complete.
*/

t_stat cpu_iog (HP_WORD IR, t_bool iotrap)
{
/* Translation for I/O subopcodes:            soHLT, soFLG, soSFC, soSFS, soMIX, soLIX, soOTX, soCTL */
static const IOSIGNAL generate_signal [] = { ioNONE, ioSTF, ioSFC, ioSFS, ioIOI, ioIOI, ioIOO, ioSTC };

const uint32 dev = IR & I_DEVMASK;                      /* device select code */
const uint32 sop = I_GETIOOP (IR);                      /* I/O subopcode */
const uint32 ab  = (IR & I_AB ? 1 : 0);                 /* A/B register selector */
uint32  ioreturn;
t_stat  iostat;
IOCYCLE signal_set;
HP_WORD iodata = (HP_WORD) ioNONE;                      /* initialize for SKF test */

if (mp_control && !iotrap                               /* if MP is enabled and the instruction is not in trap cell */
  && (sop == soHLT                                      /*   and it is a HLT */
  || dev != OVF && (mp_unit.flags & UNIT_MP_SEL1))) {   /*   or does not address SC 01 and SEL1 is out */
        if (sop == soLIX)                               /*     then an MP violation occurs; if it is an LIA/B */
            ABREG [ab] = 0;                             /*       then the register is written before the abort */

        MP_ABORT (err_PC);                              /* MP abort */
        }

signal_set = generate_signal [sop];                     /* generate I/O signal from instruction */
ion_defer = defer_tab [sop];                            /* defer depending on instruction */

if (sop == soOTX)                                       /* OTA/B instruction? */
    iodata = ABREG [ab];                                /* pass A/B register value */

else if (sop == soCTL && IR & I_CTL)                    /* CLC instruction? */
    signal_set = ioCLC;                                 /* change STC to CLC signal */

if (IR & I_HC)                                          /* if the H/C bit is set */
    if (sop == soFLG)                                   /*   then if the instruction is STF or CLF */
        signal_set = ioCLF;                             /*     then change the ioSTF signal to ioCLF */
    else                                                /*   otherwise it's a non-flag instruction */
        signal_set |= ioCLF;                            /*     so add ioCLF to the instruction-specific signal */

ioreturn = io_dispatch (dev, signal_set, iodata);       /* dispatch the I/O signals */

iostat = IOSTATUS (ioreturn);                           /* extract status */
iodata = IODATA (ioreturn);                             /* extract return data value */

if (iostat == SCPE_OK) {                                /* if instruction execution succeeded */
    if ((sop == soSFC || sop == soSFS)                  /*   then if it is an SFC or SFS */
      && (IOSIGNAL) iodata == ioSKF)                    /*     and SKF is asserted */
        PR = PR + 1 & LA_MASK;                          /*       then bump P to skip then next instruction */

    else if (sop == soLIX)                              /*   otherwise if is it an LIA or LIB */
        ABREG [ab] = iodata;                            /*     then load the returned data */

    else if (sop == soMIX)                              /*   otherwise if it is an MIA or MIB */
        ABREG [ab] = ABREG [ab] | iodata;               /*     then merge the returned data */

    else if (sop == soHLT)                              /*   otherwise if it is a HLT */
        return STOP_HALT;                               /*     then stop the simulator */

    return NOTE_IOG;                                    /* request recalculation of interrupts */
    }

else                                                    /* otherwise the execution failed */
    return iostat;                                      /*   so return the failure status */
}


/* Calculate interrupt requests.

   The interrupt request (IRQ) of the highest-priority device for which all
   higher-priority PRL bits are set is granted.  That is, there must be an
   unbroken chain of priority to a device requesting an interrupt for that
   request to be granted.

   A device sets its IRQ bit to request an interrupt, and it clears its PRL bit
   to prevent lower-priority devices from interrupting.  IRQ is cleared by an
   interrupt acknowledge (IAK) signal.  PRL generally remains low while a
   device's interrupt service routine is executing to prevent preemption.

   IRQ and PRL indicate one of four possible states for a device:

     IRQ  PRL  Device state
     ---  ---  ----------------------
      0    1   Not interrupting
      1    0   Interrupt requested
      0    0   Interrupt acknowledged
      1    1   (not allowed)

   Note that PRL must be dropped when requesting an interrupt (IRQ set).  This
   is a hardware requirement of the 1000 series.  The IRQ lines from the
   backplane are not priority encoded.  Instead, the PRL chain expresses the
   priority by allowing only one IRQ line to be active at a time.  This allows a
   simple pull-down encoding of the CIR inputs.

   The end of priority chain is marked by the highest-priority (lowest-order)
   bit that is clear.  The device corresponding to that bit is the only device
   that may interrupt (a higher priority device that had IRQ set would also have
   had PRL set, which is a state violation).  We calculate a priority mask by
   ANDing the complement of the PRL bits with an increment of the PRL bits.
   Only the lowest-order bit will differ.  For example:

     dev_prl     :  ...1 1 0 1 1 0 1 1 1 1 1 1   (PRL denied for SC 06 and 11)

     dev_prl + 1 :  ...1 1 0 1 1 1 0 0 0 0 0 0
    ~dev_prl     :  ...0 0 1 0 0 1 0 0 0 0 0 0
     ANDed value :  ...0 0 0 0 0 1 0 0 0 0 0 0   (break is at SC 06)

   The interrupt requests are then ANDed with the priority mask to determine if
   a request is pending:

     pri mask    :  ...0 0 0 0 0 1 0 0 0 0 0 0   (allowed interrupt source)
     dev_irq     :  ...0 0 1 0 0 1 0 0 0 0 0 0   (devices requesting interrupts)
     ANDed value :  ...0 0 0 0 0 1 0 0 0 0 0 0   (request to grant)

   The select code corresponding to the granted request is then returned to the
   caller.

   If ION is clear, only power fail (SC 04) and parity error (SC 05) are
   eligible to interrupt (memory protect shares SC 05, but qualification occurs
   in the MP abort handler, so if SC 05 is interrupting when ION is clear, it
   must be a parity error interrupt).
*/

uint32 calc_int (void)
{
uint32 sc, pri_mask [2], req_grant [2];

pri_mask  [0] = ~dev_prl [0] & (dev_prl [0] + 1);       /* calculate lower priority mask */
req_grant [0] = pri_mask [0] & dev_irq [0];             /* calculate lower request to grant */

if (ion)                                                    /* interrupt system on? */
    if ((req_grant [0] == 0) && (pri_mask [0] == 0)) {      /* no requests in lower set and PRL unbroken? */
        pri_mask  [1] = ~dev_prl [1] & (dev_prl [1] + 1);   /* calculate upper priority mask */
        req_grant [1] = pri_mask [1] & dev_irq [1];         /* calculate upper request to grant */
        }
    else                                                /* lower set has request */
        req_grant [1] = 0;                              /* no grants to upper set */

else {                                                  /* interrupt system off */
    req_grant [0] = req_grant [0] &                     /* only PF and PE can interrupt */
                    (BIT_M (PWR) | BIT_M (PRO));
    req_grant [1] = 0;
    }

if (req_grant [0])                                      /* device in lower half? */
    for (sc = 0; sc <= 31; sc++)                        /* determine interrupting select code */
        if (req_grant [0] & LSB)                        /* grant this request? */
            return sc;                                  /* return this select code */
        else                                            /* not this one */
            req_grant [0] = req_grant [0] >> 1;         /* position next request */

else if (req_grant [1])                                 /* device in upper half */
    for (sc = 32; sc <= 63; sc++)                       /* determine interrupting select code */
        if (req_grant [1] & LSB)                        /* grant this request? */
            return sc;                                  /* return this select code */
        else                                            /* not this one */
            req_grant [1] = req_grant [1] >> 1;         /* position next request */

return 0;                                               /* no interrupt granted */
}


/* Resolve a indirect address.

   This routine resolves a supplied memory address into a direct address by
   following an indirect chain, if any.  On entry, "MA" contains the address to
   resolve, and "irq" is non-zero if an interrupt is currently pending.  On
   exit, the variable pointed to by "addr" is set to the direct address, and
   SCPE_OK is returned.  If an interrupt is pending and permitted, NOTE_INDINT
   is returned to abort the instruction, and the variable indicated by "addr" is
   unchanged.

   Logical memory addresses are 15 bits wide, providing direct access to a 32K
   logical address space.  Addresses may also be indirect, with bit 15 (the MSB)
   serving as the direct/indirect indicator.  An indirect address may point at
   either a direct or indirect address.  In the latter case, the chain is
   followed until a direct address is obtained.

   Indirect addressing has implications for interrupt handling.  Normally,
   interrupts are checked at each level of indirection, and if one is pending,
   the CPU will abort execution of the instruction and then service the
   interrupt.  On return from the interrupt handler, the instruction will be
   restarted.

   However, the JMP indirect and JSB indirect instructions hold off interrupts
   until completion of the instruction, including complete resolution of the
   indirect chain.  If the chain is unresolvable (i.e., it points to itself, as
   in the instruction sequence JMP *+1,I and DEF *,I), then interrupts are held
   off forever.

   To prevent a user program from freezing a protected OS with an infinite
   indirect chain, and to permit real-time interrupts to be handled while
   resolving a long indirect chain, the Memory Protect accessory counts indirect
   levels during address resolution and will reenable interrupt recognition
   after the third level.  Operating systems that run without MP installed are
   subject to freezing as above, but those employing MP will be able to regain
   control from an infinite indirect chain.

   In simulation, the SET CPU INDIR=<limit> command sets the maximum number of
   levels; the default is 16.  If the level is exceeded during address
   resolution, the simulator will stop.  The maximum limit is 32768, which is
   the maximum possible address chain without an infinite loop, but an indirect
   chain over a few levels deep almost certainly represents a programming error.


   Implementation notes:

    1. Virtually all valid indirect references are one level deep, so we
       optimize for this case.  Also, we protect against entry with a direct
       address by simply returning the address, but the overhead can be saved by
       calling this routine only for indirect addresses.

    2. The 12892B Memory Protect accessory jumper W6 ("INT") controls whether
       held off pending interrupts are serviced immediately (jumper removed) or
       after three levels of indirection (jumper installed).  If the jumper is
       removed, MP must be enabled (control flip-flop set) for the interrupt
       hold off to be overridden.

       The jumper state need not be checked here, however, because this routine
       can be entered with an interrupt pending ("irq" non-zero) only if
       "ion_defer" and "check_deferral" are both true.  If either is false, the
       pending interrupt would have been serviced before calling the instruction
       executor that is calling this routine to resolve its address.  For
       "check_deferral" to return TRUE, then the INT jumper must be installed or
       the MP control flip-flop must be clear.

    3. When employing the indirect counter, the hardware clears a pending
       interrupt deferral after the third level of indirection and aborts the
       instruction after the fourth.

    4. The JRS, DJP, DJS, SJP, SJS, UJP, and UJS instructions also hold off
       interrupts for three indirect levels, but they count levels internally
       and do not depend on the presence of the MP accessory to reenable
       interrupt recognition.  However, DMS requires MP, so simulation uses the
       MP indirect counter for these instructions as well.

    5. In hardware, it is possible to execute an instruction with an infinite
       indirect loop (e.g., JMP *+1,I and DEF *,I).  If MP is not installed,
       this freezes the CPU with interrupts disabled until HALT is pressed.  In
       simulation, the instruction executes until the indirect limit is reached,
       whereupon the simulator stops with "Indirect address loop" status.
       Modelling the hardware CPU freeze would be difficult, as the simulation
       console would have to be polled locally to watch for CTRL+E (the
       simulation equivalent of the CPU front panel HALT button).
*/

t_stat resolve (HP_WORD MA, HP_WORD *address, uint32 irq)
{
uint32 level;
t_bool pending;

if (MA & I_IA) {                                        /* if the address is indirect */
    MA = ReadW (MA & LA_MASK);                          /*   then follow the chain (first level) */

    if (MA & I_IA) {                                    /* if the address is still indirect */
        pending = (irq && !(mp_unit.flags & DEV_DIS));  /*   then permit a pending interrupt if MP is enabled */

        for (level = 2; MA & I_IA; level++) {           /* follow the chain from level 2 until the address resolves */
            if (level > indirect_limit)                 /* if the limit is exceeded */
                return STOP_INDIR;                      /*   then stop the simulator */

            else if (pending)                           /* otherwise if an interrupt is pending */
                if (level == 3)                         /*   then if this is the third level */
                    ion_defer = FALSE;                  /*     then reenable interrupts */
                else if (level == 4)                    /*   otherwise if this is the fourth level */
                    return NOTE_INDINT;                 /*     then service the interrupt now */

            MA = ReadW (MA & LA_MASK);                  /* follow the address chain */
            }
        }
    }

*address = MA;                                          /* return the direct address */
return SCPE_OK;                                         /*   and success status */
}



/* Memory global utility routines */


/* Read a word from memory.

   Read and return a word from memory at the indicated logical address.  On
   entry, "dptr" points to the DEVICE structure of the device requesting access,
   "classification" is the type of access requested, and "address" is the offset
   into the 32K logical address space implied by the classification.

   If memory expansion is enabled, the logical address is mapped into a physical
   memory location; the map used is determined by the access classification.
   The current map (user or system), alternate map (the map not currently
   selected), or an explicit map (system, user, DCPC port A, or port B) may be
   requested.  Read protection is enabled for current or alternate map access
   and disabled for the others.  If memory expansion is disabled or not present,
   the logical address directly accesses the first 32K of memory.

   The memory protect (MP) and memory expansion module (MEM) accessories provide
   a protected mode that guards against improper accesses by user programs.
   They may be enabled or disabled independently, although protection requires
   that both be enabled.  MEM checks that read protection rules on the target
   page are compatible with the access desired.  If the check fails, and MP is
   enabled, then the request is aborted.

   The 1000 family maps memory location 0 to the A-register and location 1 to
   the B-register.  CPU reads of these locations return the A- or B-register
   values, while DCPC reads access physical memory locations 0 and 1 instead.


   Implementation notes:

    1. A read beyond the limit of physical memory returns 0.  This is handled by
       allocating the maximum memory array and initializing memory beyond the
       defined limit to zero, so no special handling is needed here..

    2. A MEM read protection violation with MP enabled causes an MP abort
       instead of a normal return.
*/

HP_WORD mem_read (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address)
{
uint32  index, map;
HP_WORD protection;

switch (classification) {                               /* dispatch on the access classification */

    case Fetch:
    case Data:
    default:                                            /* needed to quiet the compiler's anxiety */
        map = dms_ump;                                  /* use the currently selected map (user or system) */
        protection = RDPROT;                            /*   and enable read protection */
        break;

    case Data_Alternate:
        map = dms_ump ^ MAP_LNT;                        /* use the alternate map (user or system) */
        protection = RDPROT;                            /*   and enable read protection */
        break;

    case Data_System:
        map = SMAP;                                     /* use the system map explicitly */
        protection = NOPROT;                            /*   without protection */
        break;

    case Data_User:
        map = UMAP;                                     /* use the user map explicitly */
        protection = NOPROT;                            /*   without protection */
        break;

    case DMA_Channel_1:
        map = PAMAP;                                    /* use the DCPC port A map */
        protection = NOPROT;                            /*   without protection */
        break;

    case DMA_Channel_2:
        map = PBMAP;                                    /* use the DCPC port B map */
        protection = NOPROT;                            /*   without protection */
        break;
    }                                                   /* all cases are handled */

MR = address;                                           /* save the logical memory address */
index = meu_map (address, map, protection);             /*   and translate to a physical address */

if (index <= 1 && map < PAMAP)                          /* if the A/B register is referenced */
    TR = ABREG [index];                                 /*   then return the selected register value */
else                                                    /* otherwise */
    TR = (HP_WORD) M [index];                           /*   return the physical memory value */

tpprintf (dptr, access [classification].debug_flag,
          DMS_FORMAT "  %s%s\n",
          meu_indicator, meu_page, MR, TR,
          access [classification].name,
          access [classification].debug_flag == TRACE_FETCH ? "" : " read");

return TR;
}


/* Write a word to memory.

   Write a word to memory at the indicated logical address.  On entry, "dptr"
   points to the DEVICE structure of the device requesting access,
   "classification" is the type of access requested, "address" is the offset
   into the 32K logical address space implied by the classification, and
   "value" is the value to write.

   If memory expansion is enabled, the logical address is mapped into a physical
   memory location; the map used is determined by the access classification.
   The current map (user or system), alternate map (the map not currently
   selected), or an explicit map (system, user, DCPC port A, or port B) may be
   requested.  Write protection is enabled for current or alternate map access
   and disabled for the others.  If memory expansion is disabled or not present,
   the logical address directly accesses the first 32K of memory.

   The memory protect (MP) and memory expansion module (MEM) accessories provide
   a protected mode that guards against improper accesses by user programs.
   They may be enabled or disabled independently, although protection requires
   that both be enabled.  MP checks that memory writes do not fall below the
   Memory Protect Fence Register (MPFR) value, and MEM checks that write
   protection rules on the target page are compatible with the access desired.
   If either check fails, and MP is enabled, then the request is aborted (so, to
   pass, a page must be writable AND the target must be above the MP fence).  In
   addition, a MEM write violation will occur if MP is enabled and the alternate
   map is selected, regardless of the page protection.

   The 1000 family maps memory location 0 to the A-register and location 1 to
   the B-register.  CPU writes to these locations store the values into the A or
   B register, while DCPC writes access physical memory locations 0 and 1
   instead.  MP uses a lower bound of 2 for memory writes, allowing unrestricted
   access to the A and B registers.


   Implementation notes:

    1. A write beyond the limit of physical memory is a no-operation.

    2. When the alternate map is enabled, writes are permitted only in the
       unprotected mode, regardless of page protections or the MP fence setting.
       This behavior is not mentioned in the MEM documentation, but it is tested by
       the MEM diagnostic and is evident from the MEM schematic.  Referring to
       Sheet 2 in the ERD, gates U125 and U127 provide this logic:

         WTV = MPCNDB * MAPON * (WPRO + ALTMAP)

       The ALTMAP signal is generated by the not-Q output of flip-flop U117,
       which toggles on control signal -CL3 assertion (generated by the MESP
       microorder) to select the alternate map.  Therefore, a write violation is
       indicated whenever a memory protect check occurs while the MEM is enabled
       and either the page is write-protected or the alternate map is selected.

       The hardware reference manuals that contain descriptions of those DMS
       instructions that write to the alternate map (e.g., MBI) say, "This
       instruction will always cause a MEM violation when executed in the
       protected mode and no bytes [or words] will be transferred."  However,
       they do not state that a write violation will be indicated, nor does the
       description of the write violation state that this is a potential cause.
*/

void mem_write (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address, HP_WORD value)
{
uint32  index, map;
HP_WORD protection;

switch (classification) {                               /* dispatch on the access classification */

    case Data:
    default:                                            /* needed to quiet the compiler's anxiety */
        map = dms_ump;                                  /* use the currently selected map (user or system) */
        protection = WRPROT;                            /*   and enable write protection */
        break;

    case Data_Alternate:
        map = dms_ump ^ MAP_LNT;                        /* use the alternate map (user or system) */
        protection = WRPROT;                            /*   and enable write protection */

        if (dms_enb)                                    /* if the MEM is enabled */
            dms_viol (address, MVI_WPR);                /*   then a violation always occurs if in protected mode */
        break;

    case Data_System:
        map = SMAP;                                     /* use the system map explicitly */
        protection = NOPROT;                            /*   without protection */
        break;

    case Data_User:
        map = UMAP;                                     /* use the user map explicitly */
        protection = NOPROT;                            /*   without protection */
        break;

    case DMA_Channel_1:
        map = PAMAP;                                    /* use the DCPC port A map */
        protection = NOPROT;                            /*   without protection */
        break;

    case DMA_Channel_2:
        map = PBMAP;                                    /* use the DCPC port B map */
        protection = NOPROT;                            /*   without protection */
        break;

    case Fetch:                                         /* instruction fetches */
        return;                                         /*   do not cause writes */

    }                                                   /* all cases are handled */

MR = address;                                           /* save the logical memory address */
index = meu_map (address, map, protection);             /*   and translate to a physical address */

if (protection != NOPROT && MP_TEST (address))          /* if protected and the MP check fails */
    MP_ABORT (address);                                 /*   then abort with an MP violation */

if (index <= 1 && map < PAMAP)                          /* if the A/B register is referenced */
    ABREG [index] = value;                              /*   then write the value to the selected register */

else if (index < fwanxm)                                /* otherwise if the location is within defined memory */
    M [index] = (MEMORY_WORD) value;                    /*   then write the value to memory */

TR = value;                                             /* save the value */

tpprintf (dptr, access [classification].debug_flag,
          DMS_FORMAT "  %s write\n",
          meu_indicator, meu_page, MR, TR,
          access [classification].name);

return;
}


/* Read a byte from memory.

   Read and return a byte from memory at the indicated logical address.  On
   entry, "dptr" points to the DEVICE structure of the device requesting access,
   "classification" is the type of access requested, and "byte_address" is the
   byte offset into the 32K logical address space implied by the classification.

   The 1000 is a word-oriented machine.  To permit byte accesses, a logical byte
   address is defined as two times the associated word address.  The LSB of the
   byte address designates the byte to access: 0 for the upper byte, and 1 for
   the lower byte.  As all 16 bits are used, byte addresses cannot be indirect.


   Implementation notes:

    1. Word buffering is not used to minimize memory reads, as the HP 1000
       microcode does a full word read for each byte accessed.
*/

uint8 mem_read_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address)
{
const HP_WORD word_address = byte_address >> 1;         /* the address of the word containing the byte */
HP_WORD word;

word = mem_read (dptr, classification, word_address);   /* read the addressed word */

if (byte_address & LSB)                                 /* if the byte address is odd */
    return LOWER_BYTE (word);                           /*   then return the right-hand byte */
else                                                    /* otherwise */
    return UPPER_BYTE (word);                           /*   return the left-hand byte */
}


/* Write a byte to memory.

   Write a byte to memory at the indicated logical address.  On entry, "dptr"
   points to the DEVICE structure of the device requesting access,
   "classification" is the type of access requested, "byte_address" is the
   byte offset into the 32K logical address space implied by the classification,
   and "value" is the value to write.

   The 1000 is a word-oriented machine.  To permit byte accesses, a logical byte
   address is defined as two times the associated word address.  The LSB of the
   byte address designates the byte to access: 0 for the upper byte, and 1 for
   the lower byte.  As all 16 bits are used, byte addresses cannot be indirect.


   Implementation notes:

    1. Word buffering is not used to minimize memory writes, as the HP 1000
       base-set microcode does a full word write for each byte accessed.  (The
       DMS byte instructions, e.g., MBI, do full-word accesses for each pair of
       bytes, but that is to minimize the number of map switches.)
*/

void mem_write_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address, uint8 value)
{
const HP_WORD word_address = byte_address >> 1;         /* the address of the word containing the byte */
HP_WORD word;

word = mem_read (dptr, classification, word_address);   /* read the addressed word */

if (byte_address & LSB)                                 /* if the byte address is odd */
    word = REPLACE_LOWER (word, value);                 /*   then replace the right-hand byte */
else                                                    /* otherwise */
    word = REPLACE_UPPER (word, value);                 /*   replace the left-hand byte */

mem_write (dptr, classification, word_address, word);   /* write the updated word back */

return;
}


/* Fast read from memory.

   This routine reads and returns a word from memory at the indicated logical
   address using the specified map.  Memory protection is not used, and tracing
   is not available.

   This routine is used when fast, unchecked access to mapped memory is
   required.
*/

HP_WORD mem_fast_read (HP_WORD address, uint32 map)
{
return mem_examine (meu_map (address, map, NOPROT));    /* return the value at the translated address */
}


/* Examine a physical memory address.

   This routine reads and returns a word from memory at the indicated physical
   address.  If the address lies outside of allocated memory, a zero value is
   returned.  There are no protections or error indications.
*/

HP_WORD mem_examine (uint32 address)
{
if (address <= 1)                                       /* if the address is 0 or 1 */
    return ABREG [address];                             /*   then return the A or B register value */

else if (address < PASIZE)                              /* otherwise if the address is within allocated memory */
    return (HP_WORD) M [address];                       /*   then return the memory value */

else                                                    /* otherwise the access is outside of memory */
    return 0;                                           /*   which reads as zero */
}


/* Deposit into a physical memory address.

   This routine writes a word into memory at the indicated physical address.  If
   the address lies outside of defined memory, the write is ignored.  There are
   no protections or error indications.
*/

void mem_deposit (uint32 address, HP_WORD value)
{
if (address <= 1)                                       /* if the address is 0 or 1 */
    ABREG [address] = value & DV_MASK;                  /*   then store into the A or B register */

else if (address < fwanxm)                              /* otherwise if the address is within defined memory */
    M [address] = (MEMORY_WORD) value & DV_MASK;        /*   then store the value */

return;
}



/* Memory Expansion Unit global utility routines */


/* DMS read and write map registers */

uint16 dms_rmap (uint32 mapi)
{
return dms_map [mapi & MAP_MASK] & ~MAP_RSVD;
}

void dms_wmap (uint32 mapi, uint32 dat)
{
dms_map [mapi & MAP_MASK] = (uint16) (dat & ~MAP_RSVD);
return;
}


/* Process a MEM violation.

   A MEM violation will report the cause in the violation register.  This occurs
   even if the MEM is not in the protected mode (i.e., MP is not enabled).  If
   MP is enabled, an MP abort is taken with the MEV flip-flop set.  Otherwise,
   we return to the caller.
*/

void dms_viol (uint32 va, HP_WORD st)
{
dms_vr = st | dms_upd_vr (va);                          /* set violation cause in register */

if (mp_control) {                                       /* memory protect on? */
    mp_mem_changed = TRUE;                              /* set the MP/MEM registers changed flag */

    mp_mevff = SET;                                     /* record memory expansion violation */
    MP_ABORT (va);                                      /* abort */
    }
return;
}


/* Update the MEM violation register.

   In hardware, the MEM violation register (VR) is clocked on every memory read,
   every memory write above the lower bound of protected memory, and every
   execution of a privileged DMS instruction.  The register is not clocked when
   MP is disabled by an MP or MEM error (i.e., when MEVFF sets or CTL5FF
   clears), in order to capture the state of the MEM.  In other words, the VR
   continually tracks the memory map register accessed plus the MEM state
   (MEBEN, MAPON, and USR) until a violation occurs, and then it's "frozen."

   Under simulation, we do not have to update the VR on every memory access,
   because the visible state is only available via a programmed RVA/B
   instruction or via the SCP interface.  Therefore, it is sufficient if the
   register is updated:

     - at a MEM violation (when freezing)
     - at an MP violation (when freezing)
     - during RVA/B execution (if not frozen)
     - before returning to SCP after a simulator stop (if not frozen)
*/

HP_WORD dms_upd_vr (uint32 va)
{
if (mp_control && (mp_mevff == CLEAR)) {                /* violation register unfrozen? */
    dms_vr = VA_GETPAG (va) |                           /* set map address */
             (dms_enb ? MVI_MEM : 0) |                  /*   and MEM enabled */
             (dms_ump ? MVI_UMP : 0);                   /*   and user map enabled */

    if (is_mapped (va))                                 /* is addressed mapped? */
        dms_vr = dms_vr | MVI_MEB;                      /* ME bus is enabled */

    mp_mem_changed = TRUE;                              /* set the MP/MEM registers changed flag */
    }

return dms_vr;
}


/* Update the MEM status register */

HP_WORD dms_upd_sr (void)
{
dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO);

if (dms_enb)
    dms_sr = dms_sr | MST_ENB;

if (dms_ump)
    dms_sr = dms_sr | MST_UMP;

if (mp_control)
    dms_sr = dms_sr | MST_PRO;

return dms_sr;
}



/* Memory Protect global utility routines */


/* Memory protect and DMS validation for jumps.

   Jumps are a special case of write validation.  The target address is treated
   as a write, even when no physical write takes place, so jumping to a
   write-protected page causes a MEM violation.  In addition, a MEM violation is
   indicated if the jump is to the unmapped portion of the base page.  Finally,
   jumping to a location under the memory-protect fence causes an MP violation.

   Because the MP and MEM hardware works in parallel, all three violations may
   exist concurrently.  For example, a JMP to the unmapped portion of the base
   page that is write protected and under the MP fence will indicate a
   base-page, write, and MP violation, whereas a JMP to the mapped portion will
   indicate a write and MP violation (BPV is inhibited by the MEBEN signal).  If
   MEM and MP violations occur concurrently, the MEM violation takes precedence,
   as the SFS and SFC instructions test the MEV flip-flop.

   The lower bound of protected memory is passed in the "plb" argument.  This
   must be either 0 or 2.  All violations are qualified by the MPCND signal,
   which responds to the lower bound.  Therefore, if the lower bound is 2, and
   if the part below the base-page fence is unmapped, or if the base page is
   write-protected, then a MEM violation will occur only if the access is not to
   locations 0 or 1.  The instruction set firmware uses a lower bound of 0 for
   JMP, JLY, and JPY (and for JSB with W5 out), and of 2 for DJP, SJP, UJP, JRS,
   and .GOTO (and JSB with W5 in).

   Finally, all violations are inhibited if MP is off (mp_control is CLEAR), and
   MEM violations are inhibited if the MEM is disabled.
*/

void mp_dms_jmp (uint32 va, uint32 plb)
{
HP_WORD violation = 0;
uint32  pgn = VA_GETPAG (va);                           /* get page number */

if (mp_control) {                                       /* MP on? */
    if (dms_enb) {                                      /* MEM on? */
        if (dms_map [dms_ump + pgn] & WRPROT)           /* page write protected? */
            violation = MVI_WPR;                        /* write violation occurred */

        if (!is_mapped (va) && (va >= plb))             /* base page target? */
            violation = violation | MVI_BPG;            /* base page violation occurred */

        if (violation)                                  /* any violation? */
            dms_viol (va, violation);                   /* signal MEM violation */
        }

    if ((va >= plb) && (va < mp_fence))                 /* jump under fence? */
        MP_ABORT (va);                                  /* signal MP violation */
    }

return;
}



/* CPU local SCP support routine declarations */


/* CPU (SC 0) I/O signal handler.

   I/O instructions for select code 0 manipulate the interrupt system.  STF and
   CLF turn the interrupt system on and off, and SFS and SFC test the state of
   the interrupt system.  When the interrupt system is off, only power fail and
   parity error interrupts are allowed.

   A PON reset initializes certain CPU registers.  The 1000 series does a
   microcoded memory clear and leaves the T and P registers set as a result.

   Front-panel PRESET performs additional initialization.  We also handle MEM
   preset here.


   Implementation notes:

    1. An IOI signal reads the floating I/O bus (0 on all machines).

    2. A CLC 0 issues CRS to all devices, not CLC.  While most cards react
       identically to CRS and CLC, some do not, e.g., the 12566B when used as an
       I/O diagnostic target.

    3. RTE uses the undocumented SFS 0,C instruction to both test and turn off
       the interrupt system.  This is confirmed in the "RTE-6/VM Technical
       Specifications" manual (HP 92084-90015), section 2.3.1 "Process the
       Interrupt", subsection "A.1 $CIC":

        "Test to see if the interrupt system is on or off.  This is done with
         the SFS 0,C instruction.  In either case, turn it off (the ,C does
         it)."

       ...and in section 5.8, "Parity Error Detection":

        "Because parity error interrupts can occur even when the interrupt
         system is off, the code at $CIC must be able to save the complete
         system status. The major hole in being able to save the complete state
         is in saving the interrupt system state. In order to do this in both
         the 21MX and the 21XE the instruction 103300 was used to both test the
         interrupt system and turn it off."

    4. Select code 0 cannot interrupt, so there is no SIR handler.
*/

static uint32 cpuio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
uint32 sc;
IOSIGNAL signal;
IOCYCLE  working_set = signal_set;                      /* no SIR handler needed */

while (working_set) {
    signal = IONEXT (working_set);                      /* isolate next signal */

    switch (signal) {                                   /* dispatch I/O signal */

        case ioCLF:                                     /* clear flag flip-flop */
            ion = CLEAR;                                /* turn interrupt system off */
            break;

        case ioSTF:                                     /* set flag flip-flop */
            ion = SET;                                  /* turn interrupt system on */
            break;

        case ioSFC:                                     /* skip if flag is clear */
            setSKF (!ion);                              /* skip if interrupt system is off */
            break;

        case ioSFS:                                     /* skip if flag is set */
            setSKF (ion);                               /* skip if interrupt system is on */
            break;

        case ioIOI:                                     /* I/O input */
            stat_data = IORETURN (SCPE_OK, 0);          /* returns 0 */
            break;

        case ioPON:                                     /* power on normal */
            AR = 0;                                     /* clear A register */
            BR = 0;                                     /* clear B register */
            SR = 0;                                     /* clear S register */
            TR = 0;                                     /* clear T register */
            E = 1;                                      /* set E register */

            if (is_1000) {                              /* 1000 series? */
                memset (M, 0, (uint32) MEMSIZE * 2);    /* zero allocated memory */
                MR = 0077777;                           /* set M register */
                PR = 0100000;                           /* set P register */
                }

            else {                                      /* 21xx series */
                MR = 0;                                 /* clear M register */
                PR = 0;                                 /* clear P register */
                }
            break;

        case ioPOPIO:                                   /* power-on preset to I/O */
            O = 0;                                      /* clear O register */
            ion = CLEAR;                                /* turn off interrupt system */
            ion_defer = FALSE;                          /* clear interrupt deferral */

            dms_enb = 0;                                /* turn DMS off */
            dms_ump = 0;                                /* init to system map */
            dms_sr = 0;                                 /* clear status register and BP fence */
            dms_vr = 0;                                 /* clear violation register */

            mp_mem_changed = TRUE;                      /* set the MP/MEM registers changed flag */
            break;

        case ioCLC:                                     /* clear control flip-flop */
            for (sc = CRSDEV; sc <= MAXDEV; sc++)       /* send CRS to devices */
                io_dispatch (sc, ioCRS, 0);             /*   from select code 6 and up */
            break;

        default:                                        /* all other signals */
            break;                                      /*   are ignored */
        }

    working_set = working_set & ~signal;                /* remove current signal from set */
    }

return stat_data;
}


/* Overflow/S-register (SC 1) I/O signal handler.

   Flag instructions directed to select code 1 manipulate the overflow (O)
   register.  Input and output instructions access the switch (S) register.  On
   the 2115 and 2116, there is no S-register indicator, so it is effectively
   read-only.  On the other machines, a front-panel display of the S-register is
   provided.  On all machines, front-panel switches are provided to set the
   contents of the S register.


   Implementation notes:

    1. Select code 1 cannot interrupt, so there is no SIR handler.
*/

static uint32 ovflio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
IOSIGNAL signal;
IOCYCLE  working_set = signal_set;                      /* no SIR handler needed */

while (working_set) {
    signal = IONEXT (working_set);                      /* isolate next signal */

    switch (signal) {                                   /* dispatch I/O signal */

        case ioCLF:                                     /* clear flag flip-flop */
            O = 0;                                      /* clear overflow */
            break;

        case ioSTF:                                     /* set flag flip-flop */
            O = 1;                                      /* set overflow */
            break;

        case ioSFC:                                     /* skip if flag is clear */
            setSKF (!O);                                /* skip if overflow is clear */
            break;

        case ioSFS:                                     /* skip if flag is set */
            setSKF (O);                                 /* skip if overflow is set */
            break;

        case ioIOI:                                     /* I/O input */
            stat_data = IORETURN (SCPE_OK, SR);         /* read switch register value */
            break;

        case ioIOO:                                     /* I/O output */
            if ((UNIT_CPU_MODEL != UNIT_2116) &&        /* no S register display on */
                (UNIT_CPU_MODEL != UNIT_2115))          /*   2116 and 2115 machines */
                SR = IODATA (stat_data);                /* write S register value */
            break;

        default:                                        /* all other signals */
            break;                                      /*   are ignored */
        }

    working_set = working_set & ~signal;                /* remove current signal from set */
    }

return stat_data;
}


/* Power fail (SC 4) I/O signal handler.

   Power fail detection is standard on 2100 and 1000 systems and is optional on
   21xx systems.  Power fail recovery is standard on the 2100 and optional on
   the others.  Power failure or restoration will cause an interrupt on select
   code 4.  The direction of power change (down or up) can be tested by SFC.

   We do not implement power fail under simulation.  However, the central
   interrupt register (CIR) is always read by an IOI directed to select code 4.
*/

static uint32 pwrfio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
IOSIGNAL signal;
IOCYCLE  working_set = IOADDSIR (signal_set);           /* add ioSIR if needed */

while (working_set) {
    signal = IONEXT (working_set);                      /* isolate next signal */

    switch (signal) {                                   /* dispatch I/O signal */

        case ioSTC:                                     /* set control flip-flop */
            break;                                      /* reinitializes power fail */

        case ioCLC:                                     /* clear control flip-flop */
            break;                                      /* reinitializes power fail */

        case ioSFC:                                     /* skip if flag is clear */
            break;                                      /* skips if power fail occurred */

        case ioIOI:                                     /* I/O input */
            stat_data = IORETURN (SCPE_OK, CIR);        /* input CIR value */
            break;

        default:                                        /* all other signals */
            break;                                      /*   are ignored */
        }

    working_set = working_set & ~signal;                /* remove current signal from set */
    }

return stat_data;
}


/* Examine a CPU memory location.

   This routine is called by the SCP to examine memory.  The routine retrieves
   the memory location indicated by "address" as modified by any "switches" that
   were specified on the command line and returns the value in the first element
   of "eval_array".

   On entry, the "map_address" routine is called to translate a logical address
   to a physical address.  If "switches" includes SIM_SW_REST or "-N", then the
   address is a physical address, and the routine returns the address unaltered.

   Otherwise, the address is a logical address interpreted in the context of the
   translation map implied by the specified switch and is mapped to a physical
   address.  If memory expansion is disabled but a map is specified, then the
   command is rejected.  Otherwise if the resulting address is beyond the
   current memory limit, or if mapping is implied or explicit but the address
   specified is outside of the logical address space, "address space exceeded"
   status is returned.

   Otherwise, the value is obtained from memory or the A/B register and returned
   in the first word of "eval_array."
*/

static t_stat cpu_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches)
{
uint32 index;

index = map_address ((HP_WORD) address, switches);      /* map the supplied address as directed by the switches */

if (dms_enb == 0 && switches & ALL_MAPMODES)            /* if the MEM is disabled but a mapping mode was given */
    return SCPE_NOFNC;                                  /*   then the command is not allowed */

else if (index >= MEMSIZE)                              /* otherwise if the address is beyond the memory limit */
    return SCPE_NXM;                                    /*   then return non-existent memory status */

else if (eval_array == NULL)                            /* otherwise if the value pointer was not supplied */
    return SCPE_IERR;                                   /*   then return internal error status */

else if (switches & SIM_SW_REST || index >= 2)          /* otherwise if restoring or memory is being accessed */
    *eval_array = (t_value) M [index];                  /*   then return the memory value */
else                                                    /* otherwise */
    *eval_array = (t_value) ABREG [index];              /*   return the A or B register value */

return SCPE_OK;                                         /* return success status */
}


/* Deposit to a CPU memory location.

   This routine is called by the SCP to deposit to memory.  The routine stores
   the supplied "value" into memory at the "address" location as modified by any
   "switches" that were specified on the command line.

   On entry, the "map_address" routine is called to translate a logical address
   to a physical address.  If "switches" includes SIM_SW_REST or "-N", then the
   address is a physical address, and the routine returns the address unaltered.

   Otherwise, the address is a logical address interpreted in the context of the
   translation map implied by the specified switch and is mapped to a physical
   address.  If memory expansion is disabled but a map is specified, then the
   command is rejected.  Otherwise if the resulting address is beyond the
   current memory limit, or if mapping is implied or explicit but the address
   specified is outside of the logical address space, "address space exceeded"
   status is returned.

   Otherwise, the value is stored into memory or the A/B register.
*/

static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches)
{
uint32 index;

index = map_address ((HP_WORD) address, switches);      /* map the supplied address as directed by the switches */

if (dms_enb == 0 && switches & ALL_MAPMODES)            /* if the MEM is disabled but a mapping mode was given */
    return SCPE_NOFNC;                                  /*   then the command is not allowed */

else if (index >= MEMSIZE)                              /* otherwise if the address is beyond the memory limit */
    return SCPE_NXM;                                    /*   then return non-existent memory status */

else if (switches & SIM_SW_REST || index >= 2)          /* otherwise if restoring or memory is being accessed */
    M [index] = (MEMORY_WORD) value & DV_MASK;          /*   then write the memory value */

else                                                    /* otherwise */
    ABREG [index] = (HP_WORD) value & DV_MASK;          /*   write the A or B register value */

return SCPE_OK;                                         /* return success status */
}


/* Reset the CPU.

   This routine is called for a RESET, RESET CPU, RUN, or BOOT CPU command.  It
   is the simulation equivalent of an initial power-on condition (corresponding
   to PON, POPIO, and CRS signal assertion in the CPU) or a front-panel PRESET
   button press (corresponding to POPIO and CRS assertion).  SCP delivers a
   power-on reset to all devices when the simulator is started.

   If this is the first call after simulator startup, the initial memory array
   is allocated, the default CPU and memory size configuration is set, and the
   SCP-required program counter pointer is set to point to the REG array element
   corresponding to the P register.

   If this is a power-on reset ("RESET -P"), the default Basic Binary Loader is
   installed in protected memory (the upper 64 words of the defined memory
   size).


   Implementation notes:

    1. Setting the sim_PC value at run time accommodates changes in the register
       order automatically.  A fixed setting runs the risk of it not being
       updated if a change in the register order is made.

    2. A power-on reset installs the BBL in 21xx machines.  The IBL copy routine
       sets the last loader word to the two's complement of the loader starting
       address and configures the second-to-last word (nominally the DMA control
       word) to the I/O select code.  For the BBL, the starting address location
       is five locations back, and the DMA control word is not used, so these
       fixups are performed after copying.
*/

static t_stat cpu_reset (DEVICE *dptr)
{
if (M == NULL) {                                        /* initial call after startup? */
    pcq_r = find_reg ("PCQ", NULL, dptr);               /* get PC queue pointer */

    if (pcq_r)                                          /* defined? */
        pcq_r->qptr = 0;                                /* initialize queue */
    else                                                /* not defined */
        return SCPE_IERR;                               /* internal error */

    M = (MEMORY_WORD *) calloc (PASIZE, sizeof (MEMORY_WORD));  /* alloc mem */

    if (M == NULL)                                      /* alloc fail? */
        return SCPE_MEM;

    else {                                              /* do one-time init */
        for (sim_PC = dptr->registers;                  /* find the P register entry */
             sim_PC->loc != &PR && sim_PC->loc != NULL; /*   in the register array */
             sim_PC++);                                 /*     for the SCP interface */

        if (sim_PC == NULL)                             /* if the P register entry is not present */
            return SCPE_NXREG;                          /*   then there is a serious problem! */

        MEMSIZE = 32768;                                /* set the initial memory size */
        set_model (NULL, UNIT_2116, NULL, NULL);        /*   and the initial CPU model */
        }
    }


if (sim_switches & SWMASK ('P')) {                      /* if this is a power-on reset */
    if (! is_1000) {                                    /*   then if the CPU is configured as a 21xx machine */
        cpu_ibl (bbl, ptr_dib.select_code,              /*     then install the BBL and configure it */
                 IBL_S_NOCLR, IBL_S_NOSET);             /*       to the paper tape reader's select code */

        M [PR + BBL_FWA] = M [PR + IBL_END];            /* move the BBL starting address to correct location */
        M [PR + IBL_DPC] = bbl [IBL_DPC];               /*   and restore the last two words of the BBL */
        M [PR + IBL_END] = bbl [IBL_END];               /*     that are overwritten by the 1000 IBL */

        set_loader (NULL, FALSE, NULL, NULL);          /* disable the loader, which was enabled by cpu_ibl */
        }

    IOPOWERON (&cpu_dib);                               /* issue the PON signal to the CPU */
    }

else                                                    /* PRESET */
    IOPRESET (&cpu_dib);

sim_brk_dflt = SWMASK ('N');                            /* the default breakpoint type is "nomap" as MEM is disabled */

return SCPE_OK;
}


/* IBL routine (CPU boot) */

static t_stat cpu_boot (int32 unitno, DEVICE *dptr)
{
int32 dev = (SR >> IBL_V_DEV) & I_DEVMASK;
int32 sel = (SR >> IBL_V_SEL) & IBL_M_SEL;

if (dev < 010)
    return SCPE_NOFNC;

switch (sel) {

    case 0:                                             /* PTR boot */
        cpu_ibl (ptr_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
        break;

    case 1:                                             /* DP/DQ boot */
        cpu_ibl (dq_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
        break;

    case 2:                                             /* MS boot */
        cpu_ibl (ms_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
        break;

    case 3:                                             /* DS boot */
        cpu_ibl (ds_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
        break;
        }

return SCPE_OK;
}


/* Set the CPU simulation stop conditions.

   This validation routine is called to configure the set of CPU stop
   conditions.  The "option" parameter is 0 to clear the stops, 1 to set the
   stops, and 2 to set the indirect chain length limit.  "cptr" points to the
   first character of the name of the stop to be cleared or set.  The unit and
   description pointers are not used.

   The routine processes commands of the form:

     SET CPU STOP
     SET CPU STOP=<stopname>[;<stopname>...]
     SET CPU NOSTOP
     SET CPU NOSTOP=<stopname>[;<stopname>...]
     SET CPU INDIR=<limit>

   The valid <stopname>s are contained in the "cpu_stop" table.  If names are
   not specified, all stop conditions are enabled or disabled.


   Implementation notes:

    1. The maximum indirect limit value is 32K, as an indirect chain cannot
       exceed the logical memory size without being in a loop.
*/

static t_stat set_stops (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
{
char gbuf [CBUFSIZE];
t_stat status;
uint32 stop;

if (cptr == NULL)                                               /* if there are no arguments */
    if (option == 0)                                            /*   then if we're clearing the stops */
        for (stop = 0; cpu_stop [stop].name != NULL; stop++)    /*     then loop through the flags */
            *cpu_stop [stop].status = SCPE_OK;                  /*       and clear each stop status */

    else if (option == 1)                                       /* otherwise if we're setting the stops */
        for (stop = 0; cpu_stop [stop].name != NULL; stop++)    /*   then loop through the flags */
            *cpu_stop [stop].status = cpu_stop [stop].value;    /*     and set each stop status */

    else                                                        /* otherwise */
        return SCPE_MISVAL;                                     /*   report the missing indirect limit value */

else if (*cptr == '\0')                                 /* otherwise if the argument is empty */
    return SCPE_MISVAL;                                 /*   then report the missing value */

else if (option == 2) {                                         /* otherwise if we're setting the indirect limit */
    stop = (uint32) get_uint (cptr, 10, LA_MAX + 1, &status);   /*   then parse the limit value */

    if (status != SCPE_OK)                              /* if a parsing error occurred */
        return status;                                  /*   then return the error status */
    else                                                /* otherwise */
        indirect_limit = stop;                          /*   set the indirect limit */
    }

else                                                    /* otherwise at least one stop argument is present */
    while (*cptr) {                                     /* loop through the arguments */
        cptr = get_glyph (cptr, gbuf, ';');             /* get the next argument */

        for (stop = 0; cpu_stop [stop].name != NULL; stop++)            /* loop through the flags */
            if (strcmp (cpu_stop [stop].name, gbuf) == 0) {             /*   and if the argument matches */
                if (option == 1)                                        /*     then if it's a STOP argument */
                    *cpu_stop [stop].status = cpu_stop [stop].value;    /*       then set the stop status */
                else                                                    /*     otherwise it's a NOSTOP argument */
                    *cpu_stop [stop].status = SCPE_OK;                  /*       so clear the stop status */

                break;                                  /* this argument has been processed */
                }

        if (cpu_stop [stop].name == NULL)               /* if the argument was not found */
            return SCPE_ARG;                            /*   then report it */
        }

return SCPE_OK;                                         /* the stops were successfully processed */
}


/* Change the CPU memory size.

   This validation routine is called to configure the CPU memory size.  The
   "new_size" parameter is set to the size desired and will be one of the
   discrete sizes supported by the simulator.  The "uptr" parameter points to
   the CPU unit and is used to obtain the CPU model.  The other parameters are
   not used.

   The routine processes commands of the form:

     SET [-F] CPU <memsize>

   If the new memory size is larger than the supported size for the CPU model
   currently selected, the routine returns an error.  If the new size is smaller
   than the previous size, and if the area that would be lost contains non-zero
   data, the user is prompted to confirm that memory should be truncated.  If
   the user denies the request, the change is rejected.  Otherwise, the new size
   is set.  The user may omit the confirmation request and force truncation by
   specifying the "-F" switch on the command line.

   On a 21xx CPU, the last 64 words in memory are reserved for the binary
   loader.  Before changing the memory size, the current loader is copied to the
   shadow RAM to preserve any manual changes that were made.  Then the new
   memory size is set, with the beginning of the loader area set as the first
   word of non-existent memory.

   Finally, non-existent memory is zeroed, so that the mem_read routine does not
   need any special handling for addresses beyond the end of defined memory.


   Implementation notes:

    1. In hardware, reads from non-existent memory return zero, and writes are
       ignored.  In simulation, the largest possible memory is instantiated and
       initialized to zero.  Therefore, only writes need to be checked against
       memory size.

    2. On the 21xx machines, doing SET CPU LOADERDISABLE decreases available
       memory size by 64 words.
*/

static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc)
{
static CONST char confirm [] = "Really truncate memory [N]?";
uint32 i;
uint32 old_size = (uint32) MEMSIZE;                     /* current memory size */

const uint32 model = CPU_MODEL_INDEX;                   /* the current CPU model index */

if ((uint32) new_size > cpu_features [model].maxmem)    /* if the new memory size is not supported on current model */
    return SCPE_NOFNC;                                  /*   then report the error */

if (!(sim_switches & SWMASK ('F'))                      /* if truncation is not explicitly forced */
  && ! mem_is_empty (new_size)                          /*   and the truncated part is not empty */
  && get_yn (confirm, FALSE) == FALSE)                  /*     and the user denies confirmation */
    return SCPE_INCOMP;                                 /*       then abort the command */

if (is_1000)                                            /* loader unsupported */
    MEMSIZE = fwanxm = new_size;                        /* set new memory size */

else {                                                  /* 21xx CPU? */
    set_loader (uptr, FALSE, NULL, NULL);               /* save loader to shadow RAM */
    MEMSIZE = new_size;                                 /* set new memory size */
    fwanxm = (uint32) MEMSIZE - IBL_LNT;                /* reserve memory for loader */
    }

for (i = fwanxm; i < old_size; i++)                     /* zero non-existent memory */
    M [i] = 0;

return SCPE_OK;
}


/* Change CPU models.

   For convenience, MP and DMA are typically enabled if available; they may be
   disabled subsequently if desired.  Note that the 2114 supports only one DMA
   channel (channel 1).  All other models support two channels.

   Validation:
   - Sets standard equipment and convenience features.
   - Changes DMA device name to DCPC if 1000 is selected.
   - Enforces maximum memory allowed (doesn't change otherwise).
   - Disables loader on 21xx machines.


   Implementation notes:

    1. "cpu_configuration" is used by the symbolic examine and deposit routines
       and instruction tracing to determine whether the firmware implementing a
       given opcode is present.  It is a copy of the CPU unit option flags with
       the encoded CPU model decoded into model flag bits.  This allows a simple
       (and fast) AND operation with a firmware feature word to determine
       applicability, saving the multiple masks and comparisons that would
       otherwise be required.

       Additionally, the configuration word has the unit CPU model bits set on
       permanently to permit a base-set feature test for those CPUs that have no
       options currently enabled (at least one non-option bit must be on for the
       test to succeed, and the model bits are not otherwise used).
*/

static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc)
{
const uint32 old_family = UNIT_CPU_FAMILY;              /* current CPU type */
const uint32 new_family = new_model & UNIT_FAMILY_MASK; /* new CPU family */
const uint32 new_index  = new_model >> UNIT_V_CPU;      /* new CPU model index */
uint32 new_memsize;
t_stat result;

if (MEMSIZE > cpu_features [new_index].maxmem)          /* if the current memory size is too large for the new model */
    new_memsize = cpu_features [new_index].maxmem;      /*   then set it to the maximum size supported */
else                                                    /* otherwise */
    new_memsize = (uint32) MEMSIZE;                     /*   leave it unchanged */

result = set_size (uptr, new_memsize, NULL, NULL);      /* set the new memory size */

if (result == SCPE_OK) {                                            /* if the change succeeded */
    cpu_configuration = cpu_features [new_index].typ & UNIT_OPTS    /*   then set the typical options */
                          | UNIT_MODEL_MASK                         /*     and the base model bits */
                          | 1u << new_index;                        /*       and the new CPU model flag */

    cpu_unit.flags = cpu_unit.flags & ~UNIT_OPTS                    /* enable the typical features */
                       | cpu_features [new_index].typ & UNIT_OPTS;  /*   for the new model */

    if (cpu_features [new_index].typ & UNIT_MP)         /* MP in typ config? */
        mp_dev.flags &= ~DEV_DIS;                       /* enable it */
    else
        mp_dev.flags |= DEV_DIS;                        /* disable it */

    if (cpu_features[new_index].opt & UNIT_MP)          /* MP an option? */
        mp_dev.flags |= DEV_DISABLE;                    /* make it alterable */
    else
        mp_dev.flags &= ~DEV_DISABLE;                   /* make it unalterable */


    if (cpu_features [new_index].typ & UNIT_DMA) {      /* DMA in typ config? */
        dma1_dev.flags &= ~DEV_DIS;                     /* enable DMA channel 1 */

        if (new_model == UNIT_2114)                     /* 2114 has only one channel */
            dma2_dev.flags |= DEV_DIS;                  /* disable channel 2 */
        else                                            /* all others have two channels */
            dma2_dev.flags &= ~DEV_DIS;                 /* enable it */
        }

    else {
        dma1_dev.flags |= DEV_DIS;                      /* disable channel 1 */
        dma2_dev.flags |= DEV_DIS;                      /* disable channel 2 */
        }

    if (cpu_features [new_index].opt & UNIT_DMA) {      /* DMA an option? */
        dma1_dev.flags |= DEV_DISABLE;                  /* make it alterable */

        if (new_model == UNIT_2114)                     /* 2114 has only one channel */
            dma2_dev.flags &= ~DEV_DISABLE;             /* make it unalterable */
        else                                            /* all others have two channels */
            dma2_dev.flags |= DEV_DISABLE;              /* make it alterable */
        }

    else {                                              /* otherwise DMA is not available */
        dma1_dev.flags &= ~DEV_DISABLE;                 /* make it unalterable */
        dma2_dev.flags &= ~DEV_DISABLE;                 /* make it unalterable */
        }

    if ((old_family == UNIT_FAMILY_1000) &&             /* if current family is 1000 */
        (new_family == UNIT_FAMILY_21XX)) {             /* and new family is 21xx */
        deassign_device (&dma1_dev);                    /* delete DCPC names */
        deassign_device (&dma2_dev);
        }

    else if ((old_family == UNIT_FAMILY_21XX) &&        /* otherwise if current family is 21xx */
             (new_family == UNIT_FAMILY_1000)) {        /* and new family is 1000 */
        assign_device (&dma1_dev, "DCPC1");             /* change DMA device name */
        assign_device (&dma2_dev, "DCPC2");             /* to DCPC for familiarity */
        }


    if (!(cpu_features [new_index].typ & UNIT_DMS))     /* if DMS is not being enabled */
        dms_enb = 0;                                    /*   then disable MEM mapping */

    is_1000 = (new_family == UNIT_FAMILY_1000);         /* set model */

    if (is_1000)
        fwanxm = (uint32) MEMSIZE;                      /* loader reserved only for 21xx */
    else                                                /* 2100 or 211x */
        fwanxm = (uint32) MEMSIZE - IBL_LNT;            /* reserve memory for loader */
    }

return result;
}


/* Change a CPU option.

   This validation routine is called to configure the option set for the current
   CPU model.  The "option" parameter is set to the option desired and will be
   one of the unit option flags.  The "uptr" parameter points to the CPU unit
   and is used to obtain the CPU model.  The other parameters are not used.

   The routine processes commands of the form:

     SET CPU <option>[,<option>...]

   The option must be valid for the current CPU model, or the command is
   rejected.


   Implementation notes:

    1. "cpu_configuration" is used by the symbolic examine and deposit routines
       and instruction tracing to determine whether the firmware implementing a
       given opcode is present.  It is a copy of the CPU unit option flags with
       the encoded CPU model decoded into model flag bits.  This allows a simple
       (and fast) AND operation with a firmware feature word to determine
       applicability, saving the multiple masks and comparisons that would
       otherwise be required.
*/

static t_stat set_option (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
{
uint32 model = CPU_MODEL_INDEX;                         /* current CPU model index */

if ((cpu_features [model].opt & option) == 0)           /* option supported? */
    return SCPE_NOFNC;                                  /* no */

if (UNIT_CPU_TYPE == UNIT_TYPE_2100) {
    if ((option == UNIT_FP) || (option == UNIT_FFP))    /* 2100 IOP and FP/FFP options */
        uptr->flags &= ~UNIT_IOP;                       /*   are mutually exclusive */
    else if (option == UNIT_IOP)
        uptr->flags &= ~(UNIT_FP | UNIT_FFP);

    if (option == UNIT_FFP)                             /* 2100 FFP option requires FP */
        uptr->flags |= UNIT_FP;
    }

cpu_configuration = cpu_configuration & ~UNIT_OPTS      /* update the CPU configuration */
                      | uptr->flags & UNIT_OPTS;        /*   with the revised option settings */

if (option & UNIT_EMA_VMA)                              /* if EMA or VMA is being set */
    cpu_configuration &= ~UNIT_EMA_VMA;                 /*   then remove both as they are mutually exclusive */

cpu_configuration |= option;                            /* include the new setting */

return SCPE_OK;
}


/* Clear a CPU option.

   Validation:
   - Checks that the current CPU model supports the option selected.
   - Clears flag from unit structure (we are processing MTAB_XTD entries).
   - If CPU is 2100, ensures that FFP is disabled if FP disabled
     (FP is required for FFP installation).


   Implementation notes:

    1. "cpu_configuration" is used by the symbolic examine and deposit routines
       and instruction tracing to determine whether the firmware implementing a
       given opcode is present.  It is a copy of the CPU unit option flags with
       the encoded CPU model decoded into model flag bits.  This allows a simple
       (and fast) AND operation with a firmware feature word to determine
       applicability, saving the multiple masks and comparisons that would
       otherwise be required.
*/

t_bool clear_option (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
{
uint32 model = CPU_MODEL_INDEX;                         /* current CPU model index */

if ((cpu_features[model].opt & option) == 0)            /* option supported? */
    return SCPE_NOFNC;                                  /* no */

uptr->flags = uptr->flags & ~option;                    /* disable option */

if (option == UNIT_DMS)                                 /* if DMS is being disabled */
    dms_enb = 0;                                        /*   then disable MEM mapping */

if ((UNIT_CPU_TYPE == UNIT_TYPE_2100) &&                /* disabling 2100 FP? */
    (option == UNIT_FP))
    uptr->flags = uptr->flags & ~UNIT_FFP;              /* yes, so disable FFP too */

cpu_configuration = cpu_configuration & ~UNIT_OPTS      /* update the CPU configuration */
                      | uptr->flags & UNIT_OPTS;        /*   with the revised option settings */

return SCPE_OK;
}


/* 21xx loader enable/disable function.

   The 21xx CPUs store their initial binary loaders in the last 64 words of
   available memory.  This memory is protected by a LOADER ENABLE switch on the
   front panel.  When the switch is off (disabled), main memory effectively ends
   64 locations earlier, i.e., the loader area is treated as non-existent.
   Because these are core machines, the loader is retained when system power is
   off.

   1000 CPUs do not have a protected loader feature.  Instead, loaders are
   stored in PROMs and are copied into main memory for execution by the IBL
   switch.

   Under simulation, we keep both a total configured memory size (MEMSIZE) and a
   current configured memory size (fwanxm = "first word address of non-existent
   memory").  When the two are equal, the loader is enabled.  When the current
   size is less than the total size, the loader is disabled.

   Disabling the loader copies the last 64 words to a shadow array, zeros the
   corresponding memory, and decreases the last word of addressable memory by
   64.  Enabling the loader reverses this process.

   Disabling may be done manually by user command or automatically when a halt
   instruction is executed.  Enabling occurs only by user command.  This differs
   slightly from actual machine operation, which additionally disables the
   loader when a manual halt is performed.  We do not do this to allow
   breakpoints within and single-stepping through the loaders.
*/

static t_stat set_loader (UNIT *uptr, int32 enable, CONST char *cptr, void *desc)
{
static BOOT_ROM loader;
int32 i;
t_bool is_enabled = (fwanxm == MEMSIZE);

if (is_1000 || MEMSIZE == 0)                            /* valid only for 21xx and for initialized memory */
    return SCPE_NOFNC;

if (is_enabled && (enable == 0)) {                      /* disable loader? */
    fwanxm = (uint32) MEMSIZE - IBL_LNT;                /* decrease available memory */
    for (i = 0; i < IBL_LNT; i++) {                     /* copy loader */
        loader [i] = M [fwanxm + i];                    /* from memory */
        M [fwanxm + i] = 0;                             /* and zero location */
        }
    }

else if ((!is_enabled) && (enable == 1)) {              /* enable loader? */
    for (i = 0; i < IBL_LNT; i++)                       /* copy loader */
        M [fwanxm + i] = loader [i];                    /* to memory */
    fwanxm = (uint32) MEMSIZE;                          /* increase available memory */
    }

return SCPE_OK;
}


/* Change the instruction execution trace criteria.

   This validation routine is called to configure the criteria that select
   instruction execution tracing.  The "option" parameter is 0 to clear and 1 to
   set the criteria, and "cptr" points to the first character of the match value
   to be set.  The unit and description pointers are not used.

   The routine processes commands of the form:

     SET CPU EXEC=<match>[;<mask>]
     SET CPU NOEXEC

   If the <mask> value is not supplied, a mask of 177777 octal is used.  The
   values are entered in the current CPU data radix, which defaults to octal,
   unless an override switch is present on the command line.
*/

static t_stat set_exec (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
{
char   gbuf [CBUFSIZE];
uint32 match, mask, radix;
t_stat status;

if (option == 0)                                        /* if this is a NOEXEC request */
    if (cptr == NULL) {                                 /*   then if there are no arguments */
        exec_match = D16_UMAX;                          /*     then set the match and mask values */
        exec_mask  = 0;                                 /*       to prevent matching */
        return SCPE_OK;                                 /*         and return success */
        }

    else                                                /*   otherwise there are extraneous characters */
        return SCPE_2MARG;                              /*     so report that there are too many arguments */

else if (cptr == NULL || *cptr == '\0')                 /* otherwise if the EXEC request supplies no arguments */
    return SCPE_MISVAL;                                 /*   then report a missing value */

else {                                                  /* otherwise at least one argument is present */
    cptr = get_glyph (cptr, gbuf, ';');                 /*   so get the match argument */

    if (sim_switches & SWMASK ('O'))                    /* if an octal override is present */
        radix = 8;                                      /*   then parse the value in base 8 */
    else if (sim_switches & SWMASK ('D'))               /* otherwise if a decimal override is present */
        radix = 10;                                     /*   then parse the value in base 10 */
    else if (sim_switches & SWMASK ('H'))               /* otherwise if a hex override is present */
        radix = 16;                                     /*   then parse the value in base 16 */
    else                                                /* otherwise */
        radix = cpu_dev.dradix;                         /*   use the current CPU data radix */

    match = (uint32) get_uint (gbuf, radix, D16_UMAX, &status); /* parse the match value */

    if (status != SCPE_OK)                              /* if a parsing error occurred */
        return status;                                  /*   then return the error status */

    else if (*cptr == '\0') {                           /* otherwise if no more characters are present */
        exec_match = match;                             /*   then set the match value */
        exec_mask  = D16_MASK;                          /*     and default the mask value */
        return SCPE_OK;                                 /*       and return success */
        }

    else {                                              /* otherwise another argument is present */
        cptr = get_glyph (cptr, gbuf, ';');             /*   so get the mask argument */

        mask = (uint32) get_uint (gbuf, radix, D16_UMAX, &status);  /* parse the mask value */

        if (status != SCPE_OK)                          /* if a parsing error occurred */
            return status;                              /*   then return the error status */

        else if (*cptr == '\0')                         /* if no more characters are present */
            if (mask == 0)                              /*   then if the mask value is zero */
                return SCPE_ARG;                        /*     then the match will never succeed */

            else {                                      /*   otherwise */
                exec_match = match;                     /*     set the match value */
                exec_mask  = mask;                      /*       and the mask value */
                return SCPE_OK;                         /*         and return success */
                }

        else                                            /* otherwise extraneous characters are present */
            return SCPE_2MARG;                          /*   so report that there are too many arguments */
        }
    }
}


/* Show the CPU simulation stop conditions.

   This display routine is called to show the set of CPU stop conditions or the
   indirect chain length limit.  The "st" parameter is the open output stream,
   and "val" is 1 to show the stops and 2 to show the indirect limit.  The other
   parameters are not used.

   To show stops, the routine searches through the stop table for status
   variables that are set to values other than SCPE_OK.  For each one it finds,
   the routine prints the corresponding stop name.  If none are found, it
   reports that all stops are disabled.

   This routine services an extended modifier entry, so it must add the trailing
   newline to the output before returning.
*/

static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
uint32 stop;
t_bool need_spacer = FALSE;

if (val == 2)                                           /* if the indirect limit is requested */
    fprintf (st, "Limit=%d\n", indirect_limit);         /*   then show it */

else {                                                      /* otherwise show the enabled stops */
    for (stop = 0; cpu_stop [stop].name != NULL; stop++)    /* loop through the set of stops in the table */
        if (*cpu_stop [stop].status != SCPE_OK) {           /* if the current stop is enabled */
            if (need_spacer)                                /*   then if a spacer is needed */
                fputc (';', st);                            /*     then add it first */
            else                                            /* otherwise this is the first one reported */
                fputs ("Stop=", st);                        /*   so print the report label */

            fputs (cpu_stop [stop].name, st);               /* report the stop name */

            need_spacer = TRUE;                             /* a spacer will be needed next time */
            }

    if (need_spacer)                                    /* if at least one simulation stop was enabled */
        fputc ('\n', st);                               /*   then add the required trailing newline */
    else                                                /* otherwise no enabled stops were found */
        fputs ("Stops disabled\n", st);                 /*   so report that all are disabled */
    }

return SCPE_OK;                                         /* report the success of the display */
}


/* Display the CPU model and optional loader status.

   Loader status is displayed for 21xx models and suppressed for 1000 models.
*/

static t_stat show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fputs ((const char *) desc, st);                        /* write model name */

if (! is_1000)                                          /* valid only for 21xx */
    if (fwanxm < MEMSIZE)                               /* loader area non-existent? */
        fputs (", loader disabled", st);                /* yes, so access disabled */
    else
        fputs (", loader enabled", st);                 /* no, so access enabled */

return SCPE_OK;
}


/* Show the instruction execution trace criteria.

   This display routine is called to show the criteria that select instruction
   execution tracing.  The "st" parameter is the open output stream.  The other
   parameters are not used.

   This routine services an extended modifier entry, so it must add the trailing
   newline to the output before returning.
*/

static t_stat show_exec (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
uint32 radix;

if (exec_mask == 0)                                     /* if the instruction is entirely masked */
    fputs ("Execution trace disabled\n", st);           /*   then report that matching is disabled */

else {                                                  /* otherwise */
    if (sim_switches & SWMASK ('O'))                    /*   if an octal override is present */
        radix = 8;                                      /*     then print the value in base 8 */
    else if (sim_switches & SWMASK ('D'))               /*   otherwise if a decimal override is present */
        radix = 10;                                     /*     then print the value in base 10 */
    else if (sim_switches & SWMASK ('H'))               /*   otherwise if a hex override is present */
        radix = 16;                                     /*     then print the value in base 16 */
    else                                                /*   otherwise */
        radix = cpu_dev.dradix;                         /*     use the current CPU data radix */

    fputs ("Execution trace match = ", st);                         /* print the label */
    fprint_val (st, exec_match, radix, cpu_dev.dwidth, PV_RZRO);    /*   and the match value */

    fputs (", mask = ", st);                                        /* print a separator */
    fprint_val (st, exec_mask, radix, cpu_dev.dwidth, PV_RZRO);     /*   and the mask value */

    fputc ('\n', st);                                               /* tie off the line */
    }

return SCPE_OK;                                         /* report the success of the display */
}


/* Show the current CPU simulation speed.

   This display routine is called to show the current simulation speed.  The
   "st" parameter is the open output stream.  The other parameters are not used.

   The CPU speed, expressed as a multiple of the hardware speed, is calculated
   by the time-base generator service routine.  It is only representative when
   the TBG is calibrated, and the CPU is not idling.
*/

static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "Simulation speed = %ux\n", cpu_speed);    /* display the current CPU speed */
return SCPE_OK;                                         /*   and report success */
}



/* CPU local utility routine declarations */


/* Get effective address from IR */

static t_stat ea (HP_WORD IR, HP_WORD *address, uint32 irq)
{
HP_WORD MA;

MA = IR & (I_IA | I_DISP);                              /* ind + disp */

if (IR & I_CP)                                          /* current page? */
    MA = ((PR - 1) & I_PAGENO) | MA;                    /* merge in page from P */

if (IR & I_IA)                                          /* if the address is indirect */
    return resolve (MA, address, irq);                  /*   then resolve it to a direct address */

else {                                                  /* otherwise the address is direct */
    *address = MA;                                      /*   so use it as is */
    return SCPE_OK;                                     /*     and return success */
    }
}


/* Execute a Shift/Rotate Group micro-operation.

   SRG instructions consist of two shift/rotate micro-operations plus a CLE and
   a SLA/SLB micro-op.  This routine implements the shift and rotate operation.

   Each of the two shift/rotate operations has an enable bit that must be set to
   enable the operation.  If the bit is not set, the operation is a NOP, with
   the exception that an ELA/ELB or ERA/ERB operation alters the E register (but
   not the A/B register).  We accommodate this by including the enable/disable
   bit with the three-bit operation code and decode the disabled operations of
   ELA/ELB and ERA/ERB separately from their enabled operations.

   On entry, "value" is the value of the selected accumulator (A/B), and
   "operation" is the micro-op and enable bit.  The routine returns the updated
   accumulator value and modifies the E register as indicated.


   Implementation notes:

    1. The enable bit is located adjacent to the three-bit encoded operation for
       the first shift/rotate micro-op, but it is spaced one bit away from the
       encoded operation for the second micro-op.  It is faster to decode
       separate values for each location rather than move the second enable bit
       adjacent to its encoded operation.  The former imposes no time penalty;
       the jump table for the "switch" statement is simply somewhat larger.
*/

static HP_WORD srg_uop (HP_WORD value, HP_WORD operation)
{
uint32 extend;

switch (operation) {                                       /* dispatch on the micro operation */

    case SRG1_EN | I_xLS:
    case SRG2_EN | I_xLS:                                   /* ALS/BLS */
        return value & D16_SIGN | value << 1 & D16_SMAX;    /* arithmetic left shift */

    case SRG1_EN | I_xRS:
    case SRG2_EN | I_xRS:                                   /* ARS/BRS */
        return value & D16_SIGN | value >> 1;               /* arithmetic right shift */

    case SRG1_EN | I_RxL:
    case SRG2_EN | I_RxL:                                   /* RAL/RBL */
        return (value << 1 | value >> 15) & D16_MASK;       /* rotate left */

    case SRG1_EN | I_RxR:
    case SRG2_EN | I_RxR:                                   /* RAR/RBR */
        return (value >> 1 | value << 15) & D16_MASK;       /* rotate right */

    case SRG1_EN | I_xLR:
    case SRG2_EN | I_xLR:                                   /* ALR/BLR */
        return value << 1 & D16_SMAX;                       /* arithmetic left shift, clear sign */

    case SRG_DIS | I_ERx:                                   /* disabled ERA/ERB */
        E = value & LSB;                                    /* rotate the LSB right into E */
        return value;                                       /*   and return the original value */

    case SRG1_EN | I_ERx:
    case SRG2_EN | I_ERx:                                   /* ERA/ERB */
        extend = E;                                         /* save the original E value */
        E = value & LSB;                                    /* rotate the LSB right into E */
        return value >> 1 | (HP_WORD) extend << 15;         /*   and rotate right with E filling the MSB */

    case SRG_DIS | I_ELx:                                   /* disabled ELA/ELB */
        E = value >> 15 & LSB;                              /* rotate the MSB left into E */
        return value;                                       /*   and return the original value */

    case SRG1_EN | I_ELx:
    case SRG2_EN | I_ELx:                                   /* ELA/ELB */
        extend = E;                                         /* save the original E value */
        E = value >> 15 & LSB;                              /* rotate the MSB left into E */
        return (value << 1 | (HP_WORD) extend) & D16_MASK;  /*   and rotate left with E filling the LSB */

    case SRG1_EN | I_xLF:
    case SRG2_EN | I_xLF:                                   /* ALF/BLF */
        return (value << 4 | value >> 12) & D16_MASK;       /* rotate left four */

    default:                                                /* all other (disabled) cases */
        return value;                                       /*   return the original value */
    }
}


/* Execute one machine instruction.

   This routine executes the CPU instruction present in the IR.  The CPU state
   (registers, memory, interrupt status) is modified as necessary, and the
   routine return SCPE_OK if the instruction executed successfully.  Any other
   status indicates that execution should cease, and control should return to
   the simulator console.  For example, a programmed HALT instruction returns
   STOP_HALT status.

   This routine implements the main instruction dispatcher.  Instructions
   corresponding to the MRG, SRG, and ASG are executed inline.  IOG, EAG, and
   UIG instructions are executed in external handlers.

   The JMP instruction executor handles CPU idling.  The 21xx/1000 CPUs have no
   "wait for interrupt" instruction.  Idling in HP operating systems consists of
   sitting in "idle loops" that end with JMP instructions.  We test for certain
   known patterns when a JMP instruction is executed to decide if the simulator
   should idle.  The recognized patterns are:

     for RTE-6/VM:
      - ISZ <n> / JMP *-1
      - mp_fence = 0
      - XEQT (address 1717B) = 0
      - DMS on with system map enabled
      - RTE verification: TBG (address 1674B) = CLK select code

     for RTE though RTE-IVB:
      - JMP *
      - mp_fence = 0
      - XEQT (address 1717B) = 0
      - DMS on with user map enabled (RTE-III through RTE-IVB only)
      - RTE verification: TBG (address 1674B) = CLK select code

     for DOS through DOS-III:
      - STF 0 / CCA / CCB / JMP *-3
      - DOS verification: A = B = -1, address 40B = -64, address 67B = +64
      - Note that in DOS, the TBG is set to 100 milliseconds

   Idling must not occur if an interrupt is pending.  As mentioned before, the
   CPU will defer pending interrupts when certain instructions are executed.  OS
   interrupt handlers exit via such deferring instructions.  If there is a
   pending interrupt when the OS is otherwise idle, the idle loop will execute
   one instruction before reentering the interrupt handler.  If we call
   sim_idle() in this case, we will lose interrupts.

   Consider the situation in RTE.  Under simulation, the TTY and CLK events are
   co-scheduled, with the CLK expiring one instruction after the TTY.  When the
   TTY interrupts, $CIC in RTE is entered.  One instruction later, the CLK
   expires and posts its interrupt, but it is not immediately handled, because
   the JSB $CIC,I / JMP $CIC0,I / SFS 0,C instruction entry sequence continually
   defers interrupts until the interrupt system is turned off.  When $CIC
   returns via $IRT, one instruction of the idle loop is executed, even though
   the CLK interrupt is still pending, because the UJP instruction used to
   return also defers interrupts.

   If "sim_idle" is called at this point, the simulator will sleep when it
   should be handling the pending CLK interrupt.  When it awakes, TTY expiration
   will be moved forward to the next instruction.  The still-pending CLK
   interrupt will then be recognized, and $CIC will be entered.  But the TTY and
   then the CLK will then expire and attempt to interrupt again, although they
   are deferred by the $CIC entry sequence.  This causes the second CLK
   interrupt to be missed, as processing of the first one is just now being
   started.

   Similarly, at the end of the CLK handling, the TTY interrupt is still
   pending.  When $IRT returns to the idle loop, "sim_idle" would be called
   again, so the TTY and then CLK interrupt a third time.  Because the second
   TTY interrupt is still pending, $CIC is entered, but the third TTY interrupt
   is lost.

   We solve this problem by testing for a pending interrupt before calling
   "sim_idle".  The system isn't really quiescent if it is just about to handle
   an interrupt.


   Implementation notes:

    1. Instruction decoding is based on the HP 1000, which does a 256-way branch
       on the upper eight bits of the instruction, as follows:

         15 14 13 12 11 10  9  8  Instruction Group
         -- -- -- -- -- -- -- --  ---------------------------------------
          x <-!= 0->  x  x  x  x  memory reference
          0  0  0  0  x  0  x  x  shift/rotate
          0  0  0  0  x  1  x  x  alter/skip
          1  0  0  0  x  1  x  x  I/O
          1  0  0  0  0  0  x  0  extended arithmetic
          1  0  0  0  0  0  0  1  divide (decoded as 100400)
          1  0  0  0  1  0  0  0  double load (decoded as 104000)
          1  0  0  0  1  0  0  1  double store (decoded as 104400)
          1  0  0  0  1  0  1  0  extended instr group 0 (A/B is set)
          1  0  0  0  x  0  1  1  extended instr group 1 (A/B is ignored)

    2. JSB is tricky.  It is possible to generate both an MP and a DM violation
       simultaneously, as the MP and MEM cards validate in parallel.  Consider a
       JSB to a location under the MP fence and on a write-protected page.  This
       situation must be reported as a DM violation, because it has priority
       (SFS 5 and SFC 5 check only the MEVFF, which sets independently of the MP
       fence violation).  Under simulation, this means that DM violations must
       be checked, and the MEVFF must be set, before an MP abort is taken.  This
       is done by the "mp_dms_jmp" routine.

    3. Although MR (and TR) will be changed by reads of an indirect chain, the
       idle loop JMP will be direct, and so MR will contain the correct value
       for the "idle loop omitted" trace message.

    4. The Alter/Skip Group RSS micro-op reverses the skip sense of the SEZ,
       SSA/SSB, SLA/SLB, and SZA/SZB micro-op tests.  Normally, the instruction
       skips if any test is true.  However, the specific combination of SSA/SSB,
       SLA/SLB, and RSS micro-ops causes a skip if BOTH of the skip cases are
       true, i.e., if both the MSB and LSB of the register value are ones.  We
       handle this as a special case, because without RSS, the instruction skips
       if EITHER the MSB or LSB is zero.  The other reversed skip cases (SEZ,RSS
       and SZA,RSS/SZB,RSS) are independent.
*/

static t_stat machine_instruction (HP_WORD IR, t_bool iotrap, uint32 irq_pending, uint32 *idle_save)
{
uint32  ab_selector, result, skip;
HP_WORD data, MA;
t_bool  rss;
t_stat  status = SCPE_OK;

switch (UPPER_BYTE (IR)) {                              /* dispatch on bits 15-8 of the instruction */

/* Memory Reference Group */

    case 0020: case 0021: case 0022: case 0023:
    case 0024: case 0025: case 0026: case 0027:         /* AND */
    case 0220: case 0221: case 0222: case 0223:
    case 0224: case 0225: case 0226: case 0227:         /* AND,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            AR = AR & ReadW (MA);                       /*   then AND the accumulator and memory */
        break;


    case 0230: case 0231: case 0232: case 0233:
    case 0234: case 0235: case 0236: case 0237:         /* JSB,I */
        ion_defer = TRUE;                               /* defer interrupts */

    /* fall into the JSB case */

    case 0030: case 0031: case 0032: case 0033:
    case 0034: case 0035: case 0036: case 0037:         /* JSB */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK) {                        /* if the address resolved */
            mp_dms_jmp (MA, jsb_plb);                   /*   then validate the jump address */

            WriteW (MA, PR);                            /* store P into the target memory address */

            PCQ_ENTRY;                                  /* save P in the queue */
            PR = MA + 1 & LA_MASK;                      /*   and jump to the word after the target address */
            }
        break;


    case 0040: case 0041: case 0042: case 0043:
    case 0044: case 0045: case 0046: case 0047:         /* XOR */
    case 0240: case 0241: case 0242: case 0243:
    case 0244: case 0245: case 0246: case 0247:         /* XOR,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            AR = AR ^ ReadW (MA);                       /*   then XOR the accumulator and memory */
        break;


    case 0250: case 0251: case 0252: case 0253:
    case 0254: case 0255: case 0256: case 0257:         /* JMP,I */
        ion_defer = TRUE;                               /* defer interrupts */

    /* fall into the JMP case */

    case 0050: case 0051: case 0052: case 0053:
    case 0054: case 0055: case 0056: case 0057:         /* JMP */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status != SCPE_OK)                          /* if the address failed to resolve */
            break;                                      /*   then abort execution */

        mp_dms_jmp (MA, 0);                             /* validate the jump address */

        PCQ_ENTRY;                                      /* save P in the queue */
        PR = MA;                                        /*   and jump to the target address */

        if (sim_idle_enab && irq_pending == 0                   /* if idle is enabled and no interrupt is pending */
          && ((PR == err_PC                                     /*   and the jump target is * (RTE through RTE-IVB) */
            || PR == err_PC - 1                                 /*   or the target is *-1 (RTE-6/VM) */
            && (mem_fast_read (PR, dms_ump) & I_MRG) == I_ISZ)  /*     and *-1 is ISZ <n> */
          && mp_fence == 0                                      /*   and the MP fence is zero */
          && M [xeqt] == 0                                      /*   and no program is executing */
          && M [tbg] == clk_dib.select_code)                    /*   and the TBG select code is set */

          || PR == err_PC - 3                                   /*   or the jump target is *-3 (DOS through DOS-III) */
          && M [PR] == I_STF                                    /*   and *-3 is STF 0 */
          && AR == 0177777                                      /*   and the A and B registers */
          && BR == 0177777                                      /*     are both set to -1 */
          && M [m64] == 0177700                                 /*   and the -64 and +64 base-page constants */
          && M [p64] == 0000100) {                              /*     are set as expected */
            tprintf (cpu_dev, cpu_dev.dctrl,
                     DMS_FORMAT "idle loop execution omitted\n",
                     meu_indicator, meu_page, MR, IR);

            if (cpu_dev.dctrl != 0) {                   /* if tracing is enabled */
                *idle_save = cpu_dev.dctrl;             /*   then save the current trace flag set */
                cpu_dev.dctrl = 0;                      /*     and turn off tracing for the idle loop */
                }

            sim_idle (TMR_POLL, FALSE);                 /* idle the simulator */
            }
        break;


    case 0060: case 0061: case 0062: case 0063:
    case 0064: case 0065: case 0066: case 0067:         /* IOR */
    case 0260: case 0261: case 0262: case 0263:
    case 0264: case 0265: case 0266: case 0267:         /* IOR,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            AR = AR | ReadW (MA);                       /*   then OR the accumulator and memory */
        break;


    case 0070: case 0071: case 0072: case 0073:
    case 0074: case 0075: case 0076: case 0077:         /* ISZ */
    case 0270: case 0271: case 0272: case 0273:
    case 0274: case 0275: case 0276: case 0277:         /* ISZ,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK) {                        /* if the address resolved */
            data = ReadW (MA) + 1 & D16_MASK;           /*   then increment the memory word */
            WriteW (MA, data);                          /*     and write it back */

            if (data == 0)                              /* if the value rolled over to zero */
                PR = PR + 1 & LA_MASK;                  /*   then increment P */
            }
        break;


    case 0100: case 0101: case 0102: case 0103:
    case 0104: case 0105: case 0106: case 0107:         /* ADA */
    case 0300: case 0301: case 0302: case 0303:
    case 0304: case 0305: case 0306: case 0307:         /* ADA,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK) {                        /* if the address resolved */
            data = ReadW (MA);                          /*   then get the target word */
            result = AR + data;                         /*     and add the accumulator to memory */

            if (result > D16_UMAX)                      /* if the result overflowed */
                E = 1;                                  /*   then set the Extend register */

            if (~(AR ^ data) & (AR ^ result) & D16_SIGN)    /* if the sign of the result differs from the signs */
                O = 1;                                      /*   of the operands, then set the Overflow register */

            AR = result & R_MASK;                       /* store the sum into the accumulator */
            }
        break;


    case 0110: case 0111: case 0112: case 0113:
    case 0114: case 0115: case 0116: case 0117:         /* ADB */
    case 0310: case 0311: case 0312: case 0313:
    case 0314: case 0315: case 0316: case 0317:         /* ADB,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK) {                        /* if the address resolved */
            data = ReadW (MA);                          /*   then get the target word */
            result = BR + data;                         /*     and add the accumulator to memory */

            if (result > D16_UMAX)                      /* if the result overflowed */
                E = 1;                                  /*   then set the Extend register */

            if (~(BR ^ data) & (BR ^ result) & D16_SIGN)    /* if the sign of the result differs from the signs */
                O = 1;                                      /*   of the operands, then set the Overflow register */

            BR = result & R_MASK;                       /* store the sum into the accumulator */
            }
        break;


    case 0120: case 0121: case 0122: case 0123:
    case 0124: case 0125: case 0126: case 0127:         /* CPA */
    case 0320: case 0321: case 0322: case 0323:
    case 0324: case 0325: case 0326: case 0327:         /* CPA,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            if (AR != ReadW (MA))                       /*   then if the accumulator and memory differ */
                PR = PR + 1 & LA_MASK;                  /*     then increment P */
        break;


    case 0130: case 0131: case 0132: case 0133:
    case 0134: case 0135: case 0136: case 0137:         /* CPB */
    case 0330: case 0331: case 0332: case 0333:
    case 0334: case 0335: case 0336: case 0337:         /* CPB,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            if (BR != ReadW (MA))                       /*   then if the accumulator and memory differ */
                PR = PR + 1 & LA_MASK;                  /*     then increment P */
        break;


    case 0140: case 0141: case 0142: case 0143:
    case 0144: case 0145: case 0146: case 0147:         /* LDA */
    case 0340: case 0341: case 0342: case 0343:
    case 0344: case 0345: case 0346: case 0347:         /* LDA,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            AR = ReadW (MA);                            /*   then load the accumulator from memory */
        break;


    case 0150: case 0151: case 0152: case 0153:
    case 0154: case 0155: case 0156: case 0157:         /* LDB */
    case 0350: case 0351: case 0352: case 0353:
    case 0354: case 0355: case 0356: case 0357:         /* LDB,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            BR = ReadW (MA);                            /*   then load the accumulator from memory */
        break;


    case 0160: case 0161: case 0162: case 0163:
    case 0164: case 0165: case 0166: case 0167:         /* STA */
    case 0360: case 0361: case 0362: case 0363:
    case 0364: case 0365: case 0366: case 0367:         /* STA,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            WriteW (MA, AR);                            /*   then write the accumulator to memory */
        break;


    case 0170: case 0171: case 0172: case 0173:
    case 0174: case 0175: case 0176: case 0177:         /* STB */
    case 0370: case 0371: case 0372: case 0373:
    case 0374: case 0375: case 0376: case 0377:         /* STB,I */
        status = ea (IR, &MA, irq_pending);             /* get the effective address */

        if (status == SCPE_OK)                          /* if the address resolved */
            WriteW (MA, BR);                            /*   then write the accumulator to memory */
        break;


/* Alter/Skip Group */

    case 0004: case 0005: case 0006: case 0007:
    case 0014: case 0015: case 0016: case 0017:         /* ASG */
        skip = 0;                                       /* assume that no skip is needed */

        rss = (IR & I_RSS) != 0;                        /* get the Reverse Skip Sense flag */

        ab_selector = (IR & I_AB ? 1 : 0);              /* get the A/B register selector */
        data = ABREG [ab_selector];                     /*   and the register data */

        if (IR & I_CLx)                                 /* if the CLA/CLB micro-op is enabled */
            data = 0;                                   /*   then clear the value */

        if (IR & I_CMx)                                 /* if the CMA/CMB micro-op is enabled */
            data = data ^ D16_MASK;                     /*   then complement the value */

        if (IR & I_SEZ && (E == 0) ^ rss)               /* if SEZ[,RSS] is enabled and E is clear [set] */
            skip = 1;                                   /*   then skip the next instruction */

        if (IR & I_CLE)                                 /* if the CLE micro-op is enabled */
            E = 0;                                      /*   then clear E */

        if (IR & I_CME)                                 /* if the CME micro-op is enabled */
            E = E ^ LSB;                                /*   then complement E */

        if ((IR & I_SSx_SLx_RSS) == I_SSx_SLx_RSS) {    /* if the SSx, SLx, and RSS micro-ops are enabled together */
            if ((data & D16_SIGN_LSB) == D16_SIGN_LSB)  /*   then if both sign and least-significant bits are set */
                skip = 1;                               /*     then skip the next instruction */
            }

        else {                                          /* otherwise */
            if (IR & I_SSx && !(data & D16_SIGN) ^ rss) /*   if SSx[,RSS] is enabled and the MSB is clear [set] */
                skip = 1;                               /*     then skip the next instruction */

            if (IR & I_SLx && !(data & LSB) ^ rss)      /*   if SLx[,RSS] is enabled and the LSB is clear [set] */
                skip = 1;                               /*     then skip the next instruction */
            }

        if (IR & I_INx) {                               /* if the INA/INB micro-op is enabled */
            data = data + 1 & D16_MASK;                 /*   then increment the value */

            if (data == 0)                              /* if the value wrapped around to zero */
                E = 1;                                  /*   then set the Extend register */

            else if (data == D16_SIGN)                  /* otherwise if the value overflowed into the sign bit */
                O = 1;                                  /*   then set the Overflow register */
            }

        if (IR & I_SZx && (data == 0) ^ rss)            /* if SZx[,RSS] is enabled and the value is zero [non-zero] */
            skip = 1;                                   /*   then skip the next instruction */

        if ((IR & I_ALL_SKIPS) == I_RSS)                /* if RSS is present without any other skip micro-ops */
            skip = 1;                                   /*   then skip the next instruction unconditionally */

        ABREG [ab_selector] = data;                     /* store the result in the selected register */
        PR = PR + skip & LA_MASK;                       /*   and skip the next instruction if indicated */
        break;


/* Shift/Rotate Group */

    case 0000: case 0001: case 0002: case 0003:
    case 0010: case 0011: case 0012: case 0013:         /* SRG */
        ab_selector = (IR & I_AB ? 1 : 0);              /* get the A/B register selector */
        data = ABREG [ab_selector];                     /*   and the register data */

        data = srg_uop (data, SRG1 (IR));               /* do the first shift */

        if (IR & SRG_CLE)                               /* if the CLE micro-op is enabled */
            E = 0;                                      /*   then clear E */

        if (IR & SRG_SLx && (data & LSB) == 0)          /* if SLx is enabled and the LSB is clear */
            PR = PR + 1 & LA_MASK;                      /*   then skip the next instruction */

        ABREG [ab_selector] = srg_uop (data, SRG2 (IR));    /* do the second shift and set the accumulator */
        break;


/* I/O Group */

    case 0204: case 0205: case 0206: case 0207:
    case 0214: case 0215: case 0216: case 0217:         /* IOG */
        status = cpu_iog (IR, iotrap);                  /* execute the I/O instruction */
        break;


/* Extended Arithmetic Group */

    case 0200:                                          /* EAU group 0 */
    case 0201:                                          /* DIV */
    case 0202:                                          /* EAU group 2 */
    case 0210:                                          /* DLD */
    case 0211:                                          /* DST */
        status = cpu_eau (IR, irq_pending);             /* execute the extended arithmetic instruction */
        break;


/* User Instruction Group */

    case 0212:                                          /* UIG 0 */
        status = cpu_uig_0 (IR, irq_pending, iotrap);   /* execute the user instruction opcode */
        break;

    case 0203:
    case 0213:                                          /* UIG 1 */
        status = cpu_uig_1 (IR, irq_pending, iotrap);   /* execute the user instruction opcode */
        break;

    }                                                   /* all cases are handled */


return status;                                          /* return the execution status */
}


/* Determine whether a pending interrupt deferral should be inhibited.

   Execution of certain instructions generally cause a pending interrupt to be
   deferred until the succeeding instruction completes.  However, the interrupt
   deferral rules differ for the 21xx vs. the 1000.

   The 1000 always defers until the completion of the instruction following a
   deferring instruction.  The 21xx defers unless the following instruction is
   an MRG instruction other than JMP or JMP,I or JSB,I.  If it is, then the
   deferral is inhibited, i.e., the pending interrupt will be serviced.

   In either case, if the interrupting device is the memory protect card, or if
   the INT jumper is out on the 12892B MP card, then interrupts are not
   deferred.

   See the "Set Phase Logic Flowchart" for the transition from phase 1A to phase
   1B, and "Section III Theory of Operation," "Control Section Detailed Theory"
   division, "Phase Control Logic" subsection, "Phase 1B" paragraph (3-241) in
   the Model 2100A Computer Installation and Maintenance Manual for details.
*/

static t_bool check_deferral (uint32 irq_sc)
{
HP_WORD next_instruction;

if (! is_1000) {                                        /* if the CPU is a 21xx model */
    next_instruction = mem_fast_read (PR, dms_ump);     /*   then prefetch the next instruction */

    if (MRGOP (next_instruction)                        /* if it is an MRG instruction */
      && (next_instruction & I_MRG_I) != I_JSB_I        /*   but not JSB,I? */
      && (next_instruction & I_MRG)   != I_JMP)         /*   and not JMP or JMP,I */
        return FALSE;                                   /*     then inhibit deferral */
    }

if (irq_sc == PRO                                       /* if memory protect is interrupting */
  || mp_unit.flags & UNIT_MP_INT && mp_control)         /*   or the INT jumper is out for the 12892B card */
    return FALSE;                                       /*     then inhibit deferral */
else                                                    /* otherwise */
    return TRUE;                                        /*   deferral is permitted */
}


/* Logical-to-physical address translation for console access.

   This routine translates a logical address interpreted in the context of the
   translation map implied by the specified switch to a physical address.  It is
   called to map addresses when the user is examining or depositing memory.  It
   is also called to restore a saved configuration, although mapping is not used
   for restoration.  All memory protection checks are off for console access.

   Command line switches modify the interpretation of logical addresses as
   follows:

     Switch  Meaning
     ------  --------------------------------------------------
       -N    Use the address directly with no mapping
       -S    If memory expansion is enabled, use the system map
       -U    If memory expansion is enabled, use the user map
       -P    If memory expansion is enabled, use the port A map
       -Q    If memory expansion is enabled, use the port B map

   If no switch is specified, the address is interpreted using the current map
   if memory expansion is enabled; otherwise, the address is not mapped.  If the
   current or specified map is used, then the address must lie within the 32K
   logical address space; if not, then an address larger than the current memory
   size is returned.
*/

static uint32 map_address (HP_WORD logical, int32 switches)
{
uint32 map;

if (switches & (SWMASK ('N') | SIM_SW_REST))            /* if no mapping is requested */
    return logical;                                     /*   then the address is already a physical address */

else if ((dms_enb || switches & ALL_MAPMODES)           /* otherwise if mapping is enabled or requested */
  && logical > LA_MAX)                                  /*   and the address is not a logical address */
    return (uint32) MEMSIZE;                            /*     then report a memory overflow */

else if (switches & SWMASK ('S'))                       /* otherwise if the -S switch is specified */
    map = SMAP;                                         /*   then use the system map */

else if (switches & SWMASK ('U'))                       /* otherwise if the -U switch is specified */
    map = UMAP;                                         /*   then use the user map */

else if (switches & SWMASK ('P'))                       /* otherwise if the -P switch is specified */
    map = PAMAP;                                        /*   then use the DCPC port A map */

else if (switches & SWMASK ('Q'))                       /* otherwise if the -Q switch is specified */
    map = PBMAP;                                        /*   then use the DCPC port B map */

else                                                    /* otherwise */
    map = dms_ump;                                      /*   use the current map (system or user) */

return meu_map (logical, map, NOPROT);                  /* translate the address without protection */
}


/* Check for non-zero value in a memory address range.

   A range of memory locations is checked for the presence of a non-zero value.
   The starting address of the range is supplied, and the check continues
   through the end of defined memory.  The routine returns TRUE if the memory
   range was empty (i.e., contained only zero values) and FALSE otherwise.
*/

static t_bool mem_is_empty (uint32 starting_address)
{
uint32 address;

for (address = starting_address; address < MEMSIZE; address++)  /* loop through the specified address range */
    if (M [address] != 0)                                       /* if this location is non-zero */
        return FALSE;                                           /*   then indicate that memory is not empty */

return TRUE;                                            /* return TRUE if all locations contain zero values */
}



/* Memory Expansion Unit local utility routine declarations */


/* Mapped access check.

   Return TRUE if the address will be mapped (presuming MEM is enabled).
*/

static t_bool is_mapped (uint32 address)
{
uint32 dms_fence;

if (address >= 02000u)                                  /* if the address is not on the base page */
    return TRUE;                                        /*   then it is always mapped */

else {                                                  /* otherwise */
    dms_fence = dms_sr & MST_FENCE;                     /*   get the base-page fence value */

    if (dms_sr & MST_FLT)                               /* if the lower portion is mapped */
        return (address < dms_fence);                   /*   then return TRUE if the address is below the fence */
    else                                                /* otherwise the upper portion is mapped */
        return (address >= dms_fence);                  /*   so return TRUE if the address is at or above the fence */
    }
}


/* Map a logical address to a physical address..

   This routine translates logical into physical addresses.  The logical
   address, desired map, and desired access protection are supplied.  If the
   access is legal, the mapped physical address is returned; if it is not, then
   a MEM violation is indicated.

   The current map may be specified by passing "dms_ump" as the "map" parameter,
   or a specific map may be used.  Normally, read and write accesses pass RDPROT
   or WRPROT as the "prot" parameter to request access checking.  For DMA
   accesses, NOPROT must be passed to inhibit access checks.

   This routine checks for read, write, and base-page violations and will call
   "dms_viol" as appropriate.  The latter routine will abort if MP is enabled,
   or will return if protection is off.
*/

static uint32 meu_map (HP_WORD address, uint32 map, HP_WORD prot)
{
uint32 map_register;

if (dms_enb) {                                          /* if the Memory Expansion Unit is enabled */
    if (address <= 1 && map < PAMAP) {                  /*   then if the reference is to the A or B register */
        meu_page = 0;                                   /*     then the physical page is page 0 */
        return address;                                 /*       and the address is already physical */
        }

    else if (is_mapped (address) == FALSE) {            /* otherwise if a base-page address is not mapped */
        meu_page = 0;                                   /*   then the physical page is page 0 */

        if (address > 1 && prot == WRPROT)              /* a write to the unmapped part of the base page */
            dms_viol (address, MVI_BPG);                /*   causes a base-page violation if protection is enabled */

        return address;                                 /* the address is already physical */
        }

    else {                                                  /* otherwise the address is mapped */
        map_register = dms_map [map + VA_GETPAG (address)]; /*   so get the map register for the logical page */

        meu_page = MAP_PAGE (map_register);                 /* save the physical page number */
        meu_indicator = map_indicator [map / MAP_LNT];      /*   and set the map indicator the the applied map */

        if (map_register & prot)                            /* if the desired access is not allowed */
            dms_viol (address, prot);                       /*   then a read or write protection violation occurs */

        return TO_PAGE (meu_page) | VA_GETOFF (address);    /* form the physical address from the mapped page and offset */
        }
    }

else {                                                  /* otherwise the MEU is disabled */
    meu_page = VA_GETPAG (address);                     /*   so the physical page is the logical page */
    meu_indicator = '-';                                /* set the map indicator to indicate no mapping */

    return address;                                     /* the physical address is the logical address */
    }
}



/* DMA local SCP support routine declarations */


/* DMA/DCPC primary (SC 6/7) I/O signal handler.

   The primary DMA control interface and the service select register are
   manipulated through select codes 6 and 7.  Each channel has transfer enable,
   control, flag, and flag buffer flip-flops.  Transfer enable must be set via
   STC to start DMA.  Control is used only to enable the DMA completion
   interrupt; it is set by STC and cleared by CLC.  Flag and flag buffer are set
   at transfer completion to signal an interrupt.  STF may be issued to abort a
   transfer in progress.

   Again, there are hardware differences between the various DMA cards.  The
   12607B (2114) stores only bits 2-0 of the select code and interprets them as
   select codes 10-16 (SRQ17 is not decoded).  The 12578A (2115/16), 12895A
   (2100), and 12897B (1000) support the full range of select codes (10-77
   octal).


   Implementation notes:

     1. An IOI reads the floating S-bus (high on the 1000, low on the 21xx).

     2. The CRS signal on the DMA card resets the secondary (SC 2/3) select
        flip-flops.  Under simulation, ioCRS is dispatched to select codes 6 and
        up, so we reset the flip-flop in our handler.

     3. The 12578A supports byte-sized transfers by setting bit 14.  Bit 14 is
        ignored by all other DMA cards, which support word transfers only.
        Under simulation, we use a byte-packing/unpacking register to hold one
        byte while the other is read or written during the DMA cycle.
*/

static uint32 dmapio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
const CHANNEL ch = (CHANNEL) dibptr->card_index;        /* DMA channel number */
uint16 data;
IOSIGNAL signal;
IOCYCLE  working_set = IOADDSIR (signal_set);           /* add ioSIR if needed */

while (working_set) {
    signal = IONEXT (working_set);                      /* isolate next signal */

    switch (signal) {                                   /* dispatch I/O signal */

        case ioCLF:                                     /* clear flag flip-flop */
            dma [ch].flag = dma [ch].flagbuf = CLEAR;   /* clear flag and flag buffer */
            break;

        case ioSTF:                                     /* set flag flip-flop */
        case ioENF:                                     /* enable flag */
            if (dma [ch].xferen == SET)
                tpprintf (dma_dptrs [ch], TRACE_CMD, "Channel transfer %s\n",
                          (dma [ch].cw3 == 0 ? "completed" : "aborted"));

            dma [ch].flag = dma [ch].flagbuf = SET;     /* set flag and flag buffer */
            dma [ch].xferen = CLEAR;                    /* clear transfer enable to abort transfer */
            break;

        case ioSFC:                                     /* skip if flag is clear */
            setstdSKF (dma [ch]);                       /* skip if transfer in progress */
            break;

        case ioSFS:                                     /* skip if flag is set */
            setstdSKF (dma [ch]);                       /* skip if transfer is complete */
            break;

        case ioIOI:                                     /* I/O data input */
            if (is_1000)                                /* 1000? */
                stat_data = IORETURN (SCPE_OK, DMASK);  /* return all ones */
            else                                        /* other models */
                stat_data = IORETURN (SCPE_OK, 0);      /* return all zeros */
            break;

        case ioIOO:                                     /* I/O data output */
            data = IODATA (stat_data);                  /* clear supplied status */

            if (UNIT_CPU_MODEL == UNIT_2114)            /* 12607? */
                dma [ch].cw1 = (data & 0137707) | 010;  /* mask SC, convert to 10-17 */
            else if (UNIT_CPU_TYPE == UNIT_TYPE_211X)   /* 12578? */
                dma [ch].cw1 = data;                    /* store full select code, flags */
            else                                        /* 12895, 12897 */
                dma [ch].cw1 = data & ~DMA1_PB;         /* clip byte-packing flag */

            tpprintf (dma_dptrs [ch], TRACE_CSRW, "Control word 1 is %sselect code %02o\n",
                      fmt_bitset (data, dma_cw1_format), data & I_DEVMASK);
            break;

       case ioPOPIO:                                    /* power-on preset to I/O */
            dma [ch].flag = dma [ch].flagbuf = SET;     /* set flag and flag buffer */
            break;

        case ioCRS:                                     /* control reset */
            dma [ch].xferen = CLEAR;                    /* clear transfer enable */
            dma [ch].select = CLEAR;                    /* set secondary for word count access */
                                                        /* fall into CLC handler */

        case ioCLC:                                     /* clear control flip-flop */
            dma [ch].control = CLEAR;                   /* clear control */

            if (dma [ch].xferen == SET)
                tpprintf (dma_dptrs [ch], TRACE_CMD, "Channel completion interrupt is inhibited\n");
            break;

        case ioSTC:                                     /* set control flip-flop */
            dma [ch].packer = 0;                        /* clear packing register */
            dma [ch].xferen = dma [ch].control = SET;   /* set transfer enable and control */

            if (dma [ch].cw2 & DMA2_OI)
                tpprintf (dma_dptrs [ch], TRACE_CMD,
                          "Channel transfer of %u words from select code %02o to address %05o started\n",
                          NEG16 (dma [ch].cw3), dma [ch].cw1 & I_DEVMASK, dma [ch].cw2 & VAMASK);
            else
                tpprintf (dma_dptrs [ch], TRACE_CMD,
                          "Channel transfer of %u words from address %05o to select code %02o started\n",
                          NEG16 (dma [ch].cw3), dma [ch].cw2 & VAMASK, dma [ch].cw1 & I_DEVMASK);
            break;

        case ioSIR:                                     /* set interrupt request */
            setstdPRL (dma [ch]);
            setstdIRQ (dma [ch]);
            break;

        case ioIAK:                                     /* interrupt acknowledge */
            dma [ch].flagbuf = CLEAR;                   /* clear flag buffer */
            break;

        default:                                        /* all other signals */
            break;                                      /*   are ignored */
        }

    working_set = working_set & ~signal;                /* remove current signal from set */
    }

return stat_data;
}


/* DMA/DCPC secondary (SC 2/3) I/O signal handler.

   DMA consists of one (12607B) or two (12578A/12895A/12897B) channels.  Each
   channel uses two select codes: 2 and 6 for channel 1, and 3 and 7 for channel
   2.  The lower select codes are used to configure the memory address register
   (control word 2) and the word count register (control word 3).  The upper
   select codes are used to configure the service select register (control word
   1) and to activate and terminate the transfer.

   There are differences in the implementations of the memory address and word
   count registers among the various cards.  The 12607B (2114) supports 14-bit
   addresses and 13-bit word counts.  The 12578A (2115/6) supports 15-bit
   addresses and 14-bit word counts.  The 12895A (2100) and 12897B (1000)
   support 15-bit addresses and 16-bit word counts.


   Implementation notes:

    1. Because the I/O bus floats to zero on 211x computers, an IOI (read word
       count) returns zeros in the unused bit locations, even though the word
       count is a negative value.

    2. Select codes 2 and 3 cannot interrupt, so there is no SIR handler.
*/

static uint32 dmasio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
const CHANNEL ch = (CHANNEL) dibptr->card_index;        /* DMA channel number */
uint16 data;
IOSIGNAL signal;
IOCYCLE  working_set = signal_set;                      /* no SIR handler needed */

while (working_set) {
    signal = IONEXT (working_set);                      /* isolate next signal */

    switch (signal) {                                   /* dispatch I/O signal */

        case ioIOI:                                     /* I/O data input */
            if (UNIT_CPU_MODEL == UNIT_2114)            /* 2114? */
                data = dma [ch].cw3 & 0017777;          /* only 13-bit count */
            else if (UNIT_CPU_TYPE == UNIT_TYPE_211X)   /* 2115/2116? */
                data = dma [ch].cw3 & 0037777;          /* only 14-bit count */
            else                                        /* other models */
                data = (uint16) dma [ch].cw3;           /* rest use full value */

            stat_data = IORETURN (SCPE_OK, data);       /* merge status and remaining word count */

            tpprintf (dma_dptrs [ch], TRACE_CSRW, "Remaining word count is %u\n",
                      NEG16 (dma [ch].cw3));
            break;

        case ioIOO:                                                 /* I/O data output */
            if (dma [ch].select) {                                  /* word count selected? */
                dma [ch].cw3 = IODATA (stat_data);                  /* save count */

                tpprintf (dma_dptrs [ch], TRACE_CSRW, "Control word 3 is word count %u\n",
                          NEG16 (dma [ch].cw3));
                }

            else {                                                  /* memory address selected */
                if (UNIT_CPU_MODEL == UNIT_2114)                    /* 2114? */
                    dma [ch].cw2 = IODATA (stat_data) & 0137777;    /* only 14-bit address */
                else                                                /* other models */
                    dma [ch].cw2 = IODATA (stat_data);              /* full address stored */

                tpprintf (dma_dptrs [ch], TRACE_CSRW, "Control word 2 is %s address %05o\n",
                          (dma [ch].cw2 & DMA2_OI ? "input to" : "output from"),
                          dma [ch].cw2 & VAMASK);
                }
            break;

        case ioCLC:                                     /* clear control flip-flop */
            dma [ch].select = CLEAR;                    /* set for word count access */
            break;

        case ioSTC:                                     /* set control flip-flop */
            dma [ch].select = SET;                      /* set for memory address access */
            break;

        default:                                        /* all other signals */
            break;                                      /*   are ignored */
        }

    working_set = working_set & ~signal;                /* remove current signal from set */
    }

return stat_data;
}


/* DMA reset */

static t_stat dma_reset (DEVICE *dptr)
{
DIB *dibptr = (DIB *) dptr->ctxt;                       /* DIB pointer */
const CHANNEL ch = (CHANNEL) dibptr->card_index;        /* DMA channel number */

if (UNIT_CPU_MODEL != UNIT_2114)                        /* 2114 has only one channel */
    hp_enbdis_pair (dma_dptrs [ch],                     /* make specified channel */
                    dma_dptrs [ch ^ 1]);                /*   consistent with other channel */

if (sim_switches & SWMASK ('P')) {                      /* power-on reset? */
    dma [ch].cw1 = 0;                                   /* clear control word registers */
    dma [ch].cw2 = 0;
    dma [ch].cw3 = 0;
    }

IOPRESET (dibptr);                                      /* PRESET device (does not use PON) */

dma [ch].packer = 0;                                    /* clear byte packer */

return SCPE_OK;
}



/* DMA local utility routine declarations */


/* DMA cycle routine.

   This routine performs one DMA input or output cycle using the indicated DMA
   channel number and DMS map.  When the transfer word count reaches zero, the
   flag is set on the corresponding DMA channel to indicate completion.

   The 12578A card supports byte-packing.  If bit 14 in control word 1 is set,
   each transfer will involve one read/write from memory and two output/input
   operations in order to transfer sequential bytes to/from the device.

   DMA I/O cycles differ from programmed I/O cycles in that multiple I/O control
   backplane signals may be asserted simultaneously.  With programmed I/O, only
   CLF may be asserted with other signals, specifically with STC, CLC, SFS, SFC,
   IOI, or IOO.  With DMA, as many as five signals may be asserted concurrently.

   DMA I/O timing looks like this:

           ------------ Input ------------   ----------- Output ------------
     Sig    Normal Cycle      Last Cycle      Normal Cycle      Last Cycle
     ===   ==============   ==============   ==============   ==============
     IOI   T2-T3            T2-T3
     IOO                                        T3-T4            T3-T4
     STC *    T3                                T3               T3
     CLC *                     T3-T4                             T3-T4
     CLF      T3                                T3               T3
     EDT                          T4                                T4

      * if enabled by control word 1

   Under simulation, this routine dispatches one set of I/O signals per DMA
   cycle to the target device's I/O signal handler.  The signals correspond to
   the table above, except that all signals for a given cycle are concurrent
   (e.g., the last input cycle has IOI, EDT, and optionally CLC asserted, even
   though IOI and EDT are not coincident in hardware).  I/O signal handlers will
   process these signals sequentially, in the order listed above, before
   returning.


   Implementation notes:

    1. The address increment and word count decrement is done only after the I/O
       cycle has completed successfully.  This allows a failed transfer to be
       retried after correcting the I/O error.
*/

static t_stat dma_cycle (CHANNEL ch, ACCESS_CLASS class)
{
const uint32  dev   = dma [ch].cw1 & I_DEVMASK;          /* device select code */
const uint32  stc   = dma [ch].cw1 & DMA1_STC;           /* STC enable flag */
const uint32  bytes = dma [ch].cw1 & DMA1_PB;            /* pack bytes flag */
const uint32  clc   = dma [ch].cw1 & DMA1_CLC;           /* CLC enable flag */
const HP_WORD MA    = dma [ch].cw2 & VAMASK;             /* memory address */
const HP_WORD input = dma [ch].cw2 & DMA2_OI;            /* input flag */
const uint32  even  = dma [ch].packer & DMA_OE;          /* odd/even packed byte flag */
HP_WORD data;
t_stat status;
uint32 ioresult;
IOCYCLE signals;

if (bytes && !even || dma [ch].cw3 != DMASK) {          /* normal cycle? */
    if (input)                                          /* input cycle? */
        signals = ioIOI | ioCLF;                        /* assert IOI and CLF */
    else                                                /* output cycle */
        signals = ioIOO | ioCLF;                        /* assert IOO and CLF */

    if (stc)                                            /* STC wanted? */
        signals = signals | ioSTC;                      /* assert STC */
    }

else {                                                  /* last cycle */
    if (input)                                          /* input cycle? */
        signals = ioIOI | ioEDT;                        /* assert IOI and EDT */
    else {                                              /* output cycle */
        signals = ioIOO | ioCLF | ioEDT;                /* assert IOO and CLF and EDT */

        if (stc)                                        /* STC wanted? */
            signals = signals | ioSTC;                  /* assert STC */
        }

    if (clc)                                            /* CLC wanted? */
        signals = signals | ioCLC;                      /* assert CLC */
    }

if (input) {                                            /* input cycle? */
    ioresult = io_dispatch (dev, signals, 0);           /* do I/O input */

    status = IOSTATUS (ioresult);                       /* get cycle status */

    if (status == SCPE_OK) {                            /* good I/O cycle? */
        data = IODATA (ioresult);                       /* extract return data value */

        if (bytes) {                                    /* byte packing? */
            if (even) {                                 /* second byte? */
                data = (uint16) (dma [ch].packer << 8)  /* merge stored byte */
                         | (data & DMASK8);
                mem_write (dma_dptrs [ch], class, MA, data);    /* store word data */
                }
            else                                        /* first byte */
                dma [ch].packer = (data & DMASK8);      /* save it */

            dma [ch].packer = dma [ch].packer ^ DMA_OE; /* flip odd/even bit */
            }
        else                                            /* no byte packing */
            mem_write (dma_dptrs [ch], class, MA, data);    /* store word data */
        }
    }

else {                                                  /* output cycle */
    if (bytes) {                                        /* byte packing? */
        if (even)                                       /* second byte? */
            data = dma [ch].packer & DMASK8;            /* retrieve it */

        else {                                          /* first byte */
            dma [ch].packer = mem_read (dma_dptrs [ch], class, MA);         /* read word data */
            data = (dma [ch].packer >> 8) & DMASK8;     /* get high byte */
            }

        dma [ch].packer = dma [ch].packer ^ DMA_OE;     /* flip odd/even bit */
        }
    else                                                /* no byte packing */
        data = mem_read (dma_dptrs [ch], class, MA);    /* read word data */

    ioresult = io_dispatch (dev, signals, data);        /* do I/O output */

    status = IOSTATUS (ioresult);                       /* get cycle status */
    }

if ((even || !bytes) && (status == SCPE_OK)) {          /* new byte or no packing and good xfer? */
    dma [ch].cw2 = input | (dma [ch].cw2 + 1) & VAMASK; /* increment address */
    dma [ch].cw3 = (dma [ch].cw3 + 1) & DMASK;          /* increment word count */

    if (dma [ch].cw3 == 0)                              /* end of transfer? */
        dmapio (dibs [DMA1 + ch], ioENF, 0);            /* set DMA channel flag */
    }

return status;                                          /* return I/O status */
}


/* Calculate DMA requests */

static uint32 calc_dma (void)
{
uint32 r = 0;

if (dma [ch1].xferen && SRQ (dma [ch1].cw1 & I_DEVMASK)) {  /* check DMA1 cycle */
    r = r | DMA_1_REQ;

    tprintf (dma1_dev, TRACE_SR, "Select code %02o asserted SRQ\n",
             dma [ch1].cw1 & I_DEVMASK);
    }

if (dma [ch2].xferen && SRQ (dma [ch2].cw1 & I_DEVMASK)) {  /* check DMA2 cycle */
    r = r | DMA_2_REQ;

    tprintf (dma2_dev, TRACE_SR, "Select code %02o asserted SRQ\n",
             dma [ch2].cw1 & I_DEVMASK);
    }

return r;
}



/* Memory Protect local SCP support routine declarations */


/* Memory protect/parity error (SC 5) I/O signal handler.

   The memory protect card has a number of non-standard features:

    - CLF and STF affect the parity error enable flip-flop, not the flag
    - SFC and SFS test the memory expansion violation flip-flop, not the flag
    - POPIO clears control, flag, and flag buffer instead of setting the flags
    - CLC does not clear control (the only way to turn off MP is to cause a
      violation)
    - PRL and IRQ are a function of the flag only, not flag and control
    - IAK is used unqualified by IRQ

   The IAK backplane signal is asserted when any interrupt is acknowledged by
   the CPU.  Normally, an interface qualifies IAK with its own IRQ to ensure
   that it responds only to an acknowledgement of its own request.  The MP card
   does this to reset its flag buffer and flag flip-flops, and to reset the
   parity error indication.  However, it also responds to an unqualified IAK
   (i.e., for any interface) as follows:

    - clears the MPV flip-flop
    - clears the indirect counter
    - clears the control flip-flop
    - sets the INTPT flip-flop

   The INTPT flip-flop indicates an occurrence of an interrupt.  If the trap
   cell of the interrupting device contains an I/O instruction that is not a
   HLT, action equivalent to STC 05 is taken, i.e.:

    - sets the control flip-flop
    - set the EVR flip-flop
    - clears the MEV flip-flop
    - clears the PARERR flip-flop

   In other words, an interrupt for any device will disable MP unless the trap
   cell contains an I/O instruction other than a HLT.


   Implementation notes:

    1. Because the card uses IAK unqualified, this routine is called whenever
       any interrupt occurs.  If the MP card itself is not interrupting, the
       select code passed will not be SC 05.  In either case, the trap cell
       instruction is passed in the data portion of the "stat_data" parameter.

    2. The MEV flip-flop records memory expansion (a.k.a. dynamic mapping)
       violations.  It is set when an DM violation is encountered and can be
       tested via SFC/SFS.

    3. MP cannot be turned off in hardware, except by causing a violation.
       Microcode typically does this by executing an IOG micro-order with select
       code /= 1, followed by an IAK to clear the interrupt and a FTCH to clear
       the INTPT flip-flop.  Under simulation, mp_control may be set to CLEAR to
       produce the same effect.

    4. Parity error logic is not implemented.
*/

static uint32 protio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
uint16   data;
IOSIGNAL signal;
IOCYCLE  working_set = IOADDSIR (signal_set);           /* add ioSIR if needed */

while (working_set) {
    signal = IONEXT (working_set);                      /* isolate next signal */

    switch (signal) {                                   /* dispatch I/O signal */

        case ioCLF:                                     /* clear flag flip-flop */
            break;                                      /* turns off PE interrupt */

        case ioSTF:                                     /* set flag flip-flop */
            break;                                      /* turns on PE interrupt */

        case ioENF:                                     /* enable flag */
            mp_flag = mp_flagbuf = SET;                 /* set flag buffer and flag flip-flops */
            mp_evrff = CLEAR;                           /* inhibit violation register updates */
            break;

        case ioSFC:                                     /* skip if flag is clear */
            setSKF (!mp_mevff);                         /* skip if MP interrupt */
            break;

        case ioSFS:                                     /* skip if flag is set */
            setSKF (mp_mevff);                          /* skip if DMS interrupt */
            break;

        case ioIOI:                                     /* I/O input */
            stat_data = IORETURN (SCPE_OK, mp_viol);    /* read MP violation register */
            break;

        case ioIOO:                                     /* I/O output */
            mp_fence = IODATA (stat_data) & VAMASK;     /* write to MP fence register */

            if (cpu_unit.flags & UNIT_2100)             /* 2100 IOP uses MP fence */
                iop_sp = mp_fence;                      /*   as a stack pointer */

            mp_mem_changed = TRUE;                      /* set the MP/MEM registers changed flag */
            break;

        case ioPOPIO:                                   /* power-on preset to I/O */
            mp_control = CLEAR;                         /* clear control flip-flop */
            mp_flag = mp_flagbuf = CLEAR;               /* clear flag and flag buffer flip-flops */
            mp_mevff = CLEAR;                           /* clear memory expansion violation flip-flop */
            mp_evrff = SET;                             /* set enable violation register flip-flop */
            break;

        case ioSTC:                                     /* set control flip-flop */
            mp_control = SET;                           /* turn on MP */
            mp_mevff = CLEAR;                           /* clear memory expansion violation flip-flop */
            mp_evrff = SET;                             /* set enable violation register flip-flop */
            break;

        case ioSIR:                                     /* set interrupt request */
            setPRL (PRO, !mp_flag);                     /* set PRL signal */
            setIRQ (PRO, mp_flag);                      /* set IRQ signal */
            break;

        case ioIAK:                                     /* interrupt acknowledge */
            if (dibptr->select_code == PRO)             /* MP interrupt acknowledgement? */
                mp_flag = mp_flagbuf = CLEAR;           /* clear flag and flag buffer */

            data = IODATA (stat_data);                  /* get trap cell instruction */

            if (((data & I_NMRMASK) != I_IO) ||         /* trap cell instruction not I/O */
                (I_GETIOOP (data) == soHLT))            /*   or is halt? */
                mp_control = CLEAR;                     /* turn protection off */
            else {                                      /* non-HLT I/O instruction leaves MP on */
                mp_mevff = CLEAR;                       /*   but clears MEV flip-flop */
                mp_evrff = SET;                         /*   and reenables violation register flip-flop */
                }
            break;

        default:                                        /* all other signals */
            break;                                      /*   are ignored */
        }

    working_set = working_set & ~signal;                /* remove current signal from set */
    }

return stat_data;
}


/* Memory protect reset */

static t_stat mp_reset (DEVICE *dptr)
{
IOPRESET (&mp_dib);                                     /* PRESET device (does not use PON) */

mp_fence = 0;                                           /* clear fence register */
mp_viol = 0;                                            /* clear violation register */

mp_mem_changed = TRUE;                                  /* set the MP/MEM registers changed flag */

return SCPE_OK;
}



/* I/O system local utility routine declarations */


/* Initialize the I/O system.

   This routine is called in the instruction prelude to set up the I/O data
   structures prior to beginning execution.  It sets up two tables indexed by
   select code: one of DIB pointers, and the other of DEVICE pointers.  This
   allows fast access to the device interface routine by the I/O instruction
   executors and to the device trace flags, respectively.

   It also sets the interface priority, interrupt request, and service request
   bit vectors from the interface flip-flop values by calling the device
   interface routines.

   Finally, it sets the interrupt deferral table entries for the SFC and SFS
   signals.  These depend on the current CPU model, which may have been changed
   while the simulation was stopped.
*/

static void io_initialize (void)
{
DEVICE *dptr;
DIB    *dibptr;
uint32 i;

dev_prl [0] = dev_prl [1] = ~0u;                        /* set all priority lows */
dev_irq [0] = dev_irq [1] = 0;                          /* clear all interrupt requests */
dev_srq [0] = dev_srq [1] = 0;                          /* clear all service requests */

memset (&dibs [2], 0, sizeof dibs - 2 * sizeof dibs [0]);   /* clear the DIB pointer table */
memset (&devs [2], 0, sizeof devs - 2 * sizeof devs [0]);   /*   and the device table */

for (i = 0; sim_devices [i] != NULL; i++) {             /* loop through all of the devices */
    dptr = sim_devices [i];                             /* get a pointer to the device */
    dibptr = (DIB *) dptr->ctxt;                        /*   and to that device's DIB */

    if (dibptr && !(dptr->flags & DEV_DIS)) {           /* if the DIB exists and the device is enabled */
        devs [dibptr->select_code] = dptr;              /*   then set the device pointer into the device table */
        dibs [dibptr->select_code] = dibptr;            /*     and set the DIB pointer into the dispatch table */

        dibptr->io_handler (dibptr, ioSIR, 0);          /* set the interrupt request state */
        }
    }

dibs [PWR] = &pwrf_dib;                                 /* for now, powerfail is always present */
devs [PWR] = &cpu_dev;                                  /*   and is controlled by the CPU */

if (dibs [DMA1]) {                                      /* if the first DMA channel is enabled */
    dibs [DMALT1] = &dmas1_dib;                         /*   then set up  */
    devs [DMALT1] = &dma1_dev;                          /*     the secondary device handler */
    }

if (dibs [DMA2]) {                                      /* if the second DMA channel is enabled */
    dibs [DMALT2] = &dmas2_dib;                         /*   then set up  */
    devs [DMALT2] = &dma2_dev;                          /*     the secondary device handler */
    }

defer_tab [soSFC] = is_1000;                            /* SFC and SFS defer */
defer_tab [soSFS] = is_1000;                            /*   for 1000-Series CPUs only */

return;
}


/* Device I/O signal dispatcher.

   This routine calls the I/O signal handler of the device corresponding to the
   supplied "select_code" value, passing the "signal_set" and inbound "data"
   values.  The combined status and outbound data value from the handler is
   returned to the caller.

   The 21xx/1000 I/O structure requires that no empty slots exist between
   interface cards.  This is due to the hardware priority chaining (PRH/PRL)
   that is passed from card-to-card.  If it is necessary to leave unused I/O
   slots, HP 12777A Priority Jumper Cards must be installed in them to maintain
   priority continuity.

   Under simulation, every unassigned I/O slot behaves as though a 12777A were
   resident.  In this configuration, I/O instructions addressed to one of these
   slots read the floating bus for LIA/B and MIA/B instructions or do nothing
   for all other instructions.


   Implementation notes:

     1. For select codes < 10 octal, an IOI signal reads the floating S-bus
        (high on the 1000, low on the 21xx).  For select codes >= 10 octal, an
        IOI reads the floating I/O bus (low on all machines).

     2. If the UNSC simulation stop is set, a stop will occur when an unassigned
        device is accessed.  An exception is a CLC 0 instruction, which asserts
        the CRS signal to all I/O devices from select code 6 up.  This is a
        legitimate access and does not cause a stop.
*/

static uint32 io_dispatch (uint32 select_code, IOCYCLE signal_set, HP_WORD data)
{
uint32 stat_data;

if (devs [select_code] != NULL) {                           /* if the I/O slot is occupied */
    tpprintf (devs [select_code], TRACE_IOBUS, "Received data %06o with signals %s\n",
              data, fmt_bitset (signal_set, inbound_format));

    stat_data =                                             /*   then call the device interface */
      dibs [select_code]->io_handler (dibs [select_code],   /*     with the indicated signals and write value */
                                      signal_set,
                                      IORETURN (SCPE_OK, data));

    tpprintf (devs [select_code], TRACE_IOBUS, "Returned data %06o\n", IODATA (stat_data));
    }

else if (signal_set == ioCRS)                               /* otherwise the slot is empty, but if this is a CRS */
    return SCPE_OK;                                         /*   then it's legal to send it to unassigned slots */

else if (signal_set & ioIOI)                                /* otherwise if it is an input request */
    if (select_code < VARDEV && is_1000)                    /*   then if it is an internal device of a 1000 CPU */
        stat_data = IORETURN (STOP (cpu_ss_unsc), DMASK);   /*     then the empty slot reads as all ones */
    else                                                    /*   otherwise */
        stat_data = IORETURN (STOP (cpu_ss_unsc), 0);       /*     the empty slot reads as all zeros */

else                                                        /* otherwise */
    stat_data = IORETURN (STOP (cpu_ss_unsc), 0);           /*   the signal is ignored */

return stat_data;
}