/* hp2100_cpu1.c: HP 2100 EAU and UIG simulator

   Copyright (c) 2005, Robert M. Supnik

   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
   ROBERT M SUPNIK 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 name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   CPU1         Extended arithmetic and optional microcode instructions

   22-Feb-05    JDB     Fixed missing MPCK on JRS target
                        Removed EXECUTE instruction (is NOP in actual microcode)
   18-Feb-05    JDB     Add 2100/21MX Fast FORTRAN Processor instructions
   21-Jan-05    JDB     Reorganized CPU option and operand processing flags
                        Split code along microcode modules
   15-Jan-05    RMS     Cloned from hp2100_cpu.c

   Primary references:
   - 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)

   Additional references are listed with the associated firmware
   implementations, as are the HP option model numbers pertaining to the
   applicable CPUs.

   This source file contains the Extended Arithmetic Unit and various optional
   User Instruction Group (a.k.a. "Macro") instruction sets for the 2100 and
   21MX CPUs.  Unit flags indicate which options are present in the current
   system.

   The microcode address space of the 2100 encompassed four modules of 256 words
   each.  The 21MX M-series expanded that to sixteen modules, and the 21MX
   E-series expanded that still further to sixty-four modules.  Each CPU had its
   own microinstruction set, although the micromachines of the various 21MX
   models were similar internally.

   Regarding option instruction sets, there was some commonality across CPU
   types.  EAU instructions were identical across all models, and the floating
   point set was the same on the 2100 and 21MX.  Other options implemented
   proper instruction supersets (e.g., the Fast FORTRAN Processor from 2100 to
   21MX-M to 21MX-E to 21MX-F) or functional equivalence with differing code
   points (the 2000 I/O Processor from 2100 to 21MX).

   The 2100 decoded the EAU and UIG sets separately in hardware and supported
   only the UIG 0 code points.  Bits 7-4 of a UIG instruction decoded one of
   sixteen entry points in the lowest-numbered module after module 0.  Those
   entry points could be used directly (as for the floating-point instructions),
   or additional decoding based on bits 3-0 could be implemented.

   The 21MX generalized the instruction decoding to a series of microcoded
   jumps, based on the bits in the instruction.  Bits 15-8 indicated the group
   of the current instruction: EAU (200, 201, 202, 210, and 211), UIG 0 (212),
   or UIG 1 (203 and 213).  UIG 0, UIG 1, and some EAU instructions were decoded
   further by selecting one of sixteen modules within the group via bits 7-4.
   Finally, each UIG module decoded up to sixteen instruction entry points via
   bits 3-0.  Jump tables for all firmware options were contained in the base
   set, so modules needed only to be concerned with decoding their individual
   entry points within the module.

   While the 2100 and 21MX hardware decoded these instruction sets differently,
   the decoding mechanism of the simulation follows that of the 21MX E-series.
   Where needed, CPU type- or model-specific behavior is simulated.

   The design of the 21MX microinstruction set was such that executing an
   instruction for which no microcode was present (e.g., executing a FFP
   instruction when the FFP firmware was not installed) resulted in a NOP.
   Under simulation, such execution causes an undefined instruction stop.
*/

#include <setjmp.h>
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_fp1.h"

/* Operand processing encoding */

#define OP_NUL          0                               /* no operand */
#define OP_CON          1                               /* operand is a constant */
#define OP_VAR          2                               /* operand is a variable */
#define OP_ADR          3                               /* operand is an address */
#define OP_ADK          4                               /* op is addr of 1-word const */
#define OP_ADF          5                               /* op is addr of 2-word const */
#define OP_ADX          6                               /* op is addr of 3-word const */
#define OP_ADT          7                               /* op is addr of 4-word const */

#define OP_N_FLAGS      3                               /* number of flag bits */
#define OP_M_FLAGS      ((1 << OP_N_FLAGS) - 1)         /* mask for flag bits */

#define OP_N_F          4                               /* number of op fields */

#define OP_V_F1         (0 * OP_N_FLAGS)                /* 1st operand field */
#define OP_V_F2         (1 * OP_N_FLAGS)                /* 2nd operand field */
#define OP_V_F3         (2 * OP_N_FLAGS)                /* 3rd operand field */
#define OP_V_F4         (3 * OP_N_FLAGS)                /* 4th operand field */

/* Operand patterns */

#define OP_N            (OP_NUL)
#define OP_C            (OP_CON << OP_V_F1)
#define OP_V            (OP_VAR << OP_V_F1)
#define OP_A            (OP_ADR << OP_V_F1)
#define OP_K            (OP_ADK << OP_V_F1)
#define OP_F            (OP_ADF << OP_V_F1)
#define OP_CV           ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2))
#define OP_AC           ((OP_ADR << OP_V_F1) | (OP_CON << OP_V_F2))
#define OP_AA           ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2))
#define OP_AK           ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2))
#define OP_AX           ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2))
#define OP_KV           ((OP_ADK << OP_V_F1) | (OP_VAR << OP_V_F2))
#define OP_KA           ((OP_ADK << OP_V_F1) | (OP_ADR << OP_V_F2))
#define OP_KK           ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2))
#define OP_CVA          ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2) | \
                        (OP_ADR << OP_V_F3))
#define OP_AAF          ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
                        (OP_ADF << OP_V_F3))
#define OP_AAX          ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
                        (OP_ADX << OP_V_F3))
#define OP_AXX          ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2) | \
                        (OP_ADX << OP_V_F3))
#define OP_AAXX         ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \
                        (OP_ADX << OP_V_F3) | (OP_ADX << OP_V_F4))
#define OP_KKKK         ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2) | \
                        (OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4))

typedef uint32 OP_PAT;                                  /* operand pattern */
typedef uint32 OPS[OP_N_F * 2];                         /* operand array */

extern uint16 ABREG[2];
extern uint32 PC;
extern uint32 err_PC;
extern uint32 XR;
extern uint32 YR;
extern uint32 E;
extern uint32 O;
extern uint32 dms_enb;
extern uint32 dms_ump;
extern uint32 dms_sr;
extern uint32 dms_vr;
extern uint32 mp_fence;
extern uint32 iop_sp;
extern uint32 ion_defer;
extern uint16 pcq[PCQ_SIZE];
extern uint32 pcq_p;
extern uint32 stop_inst;
extern UNIT cpu_unit;

t_stat cpu_eau (uint32 IR, uint32 intrq);               /* EAU group handler */
t_stat cpu_uig_0 (uint32 IR, uint32 intrq);             /* UIG group 0 handler */
t_stat cpu_uig_1 (uint32 IR, uint32 intrq);             /* UIG group 1 handler */

static t_stat cpu_fp (uint32 IR, uint32 intrq);         /* Floating-point */
static t_stat cpu_ffp (uint32 IR, uint32 intrq);        /* Fast FORTRAN Processor */
static t_stat cpu_iop (uint32 IR, uint32 intrq);        /* 2000 I/O Processor */
static t_stat cpu_dms (uint32 IR, uint32 intrq);        /* Dynamic mapping system */
static t_stat cpu_eig (uint32 IR, uint32 intrq);        /* Extended instruction group */
static t_stat get_ops (OP_PAT pattern, OPS op, uint32 irq);     /* operand processor */

extern uint32 f_as (uint32 op, t_bool sub);             /* FAD/FSB */
extern uint32 f_mul (uint32 op);                        /* FMP */
extern uint32 f_div (uint32 op);                        /* FDV */
extern uint32 f_fix (void);                             /* FIX */
extern uint32 f_flt (void);                             /* FLT */
extern uint32 f_pack (int32 expon);                     /* .PACK helper */
extern void f_unpack (void);                            /* .FLUN helper */
extern void f_pwr2 (int32 n);                           /* .PWR2 helper */

/* EAU

   The Extended Arithmetic Unit (EAU) adds ten instructions with double-word
   operands, including multiply, divide, shifts, and rotates.  Option
   implementation by CPU was as follows:

      2116    2100   21MX-M  21MX-E  21MX-F
     ------  ------  ------  ------  ------
     12579A   std     std     std     std

   The instruction codes are mapped to routines as follows:

     Instr.    Bits
      Code   15-8 7-4   2116    2100   21MX-M  21MX-E  21MX-F  Note
     ------  ---- ---  ------  ------  ------  ------  ------  ---------------------
     100000   200  00                           DIAG    DIAG   Unsupported
     100020   200  01   ASL     ASL     ASL     ASL     ASL    Bits 3-0 encode shift
     100040   200  02   LSL     LSL     LSL     LSL     LSL    Bits 3-0 encode shift
     100060   200  03                          TIMER   TIMER   Unsupported
     100100   200  04   RRL     RRL     RRL     RRL     RRL    Bits 3-0 encode shift
     100200   200  10   MPY     MPY     MPY     MPY     MPY
     100400   201  xx   DIV     DIV     DIV     DIV     DIV
     101020   202  01   ASR     ASR     ASR     ASR     ASR    Bits 3-0 encode shift
     101040   202  02   LSR     LSR     LSR     LSR     LSR    Bits 3-0 encode shift
     101100   202  04   RRR     RRR     RRR     RRR     RRR    Bits 3-0 encode shift
     104200   210  xx   DLD     DLD     DLD     DLD     DLD
     104400   211  xx   DST     DST     DST     DST     DST

   The remaining codes for bits 7-4 are undefined and will cause a simulator
   stop if enabled.  On a real 21MX-M, all undefined instructions in the 200
   group decode as MPY, and all in the 202 group decode as NOP.  On a real
   21MX-E, instruction patterns 200/05 through 200/07 and 202/03 decode as NOP;
   all others cause erroneous execution.

   EAU instruction decoding on the 21MX M-series is convoluted.  The JEAU
   microorder maps IR bits 11, 9-7 and 5-4 to bits 2-0 of the microcode jump
   address.  The map is detailed on page IC-84 of the ERD.
        
   The 21MX E/F-series add two undocumented instructions to the 200 group:
   TIMER and DIAG.  These are described in the ERD on page IA 5-5, paragraph
   5-7.  The M-series executes these as MPY and RRL, respectively.  A third
   instruction, EXECUTE (100120), is also described but was never implemented,
   and the E/F-series microcode execute a NOP for this instruction code.

   Under simulation, TIMER, DIAG, and EXECUTE cause undefined instruction stops
   if the CPU is set to 2100 or 2116.  DIAG and EXECUTE also cause stops on the
   21MX-M.  TIMER does not, because it is used by several HP programs to
   differentiate between M- and E/F-series machines.
*/

t_stat cpu_eau (uint32 IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 rs, qs, sc, v1, v2, t;
int32 sop1, sop2;

if ((cpu_unit.flags & UNIT_EAU) == 0) return stop_inst; /* implemented? */

switch ((IR >> 8) & 0377) {                             /* decode IR<15:8> */

    case 0200:                                          /* EAU group 0 */
        switch ((IR >> 4) & 017) {                      /* decode IR<7:4> */

        case 000:                                       /* DIAG 100000 */
            if (UNIT_CPU_MODEL != UNIT_21MX_E)          /* must be 21MX-E */
                return stop_inst;                       /* trap if not */
            break;                                      /* DIAG is NOP unless halted */

        case 001:                                       /* ASL 100020-100037 */
            sc = (IR & 017)? (IR & 017): 16;            /* get sc */
            O = 0;                                      /* clear ovflo */
            while (sc-- != 0) {                         /* bit by bit */
                t = BR << 1;                            /* shift B */
                BR = (BR & SIGN) | (t & 077777) | (AR >> 15);
                AR = (AR << 1) & DMASK;
                if ((BR ^ t) & SIGN) O = 1;
                }
            break;

        case 002:                                       /* LSL 100040-100057 */
            sc = (IR & 017)? (IR & 017): 16;            /* get sc */
            BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK;
            AR = (AR << sc) & DMASK;                    /* BR'AR lsh left */
            break;

        case 003:                                       /* TIMER 100060 */
            if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)        /* must be 21MX */
                return stop_inst;                       /* trap if not */
            if (UNIT_CPU_MODEL == UNIT_21MX_M)          /* 21MX M-series? */
                goto MPY;                               /* decode as MPY */
            BR = (BR + 1) & DMASK;                      /* increment B */
            if (BR) PC = err_PC;                        /* if !=0, repeat */
            break;

        case 004:                                       /* RRL 100100-100117 */
            sc = (IR & 017)? (IR & 017): 16;            /* get sc */
            t = BR;                                     /* BR'AR rot left */
            BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK;
            AR = ((AR << sc) | (t >> (16 - sc))) & DMASK;
            break;

        case 010:                                       /* MPY 100200 */
        MPY:
            if (reason = get_ops (OP_K, op, intrq))     /* get operand */
                break;
            sop1 = SEXT (AR);                           /* sext AR */
            sop2 = SEXT (op[0]);                        /* sext mem */
            sop1 = sop1 * sop2;                         /* signed mpy */
            BR = (sop1 >> 16) & DMASK;                  /* to BR'AR */
            AR = sop1 & DMASK;
            O = 0;                                      /* no overflow */
            break;

        default:                                        /* others undefined */
            return stop_inst;
            }
        
        break;

    case 0201:                                          /* DIV 100400 */
        if (reason = get_ops (OP_K, op, intrq))         /* get operand */
            break;
        if (rs = qs = BR & SIGN) {                      /* save divd sign, neg? */
            AR = (~AR + 1) & DMASK;                     /* make B'A pos */
            BR = (~BR + (AR == 0)) & DMASK;             /* make divd pos */
            }
        v2 = op[0];                                     /* divr = mem */
        if (v2 & SIGN) {                                /* neg? */
            v2 = (~v2 + 1) & DMASK;                     /* make divr pos */
            qs = qs ^ SIGN;                             /* sign of quotient */
            }
        if (BR >= v2) O = 1;                            /* divide work? */
        else {                                          /* maybe... */
            O = 0;                                      /* assume ok */
            v1 = (BR << 16) | AR;                       /* 32b divd */
            AR = (v1 / v2) & DMASK;                     /* quotient */
            BR = (v1 % v2) & DMASK;                     /* remainder */
            if (AR) {                                   /* quotient > 0? */
                if (qs) AR = (~AR + 1) & DMASK;         /* apply quo sign */
                if ((AR ^ qs) & SIGN) O = 1;            /* still wrong? ovflo */
                }
            if (rs) BR = (~BR + 1) & DMASK;             /* apply rem sign */
            }
        break;

    case 0202:                                          /* EAU group 2 */
        switch ((IR >> 4) & 017) {                      /* decode IR<7:4> */

        case 001:                                       /* ASR 101020-101037 */
            sc = (IR & 017)? (IR & 017): 16;            /* get sc */
            AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK;
            BR = (SEXT (BR) >> sc) & DMASK;             /* BR'AR ash right */
            O = 0;
            break;
        
        case 002:                                       /* LSR 101040-101057 */
            sc = (IR & 017)? (IR & 017): 16;            /* get sc */
            AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK;
            BR = BR >> sc;                              /* BR'AR log right */
            break;
        
        case 004:                                       /* RRR 101100-101117 */
            sc = (IR & 017)? (IR & 017): 16;            /* get sc */
            t = AR;                                     /* BR'AR rot right */
            AR = ((AR >> sc) | (BR << (16 - sc))) & DMASK;
            BR = ((BR >> sc) | (t << (16 - sc))) & DMASK;
            break;
        
        default:                                        /* others undefined */
            return stop_inst;
            }
        
        break;

    case 0210:                                          /* DLD 104200 */
        if (reason = get_ops (OP_F, op, intrq))         /* get operand */
            break;
        AR = (op[0] >> 16) & DMASK;                     /* load AR */
        BR = op[0] & DMASK;                             /* load BR */
        break;

    case 0211:                                          /* DST 104400 */
        if (reason = get_ops (OP_A, op, intrq))         /* get operand */
            break;
        WriteW (op[0], AR);                             /* store AR */
        op[0] = (op[0] + 1) & VAMASK;
        WriteW (op[0], BR);                             /* store BR */
        break;

    default:                                            /* should never get here */
        return SCPE_IERR;
        }

return reason;
}

/* UIG 0

   The first User Instruction Group (UIG) encodes firmware options for the 2100
   and 21MX.  Instruction codes 105000-105377 are assigned to microcode options
   as follows:

     Instructions   Option Name                 2100   21MX-M  21MX-E  21MX-F
     -------------  -------------------------  ------  ------  ------  ------
     105000-105362  2000 I/O Processor          opt      -       -       -
     105000-105120  Floating Point              opt     std     std     std
     105200-105237  Fast FORTRAN Processor      opt     opt     opt     std
     105240-105257  RTE-IVA/B EMA                -       -      opt     opt
     105240-105257  RTE-6/VMA                    -       -      opt     opt
     105300-105317  Distributed System           -       -      opt     opt
     105340-105357  RTE-6/VM Operating System    -       -      opt     opt

   Because the 2100 IOP microcode uses the same instruction range as the 2100 FP
   and FFP options, it cannot coexist with them.  To simplify simulation, the
   2100 IOP instructions are remapped to the equivalent 21MX instructions and
   dispatched to the UIG 1 module.

   Note that if the 2100 IOP is installed, the only valid UIG instructions are
   IOP instructions, as the IOP used the full 2100 microcode addressing space.
*/

t_stat cpu_uig_0 (uint32 IR, uint32 intrq)
{
if ((cpu_unit.flags & UNIT_IOP) && (UNIT_CPU_TYPE == UNIT_TYPE_2100)) {
    if ((IR >= 0105020) && (IR <= 0105057))             /* remap LAI */
        IR = 0105400 | (IR - 0105020);
    else if ((IR >= 0105060) && (IR <= 0105117))        /* remap SAI */
        IR = 0101400 | (IR - 0105060);
    else {
        switch (IR) {                                   /* remap others */
        case 0105000:  IR = 0105470;  break;            /* ILIST */
        case 0105120:  IR = 0105765;  break;            /* MBYTE (maps to MBT) */
        case 0105150:  IR = 0105460;  break;            /* CRC   */
        case 0105160:  IR = 0105467;  break;            /* TRSLT */
        case 0105200:  IR = 0105777;  break;            /* MWORD (maps to MVW) */
        case 0105220:  IR = 0105462;  break;            /* READF */
        case 0105221:  IR = 0105473;  break;            /* PRFIO */
        case 0105222:  IR = 0105471;  break;            /* PRFEI */
        case 0105223:  IR = 0105472;  break;            /* PRFEX */
        case 0105240:  IR = 0105464;  break;            /* ENQ   */
        case 0105257:  IR = 0105465;  break;            /* PENQ  */
        case 0105260:  IR = 0105466;  break;            /* DEQ   */
        case 0105300:  IR = 0105764;  break;            /* SBYTE (maps to SBT) */
        case 0105320:  IR = 0105763;  break;            /* LBYTE (maps to LBT) */
        case 0105340:  IR = 0105461;  break;            /* REST  */
        case 0105362:  IR = 0105474;  break;            /* SAVE  */

        default:                                        /* all others invalid */
            return stop_inst;
            }
        }
    if (IR >= 0105700) return cpu_eig (IR, intrq);      /* dispatch to 21MX EIG */
    else return cpu_iop (IR, intrq);                    /* or to 21MX IOP */
    }

switch ((IR >> 4) & 017) {                              /* decode IR<7:4> */

    case 000:                                           /* 105000-105017 */
    case 001:                                           /* 105020-105037 */
    case 002:                                           /* 105040-105057 */
    case 003:                                           /* 105060-105077 */
    case 004:                                           /* 105100-105117 */
    case 005:                                           /* 105120-105137 */
        return cpu_fp (IR, intrq);                      /* Floating Point */

    case 010:                                           /* 105200-105217 */
    case 011:                                           /* 105220-105237 */
        return cpu_ffp (IR, intrq);                     /* Fast FORTRAN Processor */
        }

return stop_inst;                                       /* others undefined */
}

/* UIG 1

   The second User Instruction Group (UIG) encodes firmware options for the
   21MX.  Instruction codes 101400-101777 and 105400-105777 are assigned to
   microcode options as follows ("x" is "1" or "5" below):

     Instructions   Option Name                 21MX-M  21MX-E  21MX-F
     -------------  --------------------------  ------  ------  ------
     10x400-10x437  2000 IOP                     opt     opt      -
     10x460-10x477  2000 IOP                     opt     opt      -
     10x700-10x737  Dynamic Mapping System       opt     opt     std
     10x740-10x777  Extended Instruction Group   std     std     std

   Only 21MX systems execute these instructions.
*/

t_stat cpu_uig_1 (uint32 IR, uint32 intrq)
{
if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)                    /* 21MX execution? */
    return stop_inst;                                   /* no, so trap */

switch ((IR >> 4) & 017) {                              /* decode IR<7:4> */

    case 000:                                           /* 105400-105417 */
    case 001:                                           /* 105420-105437 */
    case 003:                                           /* 105460-105477 */
        return cpu_iop (IR, intrq);                     /* 2000 I/O Processor */

    case 014:                                           /* 105700-105717 */
    case 015:                                           /* 105720-105737 */
        return cpu_dms (IR, intrq);                     /* Dynamic Mapping System */

    case 016:                                           /* 105740-105737 */
    case 017:                                           /* 105760-105777 */
        return cpu_eig (IR, intrq);                     /* Extended Instruction Group */
        }

return stop_inst;                                       /* others undefined */
}

/* Floating Point

   The 2100 and 21MX CPUs share the single-precision (two word) floating point
   instruction codes.  Option implementation by CPU was as follows:

      2116    2100   21MX-M  21MX-E  21MX-F
     ------  ------  ------  ------  ------
      N/A    12901A   std     std     std

   The instruction codes are mapped to routines as follows:

     Instr.  2100/21MX-M/E/F
     ------  ---------------
     105000       FAD
     105020       FSB
     105040       FMP
     105060       FDV
     105100       FIX
     105120       FLT

   Bits 3-0 are not decoded by these instructions, so FAD (e.g.) would be
   executed by any instruction in the range 105000-105017.
*/

static const OP_PAT op_fp[6] = {
  OP_F,    OP_F,    OP_F,    OP_F,                      /*  FAD    FSB    FMP    FDV  */
  OP_N,    OP_N                                         /*  FIX    FLT    ---    ---  */
  };

static t_stat cpu_fp (uint32 IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry;

if ((cpu_unit.flags & UNIT_FP) == 0)                    /* FP option installed? */
    return stop_inst;

entry = (IR >> 4) & 017;                                /* mask to entry point */

if (op_fp[entry] != OP_N) {
    if (reason = get_ops (op_fp[entry], op, intrq))     /* get instruction operands */
        return reason;
    }

switch (entry) {                                        /* decode IR<7:4> */

    case 000:                                           /* FMP 105000 */
        O = f_as (op[0], 0);                            /* add, upd ovflo */
        break;

    case 001:                                           /* FMP 105020 */
        O = f_as (op[0], 1);                            /* sub, upd ovflo */
        break;

    case 002:                                           /* FMP 105040 */
        O = f_mul (op[0]);                              /* mul, upd ovflo */
        break;

    case 003:                                           /* FDV 105060 */
        O = f_div (op[0]);                              /* div, upd ovflo */
        break;

    case 004:                                           /* FIX 105100 */
        O = f_fix ();                                   /* fix, upd ovflo */
        break;

    case 005:                                           /* FLT 105120 */
        O = f_flt ();                                   /* float, upd ovflo */
        break;

    default:                                            /* should be impossible */
        return SCPE_IERR;
        }

return reason;
}

/* Fast FORTRAN Processor

   The Fast FORTRAN Processor (FFP) is a set of FORTRAN language accelerators
   and extended-precision (three-word) floating point routines.  Although the
   FFP is an option for the 2100 and later CPUs, each implements the FFP in a
   slightly different form.

   Option implementation by CPU was as follows:

      2116    2100   21MX-M  21MX-E  21MX-F
     ------  ------  ------  ------  ------
      N/A    12907A  12977B  13306B   std

   The instruction codes are mapped to routines as follows:

     Instr.   2100  21MX-M 21MX-E 21MX-F    Instr.   2100  21MX-M 21MX-E 21MX-F
     ------  ------ ------ ------ ------    ------  ------ ------ ------ ------
     105200    --     --     --   [test]    105220  .XFER  .XFER  .XFER  .XFER
     105201   DBLE   DBLE   DBLE   DBLE     105221  .GOTO  .GOTO  .GOTO  .GOTO
     105202   SNGL   SNGL   SNGL   SNGL     105222  ..MAP  ..MAP  ..MAP  ..MAP
     105203  .XMPY  .XMPY  .XMPY    --      105223  .ENTR  .ENTR  .ENTR  .ENTR
     105204  .XDIV  .XDIV  .XDIV    --      105224  .ENTP  .ENTP  .ENTP  .ENTP
     105205  .DFER  .DFER  .DFER  .DFER     105225    --   .PWR2  .PWR2  .PWR2
     105206    --   .XPAK  .XPAK  .XPAK     105226    --   .FLUN  .FLUN  .FLUN
     105207    --    XADD   XADD  .BLE      105227  $SETP  $SETP  $SETP  $SETP

     105210    --    XSUB   XSUB    --      105230    --   .PACK  .PACK  .PACK
     105211    --    XMPY   XMPY    --      105231    --     --   .CFER  .CFER
     105212    --    XDIV   XDIV    --      105232    --     --     --   ..FCM
     105213  .XADD  .XADD  .XADD    --      105233    --     --     --   ..TCM
     105214  .XSUB  .XSUB  .XSUB  .NGL      105234    --     --     --     --
     105215    --   .XCOM  .XCOM  .XCOM     105235    --     --     --     --
     105216    --   ..DCM  ..DCM  ..DCM     105236    --     --     --     --
     105217    --   DDINT  DDINT  DDINT     105237    --     --     --     --

   Notes:

     1. The "$SETP" instruction is sometimes listed as ".SETP" in the
        documentation.

     2. Extended-precision arithmetic routines (e.g., .XMPY) exist on the
        21MX-F, but they are assigned instruction codes in the single-precision
        floating-point module.

     3. The software implementation of ..MAP supports 1-, 2-, or 3-dimensional
        arrays, designated by setting A = -1, 0, and +1, respectively.  The
        firmware implementation supports only 2- and 3-dimensional access.
        
     4. The documentation for ..MAP for the 2100 FFP shows A = 0 or -1 for two
        or three dimensions, respectively, but the 21MX FFP shows A = 0 or +1.
        The firmware actually only checks the LSB of A.

     5. The .DFER and .XFER implementations for the 2100 FFP return X+4 and Y+4
        in the A and B registers, whereas the 21MX FFP returns X+3 and Y+3.

     6. The .XFER implementation for the 2100 FFP returns to P+2, whereas the
        21MX implementation returns to P+1.

   Additional references:
    - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981)
    - Implementing the HP 2100 Fast FORTRAN Processor (12907-90010, Nov-1974)
*/

static const OP_PAT op_ffp[32] = {
  OP_N,    OP_AAF,  OP_AX,   OP_AXX,                    /*  ---   DBLE   SNGL   .XMPY */
  OP_AXX,  OP_AA,   OP_A,    OP_AAXX,                   /* .XDIV  .DFER  .XPAK  XADD  */
  OP_AAXX, OP_AAXX, OP_AAXX, OP_AXX,                    /* XSUB   XMPY   XDIV   .XADD */
  OP_AXX,  OP_A,    OP_A,    OP_AAX,                    /* .XSUB  .XCOM  ..DCM  DDINT */
  OP_N,    OP_AK,   OP_KKKK, OP_A,                      /* .XFER  .GOTO  ..MAP  .ENTR */
  OP_A,    OP_K,    OP_N,    OP_K,                      /* .ENTP  .PWR2  .FLUN  $SETP */
  OP_C,    OP_AA,   OP_N,    OP_N,                      /* .PACK  .CFER   ---    ---  */
  OP_N,    OP_N,    OP_N,    OP_N                       /*  ---    ---    ---    ---  */
  };

static t_stat cpu_ffp (uint32 IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op, op2;
uint32 entry;
uint32 j, sa, sb, sc, da, dc, ra, MA;
int32 i;
XPN xop;

if ((cpu_unit.flags & UNIT_FFP) == 0)                   /* FFP option installed? */
    return stop_inst;

entry = IR & 037;                                       /* mask to entry point */

if (op_ffp[entry] != OP_N) {
    if (reason = get_ops (op_ffp[entry], op, intrq))    /* get instruction operands */
        return reason;
    }

switch (entry) {                                        /* decode IR<3:0> */

/* FFP module 1 */

    case 001:                                           /* DBLE 105201 (OP_AAF) */
        WriteW (op[1]++, (op[2] >> 16) & DMASK);        /* transfer high mantissa */
        WriteW (op[1]++, op[2] & 0177400);              /* convert low mantissa */
        WriteW (op[1], op[2] & 0377);                   /* convert exponent */
        break;

    case 002:                                           /* SNGL 105202 (OP_AX) */
        BR = op[2] >> 16;                               /* move LSB and expon to B */
        f_unpack ();                                    /* unpack B into A/B */
        sa = AR;                                        /* save exponent */
        AR = (op[1] >> 16) & DMASK;                     /* move MSB to A */
        BR = (op[1] & DMASK) | (BR != 0);               /* move mid to B with carry */
        O = f_pack (SEXT (sa));                         /* pack into A/B */
        break;

#if defined (HAVE_INT64)

    case 003:                                           /* .XMPY 105203 (OP_AXX) */
        i = 0;                                          /* params start at op[0] */
        goto XMPY;                                      /* process as XMPY */

    case 004:                                           /* .XDIV 105204 (OP_AXX) */
        i = 0;                                          /* params start at op[0] */
        goto XDIV;                                      /* process as XDIV */

#endif

    case 005:                                           /* .DFER 105205 (OP_AA) */
        BR = op[0];                                     /* get destination address */
        AR = op[1];                                     /* get source address */
        goto XFER;                                      /* do transfer */

#if defined (HAVE_INT64)

    case 006:                                           /* .XPAK 105206 (OP_A) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        xop = ReadX (op[0]);                            /* read unpacked */
        O = x_pak (&xop, xop, SEXT (AR));               /* pack mantissa, exponent */
        WriteX (op[0], xop);                            /* write back */
        break;

    case 007:                                           /* XADD 105207 (OP_AAXX) */
        i = 1;                                          /* params start at op[1] */
    XADD:                                               /* enter here from .XADD */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        O = x_add (&xop, AS_XPN (op [i + 1]), AS_XPN (op [i + 3]));  /* add ops */
        WriteX (op[i], xop);                            /* write sum */
        break;

    case 010:                                           /* XSUB 105210 (OP_AAXX) */
        i = 1;                                          /* params start at op[1] */
    XSUB:                                               /* enter here from .XSUB */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        O = x_sub (&xop, AS_XPN (op [i + 1]), AS_XPN (op [i + 3]));  /* subtract */
        WriteX (op[i], xop);                            /* write difference */
        break;

    case 011:                                           /* XMPY 105211 (OP_AAXX) */
        i = 1;                                          /* params start at op[1] */
    XMPY:                                               /* enter here from .XMPY */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        O = x_mpy (&xop, AS_XPN (op [i + 1]), AS_XPN (op [i + 3]));  /* multiply */
        WriteX (op[i], xop);                            /* write product */
        break;

    case 012:                                           /* XDIV 105212 (OP_AAXX) */
        i = 1;                                          /* params start at op[1] */
     XDIV:                                              /* enter here from .XDIV */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        O = x_div (&xop, AS_XPN (op [i + 1]), AS_XPN (op [i + 3]));  /* divide */
        WriteX (op[i], xop);                            /* write quotient */
        break;

    case 013:                                           /* .XADD 105213 (OP_AXX) */
        i = 0;                                          /* params start at op[0] */
        goto XADD;                                      /* process as XADD */

    case 014:                                           /* .XSUB 105214 (OP_AXX) */
        i = 0;                                          /* params start at op[0] */
        goto XSUB;                                      /* process as XSUB */

    case 015:                                           /* .XCOM 105215 (OP_A) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        xop = ReadX (op[0]);                            /* read operand */
        AR = x_com (&xop);                              /* neg and rtn exp adj */
        WriteX (op[0], xop);                            /* write result */
        break;

    case 016:                                           /* ..DCM 105216 (OP_A) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        xop = ReadX (op[0]);                            /* read operand */
        O = x_dcm (&xop);                               /* negate */
        WriteX (op[0], xop);                            /* write result */
        break;

    case 017:                                           /* DDINT 105217 (OP_AAX) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        if (intrq) {                                    /* interrupt pending? */
            PC = err_PC;                                /* restart instruction */
            break;
            }
        x_trun (&xop, AS_XPN (op [2]));                 /* truncate operand */
        WriteX (op[1], xop);                            /* write result */
        break;

#endif

/* FFP module 2 */

    case 020:                                           /* .XFER 105220 (OP_N) */
        if (UNIT_CPU_TYPE == UNIT_TYPE_2100)
            PC = (PC + 1) & VAMASK;                     /* 2100 .XFER returns to P+2 */
    XFER:                                               /* enter here from .DFER */
        sc = 3;                                         /* set count for 3-wd xfer */
        goto CFER;                                      /* do transfer */

    case 021:                                           /* .GOTO 105221 (OP_AK) */
        if ((op[1] == 0) || (op[1] & SIGN))             /* index < 1? */
            op[1] = 1;                                  /* reset min */
        sa = PC + op[1] - 1;                            /* point to jump target */
        if (sa >= op[0])                                /* must be <= last target */
            sa = op[0] - 1;
        da = ReadW (sa);                                /* get jump target */
        if (reason = resolve (da, &MA, intrq)) {        /* resolve indirects */
            PC = err_PC;                                /* irq restarts instruction */
            break;
            }
        mp_dms_jmp (MA);                                /* validate jump addr */
        PCQ_ENTRY;                                      /* record last PC */
        PC = MA;                                        /* jump */
        BR = op[0];                                     /* (for 2100 FFP compat) */
        break;

    case 022:                                           /* ..MAP 105222 (OP_KKKK) */
        op[1] = op[1] - 1;                              /* decrement 1st subscr */
        if ((AR & 1) == 0)                              /* 2-dim access? */
            op[1] = op[1] + (op[2] - 1) * op[3];        /* compute element offset */
        else {                                          /* 3-dim access */
            if (reason = get_ops (OP_KK, op2, intrq)) { /* get 1st, 2nd ranges */
                PC = err_PC;                            /* irq restarts instruction */
                break;
                }
            op[1] = op[1] + ((op[3] - 1) * op2[1] + op[2] - 1) * op2[0];  /* offset */
            }
        AR = (op[0] + op[1] * BR) & DMASK;              /* return element address */
        break;

    case 023:                                           /* .ENTR 105223 (OP_A) */
        MA = PC - 3;                                    /* get addr of entry point */
    ENTR:                                               /* enter here from .ENTP */
        da = op[0];                                     /* get addr of 1st formal */
        dc = MA - da;                                   /* get count of formals */
        sa = ReadW (MA);                                /* get addr of return point */
        ra = ReadW (sa++);                              /* get rtn, ptr to 1st actual */
        WriteW (MA, ra);                                /* stuff rtn into caller's ent */
        sc = ra - sa;                                   /* get count of actuals */
        if (sc > dc) sc = dc;                           /* use min (actuals, formals) */
        for (j = 0; j < sc; j++) {
            MA = ReadW (sa++);                          /* get addr of actual */
            if (reason = resolve (MA, &MA, intrq)) {    /* resolve indirect */
                PC = err_PC;                            /* irq restarts instruction */
                break;
                }
            WriteW (da++, MA);                          /* put addr into formal */
            }
        AR = ra;                                        /* return address */
        BR = da;                                        /* addr of 1st unused formal */
        break;

    case 024:                                           /* .ENTP 105224 (OP_A) */
        MA = PC - 5;                                    /* get addr of entry point */
        goto ENTR;

    case 025:                                           /* .PWR2 105225 (OP_K) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        f_pwr2 (SEXT (op[0]));                          /* calc result into A/B */
        break;

    case 026:                                           /* .FLUN 105226 (OP_N) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        f_unpack ();                                    /* unpack into A/B */
        break;

    case 027:                                           /* $SETP 105227 (OP_K) */
        j = sa = AR;                                    /* save initial value */
        sb = BR;                                        /* save initial address */
        AR = 0;                                         /* AR will return = 0 */
        BR = BR & VAMASK;                               /* addr must be direct */
        do {
            WriteW (BR, j);                             /* write value to address */
            j = (j + 1) & DMASK;                        /* incr value */
            BR = (BR + 1) & VAMASK;                     /* incr address */
            op[0] = op[0] - 1;                          /* decr count */
            if (op[0] && intrq) {                       /* more and intr? */
                AR = sa;                                /* restore A */
                BR = sb;                                /* restore B */
                PC = err_PC;                            /* restart instruction */
                break;
                }
            }
        while (op[0] != 0);                             /* loop until count exhausted */
        break;

    case 030:                                           /* .PACK 105230 (OP_C) */
        if (UNIT_CPU_TYPE != UNIT_TYPE_21MX)            /* must be 21MX */
            return stop_inst;                           /* trap if not */
        O = f_pack (SEXT (op[0]));                      /* calc A/B and overflow */
        break;

    case 031:                                           /* .CFER 105231 (OP_AA) */
        if (UNIT_CPU_MODEL != UNIT_21MX_E)              /* must be 21MX E-series */
            return stop_inst;                           /* trap if not */
        BR = op[0];                                     /* get destination address */
        AR = op[1];                                     /* get source address */
        sc = 4;                                         /* set for 4-wd xfer */
    CFER:                                               /* enter here from .XFER */
        for (j = 0; j < sc; j++) {                      /* xfer loop */
            WriteW (BR, ReadW (AR));                    /* transfer word */
            AR = (AR + 1) & VAMASK;                     /* bump source addr */
            BR = (BR + 1) & VAMASK;                     /* bump destination addr */
            }
        E = 0;                                          /* routine clears E */
        if (UNIT_CPU_TYPE == UNIT_TYPE_2100) {          /* 2100 (and .DFER/.XFER)? */
            AR = (AR + 1) & VAMASK;                     /* 2100 FFP returns X+4, Y+4 */
            BR = (BR + 1) & VAMASK;
            }
        break;

    default:                                            /* others undefined */
        reason = stop_inst;
        }

return reason;
}

/* 2000 I/O Processor

   The IOP accelerates certain operations of the HP 2000 Time-Share BASIC system
   I/O processor.  Most 2000 systems were delivered with 2100 CPUs, although IOP
   microcode was developed for the 21MX-M and 21MX-E.  As the I/O processors
   were specific to the 2000 system, general compatibility with other CPU
   microcode options was unnecessary, and indeed no other options were possible
   for the 2100.

   Option implementation by CPU was as follows:

      2116    2100   21MX-M  21MX-E  21MX-F
     ------  ------  ------  ------  ------
      N/A    13206A  13207A  22702A   N/A

   The routines are mapped to instruction codes as follows:

     Instr.     2100      21MX-M/E   Description
     ------  ----------  ----------  --------------------------------------------
     SAI     105060-117  101400-037  Store A indexed by B (+/- offset in IR<4:0>)
     LAI     105020-057  105400-037  Load A indexed by B (+/- offset in IR<4:0>)
     CRC     105150      105460      Generate CRC
     REST    105340      105461      Restore registers from stack
     READF   105220      105462      Read F register (stack pointer)
     INS       --        105463      Initialize F register (stack pointer)
     ENQ     105240      105464      Enqueue
     PENQ    105257      105465      Priority enqueue
     DEQ     105260      105466      Dequeue
     TRSLT   105160      105467      Translate character
     ILIST   105000      105470      Indirect address list (similar to $SETP)
     PRFEI   105222      105471      Power fail exit with I/O
     PRFEX   105223      105472      Power fail exit
     PRFIO   105221      105473      Power fail I/O
     SAVE    105362      105474      Save registers to stack

     MBYTE   105120      105765      Move bytes (MBT)
     MWORD   105200      105777      Move words (MVW)
     SBYTE   105300      105764      Store byte (SBT)
     LBYTE   105320      105763      Load byte (LBT)

   The INS instruction was not required in the 2100 implementation because the
   stack pointer was actually the memory protect fence register and so could be
   loaded directly with an OTA/B 05.  Also, the 21MX implementation did not
   offer the MBYTE, MWORD, SBYTE, and LBYTE instructions because the equivalent
   instructions from the standard Extended Instruction Group were used instead.

   Additional reference:
   - HP 2000 Computer System Sources and Listings Documentation
        (22687-90020, undated), section 3, pages 2-74 through 2-91.
*/

static const OP_PAT op_iop[16] = {
  OP_V,    OP_N,    OP_N,    OP_N,                      /* CRC    RESTR  READF  INS   */
  OP_N,    OP_N,    OP_N,    OP_V,                      /* ENQ    PENQ   DEQ    TRSLT */
  OP_AC,   OP_CVA,  OP_A,    OP_CV,                     /* ILIST  PRFEI  PRFEX  PRFIO */
  OP_N,    OP_N,    OP_N,    OP_N                       /* SAVE    ---    ---    ---  */
  };

static t_stat cpu_iop (uint32 IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry;
uint32 hp, tp, i, t, wc, MA;

if ((cpu_unit.flags & UNIT_IOP) == 0)                   /* IOP option installed? */
    return stop_inst;

entry = IR & 077;                                       /* mask to entry point */

if (entry <= 037) {                                     /* LAI/SAI 10x400-437 */
    MA = ((entry - 020) + BR) & VAMASK;                 /* +/- offset */
    if (IR & I_AB) AR = ReadW (MA);                     /* AB = 1 -> LAI */
    else WriteW (MA, AR);                               /* AB = 0 -> SAI */
    return reason;
    }
else if (entry <= 057)                                  /* IR = 10x440-457? */
    return stop_inst;                                   /* not part of IOP */

entry = entry - 060;                                    /* offset 10x460-477 */

if (op_iop[entry] != OP_N) {
    if (reason = get_ops (op_iop[entry], op, intrq))    /* get instruction operands */
        return reason;
    }

switch (entry) {                                        /* decode IR<5:0> */

    case 000:                                           /* CRC 105460 (OP_V) */
        t = ReadW (op[0]) ^ (AR & 0377);                /* xor prev CRC and char */
        for (i = 0; i < 8; i++) {                       /* apply polynomial */
            t = (t >> 1) | ((t & 1) << 15);             /* rotate right */
            if (t & SIGN) t = t ^ 020001;               /* old t<0>? xor */
            }
        WriteW (op[0], t);                              /* rewrite CRC */
        break;

    case 001:                                           /* RESTR 105461 (OP_N) */
        iop_sp = (iop_sp - 1) & VAMASK;                 /* decr stack ptr */
        t = ReadW (iop_sp);                             /* get E and O */
        O = ((t >> 1) ^ 1) & 1;                         /* restore O */
        E = t & 1;                                      /* restore E */
        iop_sp = (iop_sp - 1) & VAMASK;                 /* decr sp */
        BR = ReadW (iop_sp);                            /* restore B */
        iop_sp = (iop_sp - 1) & VAMASK;                 /* decr sp */
        AR = ReadW (iop_sp);                            /* restore A */
        if (UNIT_CPU_MODEL == UNIT_2100)
            mp_fence = iop_sp;                          /* 2100 keeps sp in MP FR */
        break;

    case 002:                                           /* READF 105462 (OP_N) */
        AR = iop_sp;                                    /* copy stk ptr */
        break;

    case 003:                                           /* INS 105463 (OP_N) */
        iop_sp = AR;                                    /* init stk ptr */
        break;

    case 004:                                           /* ENQ 105464 (OP_N) */
        hp = ReadW (AR & VAMASK);                       /* addr of head */
        tp = ReadW ((AR + 1) & VAMASK);                 /* addr of tail */
        WriteW ((BR - 1) & VAMASK, 0);                  /* entry link */
        WriteW ((tp - 1) & VAMASK, BR);                 /* tail link */
        WriteW ((AR + 1) & VAMASK, BR);                 /* queue tail */
        if (hp != 0) PC = (PC + 1) & VAMASK;            /* q not empty? skip */
        break;

    case 005:                                           /* PENQ 105465 (OP_N) */
        hp = ReadW (AR & VAMASK);                       /* addr of head */
        WriteW ((BR - 1) & VAMASK, hp);                 /* becomes entry link */
        WriteW (AR & VAMASK, BR);                       /* queue head */
        if (hp == 0)                                    /* q empty? */
            WriteW ((AR + 1) & VAMASK, BR);             /* queue tail */
        else PC = (PC + 1) & VAMASK;                    /* skip */
        break;

    case 006:                                           /* DEQ 105466 (OP_N) */
        BR = ReadW (AR & VAMASK);                       /* addr of head */
        if (BR) {                                       /* queue not empty? */
            hp = ReadW ((BR - 1) & VAMASK);             /* read hd entry link */
            WriteW (AR & VAMASK, hp);                   /* becomes queue head */
            if (hp == 0)                                /* q now empty? */
                WriteW ((AR + 1) & VAMASK, (AR + 1) & DMASK);
            PC = (PC + 1) & VAMASK;                     /* skip */
            }
        break;

    case 007:                                           /* TRSLT 105467 (OP_V) */
        wc = ReadW (op[0]);                             /* get count */
        if (wc & SIGN) break;                           /* cnt < 0? */
        while (wc != 0) {                               /* loop */
            MA = (AR + AR + ReadB (BR)) & VAMASK;
            t = ReadB (MA);                             /* xlate */
            WriteB (BR, t);                             /* store char */
            BR = (BR + 1) & DMASK;                      /* incr ptr */
            wc = (wc - 1) & DMASK;                      /* decr cnt */
            if (wc && intrq) {                          /* more and intr? */
                WriteW (op[0], wc);                     /* save count */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 010:                                           /* ILIST 105470 (OP_AC) */
        do {                                            /* for count */
            WriteW (op[0], AR);                         /* write AR to mem */
            AR = (AR + 1) & DMASK;                      /* incr AR */
            op[0] = (op[0] + 1) & VAMASK;               /* incr MA */
            op[1] = (op[1] - 1) & DMASK;                /* decr count */
            }
        while (op[1] != 0);
        break;

    case 011:                                           /* PRFEI 105471 (OP_CVA) */
        WriteW (op[1], 1);                              /* set flag */
        reason = iogrp (op[0], 0);                      /* execute I/O instr */
        op[0] = op[2];                                  /* set rtn and fall through */

    case 012:                                           /* PRFEX 105472 (OP_A) */
        PCQ_ENTRY;
        PC = ReadW (op[0]) & VAMASK;                    /* jump indirect */
        WriteW (op[0], 0);                              /* clear exit */
        break;

    case 013:                                           /* PRFIO 105473 (OP_CV) */
        WriteW (op[1], 1);                              /* set flag */
        reason = iogrp (op[0], 0);                      /* execute instr */
        break;

    case 014:                                           /* SAVE 105474 (OP_N) */
        WriteW (iop_sp, AR);                            /* save A */
        iop_sp = (iop_sp + 1) & VAMASK;                 /* incr stack ptr */
        WriteW (iop_sp, BR);                            /* save B */
        iop_sp = (iop_sp + 1) & VAMASK;                 /* incr stack ptr */
        t = ((O ^ 1) << 1) | E;                         /* merge E and O */
        WriteW (iop_sp, t);                             /* save E and O */
        iop_sp = (iop_sp + 1) & VAMASK;                 /* incr stack ptr */
        if (UNIT_CPU_TYPE == UNIT_TYPE_2100)
            mp_fence = iop_sp;                          /* 2100 keeps sp in MP FR */
        break;

    default:                                            /* instruction undefined */
        return stop_inst;
        }

return reason;
}

/* Dynamic Mapping System

   The 21MX Dynamic Mapping System (DMS) consisted of the 12731A Memory
   Expansion Module (MEM) card and 38 instructions to expand the basic 32K
   logical address space to a 1024K physical space.  The MEM provided four maps
   of 32 mapping registers each: a system map, a user map, and two DCPC maps.
   DMS worked in conjunction with memory protect to provide a "protected mode"
   in which memory read and write violations could be trapped, and that
   inhibited "privileged" instruction execution that attempted to alter the
   memory mapping.

   Option implementation by CPU was as follows:

      2116    2100   21MX-M  21MX-E  21MX-F
     ------  ------  ------  ------  ------
      N/A     N/A    12976B  13307B   std

   The instruction codes are mapped to routines as follows:

     Instr.  21MX-M  21MX-E/F   Instr.   21MX-M  21MX-E/F
     ------  ------  --------   ------   ------  --------
     10x700  [xmm]    [xmm]     10x720    XMM      XMM
     10x701  [nop]    [test]    10x721    XMS      XMS
     10x702   MBI      MBI      10x722    XM*      XM*
     10x703   MBF      MBF      10x723   [nop]    [nop]
     10x704   MBW      MBW      10x724    XL*      XL*
     10x705   MWI      MWI      10x725    XS*      XS*
     10x706   MWF      MWF      10x726    XC*      XC*
     10x707   MWW      MWW      10x727    LF*      LF*
     10x710   SY*      SY*      10x730    RS*      RS*

     10x711   US*      US*      10x731    RV*      RV*
     10x712   PA*      PA*      10x732    DJP      DJP
     10x713   PB*      PB*      10x733    DJS      DJS
     10x714   SSM      SSM      10x734    SJP      SJP
     10x715   JRS      JRS      10x735    SJS      SJS
     10x716  [nop]    [nop]     10x736    UJP      UJP
     10x717  [nop]    [nop]     10x737    UJS      UJS

   Instructions that use IR bit 9 to select the A or B register are designated
   with a * above (e.g., 101710 is SYA, and 105710 is SYB).  For those that do
   not use this feature, either the 101xxx or 105xxx code will execute the
   corresponding instruction, although the 105xxx form is the documented
   instruction code.

   Notes:

     1. Instruction code 10x700 will execute the XMM instruction, although
        10x720 is the documented instruction value.

     2. The DMS privilege violation rules are:
        - load map and CTL5 set (XMM, XMS, XM*, SY*, US*, PA*, PB*)
        - load state or fence and UMAP set (JRS, DJP, DJS, SJP, SJS, UJP, UJS, LF*)

     3. The 21MX manual is incorrect in stating that M*I, M*W, XS* are
        privileged.
*/

static const OP_PAT op_dms[32] = {
  OP_N,    OP_N,    OP_N,    OP_N,                      /* xmm    test   MBI    MBF   */
  OP_N,    OP_N,    OP_N,    OP_N,                      /* MBW    MWI    MWF    MWW   */
  OP_N,    OP_N,    OP_N,    OP_N,                      /* SYA/B  USA/B  PAA/B  PBA/B */
  OP_A,    OP_KA,   OP_N,    OP_N,                      /* SSM    JRS    nop    nop   */
  OP_N,    OP_N,    OP_N,    OP_N,                      /* XMM    XMS    XMA/B  nop   */
  OP_A,    OP_A,    OP_A,    OP_N,                      /* XLA/B  XSA/B  XCA/B  LFA/B */
  OP_N,    OP_N,    OP_A,    OP_A,                      /* RSA/B  RVA/B  DJP    DJS   */
  OP_A,    OP_A,    OP_A,    OP_A                       /* SJP    SJS    UJP    UJS   */
  };

static t_stat cpu_dms (uint32 IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry, absel;
uint32 i, t, mapi, mapj;

if ((cpu_unit.flags & UNIT_DMS) == 0)                   /* DMS option installed? */
    return stop_inst;

absel = (IR & I_AB)? 1: 0;                              /* get A/B select */
entry = IR & 037;                                       /* mask to entry point */

if (op_dms[entry] != OP_N) {
    if (reason = get_ops (op_dms[entry], op, intrq))    /* get instruction operands */
        return reason;
    }

switch (entry) {                                        /* decode IR<3:0> */

/* DMS module 1 */

    case 000:                                           /* [undefined] 105700 (OP_N) */
        goto XMM;                                       /* decodes as XMM */

    case 001:                                           /* [self test] 105701 (OP_N) */
        ABREG[absel] = ABREG[absel] ^ DMASK;            /* CMA or CMB */
        break;

    case 002:                                           /* MBI 105702 (OP_N) */
        AR = AR & ~1;                                   /* force A, B even */
        BR = BR & ~1;
        while (XR != 0) {                               /* loop */
            t = ReadB (AR);                             /* read curr */
            WriteBA (BR, t);                            /* write alt */
            AR = (AR + 1) & DMASK;                      /* incr ptrs */
            BR = (BR + 1) & DMASK;
            XR = (XR - 1) & DMASK;
            if (XR && intrq && !(AR & 1)) {             /* more, int, even? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 003:                                           /* MBF 105703 (OP_N) */
        AR = AR & ~1;                                   /* force A, B even */
        BR = BR & ~1;
        while (XR != 0) {                               /* loop */
            t = ReadBA (AR);                            /* read alt */
            WriteB (BR, t);                             /* write curr */
            AR = (AR + 1) & DMASK;                      /* incr ptrs */
            BR = (BR + 1) & DMASK;
            XR = (XR - 1) & DMASK;
            if (XR && intrq && !(AR & 1)) {             /* more, int, even? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 004:                                           /* MBW 105704 (OP_N) */
        AR = AR & ~1;                                   /* force A, B even */
        BR = BR & ~1;
        while (XR != 0) {                               /* loop */
            t = ReadBA (AR);                            /* read alt */
            WriteBA (BR, t);                            /* write alt */
            AR = (AR + 1) & DMASK;                      /* incr ptrs */
            BR = (BR + 1) & DMASK;
            XR = (XR - 1) & DMASK;
            if (XR && intrq && !(AR & 1)) {             /* more, int, even? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 005:                                           /* MWI 105705 (OP_N) */
        while (XR != 0) {                               /* loop */
            t = ReadW (AR & VAMASK);                    /* read curr */
            WriteWA (BR & VAMASK, t);                   /* write alt */
            AR = (AR + 1) & DMASK;                      /* incr ptrs */
            BR = (BR + 1) & DMASK;
            XR = (XR - 1) & DMASK;
            if (XR && intrq) {                          /* more and intr? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 006:                                           /* MWF 105706 (OP_N) */
        while (XR != 0) {                               /* loop */
            t = ReadWA (AR & VAMASK);                   /* read alt */
            WriteW (BR & VAMASK, t);                    /* write curr */
            AR = (AR + 1) & DMASK;                      /* incr ptrs */
            BR = (BR + 1) & DMASK;
            XR = (XR - 1) & DMASK;
            if (XR && intrq) {                          /* more and intr? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 007:                                           /* MWW 105707 (OP_N) */
        while (XR != 0) {                               /* loop */
            t = ReadWA (AR & VAMASK);                   /* read alt */
            WriteWA (BR & VAMASK, t);                   /* write alt */
            AR = (AR + 1) & DMASK;                      /* incr ptrs */
            BR = (BR + 1) & DMASK;
            XR = (XR - 1) & DMASK;
            if (XR && intrq) {                          /* more and intr? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 010:                                           /* SYA, SYB 10x710 (OP_N) */
    case 011:                                           /* USA, USB 10x711 (OP_N) */
    case 012:                                           /* PAA, PAB 10x712 (OP_N) */
    case 013:                                           /* PBA, PBB 10x713 (OP_N) */
        mapi = (IR & 03) << VA_N_PAG;                   /* map base */
        if (ABREG[absel] & SIGN) {                      /* store? */
            for (i = 0; i < MAP_LNT; i++) {
                t = dms_rmap (mapi + i);                /* map to memory */
                WriteW ((ABREG[absel] + i) & VAMASK, t);
                }
            }
        else {                                          /* load */
            dms_viol (err_PC, MVI_PRV);                 /* priv if PRO */
            for (i = 0; i < MAP_LNT; i++) {
                t = ReadW ((ABREG[absel] + i) & VAMASK);
                dms_wmap (mapi + i, t);                 /* mem to map */
                }
            }
        ABREG[absel] = (ABREG[absel] + MAP_LNT) & DMASK;
        break;

    case 014:                                           /* SSM 105714 (OP_A) */
        WriteW (op[0], dms_upd_sr ());                  /* store stat */
        break;

    case 015:                                           /* JRS 105715 (OP_KA) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        dms_enb = 0;                                    /* assume off */
        dms_ump = SMAP;
        if (op[0] & 0100000) {                          /* set enable? */
            dms_enb = 1;
            if (op[0] & 0040000) dms_ump = UMAP;        /* set/clr usr */
            }
        mp_dms_jmp (op[1]);                             /* mpck jmp target */
        PCQ_ENTRY;                                      /* save old PC */
        PC = op[1];                                     /* jump */
        ion_defer = 1;                                  /* defer intr */
        break;

/* DMS module 2 */

    case 020:                                           /* XMM 105720 (OP_N) */
    XMM:
        if (XR == 0) break;                             /* nop? */
        while (XR != 0) {                               /* loop */
            if (XR & SIGN) {                            /* store? */
                t = dms_rmap (AR);                      /* map to mem */
                WriteW (BR & VAMASK, t);
                XR = (XR + 1) & DMASK;
                }
            else {                                      /* load */
                dms_viol (err_PC, MVI_PRV);             /* priv viol if prot */
                t = ReadW (BR & VAMASK);                /* mem to map */
                dms_wmap (AR, t);
                XR = (XR - 1) & DMASK;
                }
            AR = (AR + 1) & DMASK;
            BR = (BR + 1) & DMASK;
            if (intrq && ((XR & 017) == 017)) {         /* intr, grp of 16? */
                PC = err_PC;                            /* stop for now */
                break;
                }
            }
        break;

    case 021:                                           /* XMS 105721 (OP_N) */
        if ((XR & SIGN) || (XR == 0)) break;            /* nop? */
        dms_viol (err_PC, MVI_PRV);                     /* priv viol if prot */
        while (XR != 0) {
            dms_wmap (AR, BR);                          /* AR to map */
            XR = (XR - 1) & DMASK;
            AR = (AR + 1) & DMASK;
            BR = (BR + 1) & DMASK;
            if (intrq && ((XR & 017) == 017)) {         /* intr, grp of 16? */
                PC = err_PC;
                break;
                }
            }
        break;

    case 022:                                           /* XMA, XMB 10x722 (OP_N) */
        dms_viol (err_PC, MVI_PRV);                     /* priv viol if prot */
        if (ABREG[absel] & 0100000) mapi = UMAP;
        else mapi = SMAP;
        if (ABREG[absel] & 0000001) mapj = PBMAP;
        else mapj = PAMAP;
        for (i = 0; i < MAP_LNT; i++) {
            t = dms_rmap (mapi + i);                    /* read map */
            dms_wmap (mapj + i, t);                     /* write map */
            }
        break;

    case 024:                                           /* XLA, XLB 10x724 (OP_A) */
        ABREG[absel] = ReadWA (op[0]);                  /* load alt */
        break;

    case 025:                                           /* XSA, XSB 10x725 (OP_A) */
        WriteWA (op[0], ABREG[absel]);                  /* store alt */
        break;

    case 026:                                           /* XCA, XCB 10x726 (OP_A) */
        if (ABREG[absel] != ReadWA (op[0]))             /* compare alt */
            PC = (PC + 1) & VAMASK;
        break;

    case 027:                                           /* LFA, LFB 10x727 (OP_N) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        dms_sr = (dms_sr & ~(MST_FLT | MST_FENCE)) |
            (ABREG[absel] & (MST_FLT | MST_FENCE));
        break;

    case 030:                                           /* RSA, RSB 10x730 (OP_N) */
        ABREG[absel] = dms_upd_sr ();                   /* save stat */
        break;

    case 031:                                           /* RVA, RVB 10x731 (OP_N) */
        ABREG[absel] = dms_vr;                          /* save viol */
        break;

    case 032:                                           /* DJP 105732 (OP_A) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        mp_dms_jmp (op[0]);                             /* validate jump addr */
        PCQ_ENTRY;                                      /* save curr PC */
        PC = op[0];                                     /* new PC */
        dms_enb = 0;                                    /* disable map */
        dms_ump = SMAP;
        ion_defer = 1;
        break;

    case 033:                                           /* DJS 105733 (OP_A) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        WriteW (op[0], PC);                             /* store ret addr */
        PCQ_ENTRY;                                      /* save curr PC */
        PC = (op[0] + 1) & VAMASK;                      /* new PC */
        dms_enb = 0;                                    /* disable map */
        dms_ump = SMAP;
        ion_defer = 1;                                  /* defer intr */
        break;

    case 034:                                           /* SJP 105734 (OP_A) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        mp_dms_jmp (op[0]);                             /* validate jump addr */
        PCQ_ENTRY;                                      /* save curr PC */
        PC = op[0];                                     /* jump */
        dms_enb = 1;                                    /* enable system */
        dms_ump = SMAP;
        ion_defer = 1;                                  /* defer intr */
        break;

    case 035:                                           /* SJS 105735 (OP_A) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        t = PC;                                         /* save retn addr */
        PCQ_ENTRY;                                      /* save curr PC */
        PC = (op[0] + 1) & VAMASK;                      /* new PC */
        dms_enb = 1;                                    /* enable system */
        dms_ump = SMAP;
        WriteW (op[0], t);                              /* store ret addr */
        ion_defer = 1;                                  /* defer intr */
        break;

    case 036:                                           /* UJP 105736 (OP_A) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        mp_dms_jmp (op[0]);                             /* validate jump addr */
        PCQ_ENTRY;                                      /* save curr PC */
        PC = op[0];                                     /* jump */
        dms_enb = 1;                                    /* enable user */
        dms_ump = UMAP;
        ion_defer = 1;                                  /* defer intr */
        break;

    case 037:                                           /* UJS 105737 (OP_A) */
        if (dms_ump) dms_viol (err_PC, MVI_PRV);        /* priv viol if prot */
        t = PC;                                         /* save retn addr */
        PCQ_ENTRY;                                      /* save curr PC */
        PC = (op[0] + 1) & VAMASK;                      /* new PC */
        dms_enb = 1;                                    /* enable user */
        dms_ump = UMAP;
        WriteW (op[0], t);                              /* store ret addr */
        ion_defer = 1;                                  /* defer intr */
        break;

    default:                                            /* others NOP */
        break;
        }

return reason;
}

/* Extended Instruction Group

   The Extended Instruction Group (EIG) adds 32 index and 10 bit/byte/word
   manipulation instructions to the 21MX base set.  These instructions
   use the new X and Y index registers that were added to the 21MX.

   Option implementation by CPU was as follows:

      2116    2100   21MX-M  21MX-E  21MX-F
     ------  ------  ------  ------  ------
      N/A     N/A     std     std     std

   The instruction codes are mapped to routines as follows:

     Instr.  21MX-M/E/F   Instr.   21MX-M/E/F
     ------  ----------   ------   ----------
     10x740     S*X       10x760      ISX
     10x741     C*X       10x761      DSX
     10x742     L*X       10x762      JLY
     10x743     STX       10x763      LBT
     10x744     CX*       10x764      SBT
     10x745     LDX       10x765      MBT
     10x746     ADX       10x766      CBT
     10x747     X*X       10x767      SFB

     10x750     S*Y       10x770      ISY
     10x751     C*Y       10x771      DSY
     10x752     L*Y       10x772      JPY
     10x753     STY       10x773      SBS
     10x754     CY*       10x774      CBS
     10x755     LDY       10x775      TBS
     10x756     ADY       10x776      CMW
     10x757     X*Y       10x777      MVW

   Instructions that use IR bit 9 to select the A or B register are designated
   with a * above (e.g., 101740 is SAX, and 105740 is SBX).  For those that do
   not use this feature, either the 101xxx or 105xxx code will execute the
   corresponding instruction, although the 105xxx form is the documented
   instruction code.

   Notes:

     1. The LBT, SBT, MBT, and MVW instructions are used as part of the 2100 IOP
        implementation.  When so called, the MBT and MVW instructions have the
        additional restriction that the count must be positive.
*/

static const OP_PAT op_eig[32] = {
  OP_A,    OP_N,    OP_A,    OP_A,                      /* S*X    C*X    L*X    STX   */
  OP_N,    OP_K,    OP_K,    OP_N,                      /* CX*    LDX    ADX    X*X   */
  OP_A,    OP_N,    OP_A,    OP_A,                      /* S*Y    C*Y    L*Y    STY   */
  OP_N,    OP_K,    OP_K,    OP_N,                      /* CY*    LDY    ADY    X*Y   */
  OP_N,    OP_N,    OP_A,    OP_N,                      /* ISX    DSX    JLY    LBT   */
  OP_N,    OP_KV,   OP_KV,   OP_N,                      /* SBT    MBT    CBT    SFB   */
  OP_N,    OP_N,    OP_C,    OP_KA,                     /* ISY    DSY    JPY    SBS   */
  OP_KA,   OP_KK,   OP_KV,   OP_KV                      /* CBS    TBS    CMW    MVW   */
  };

static t_stat cpu_eig (uint32 IR, uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry, absel;
uint32 t, v1, v2, wc;
int32 sop1, sop2;

absel = (IR & I_AB)? 1: 0;                              /* get A/B select */
entry = IR & 037;                                       /* mask to entry point */

if (op_eig[entry] != OP_N) {
    if (reason = get_ops (op_eig[entry], op, intrq))    /* get instruction operands */
        return reason;
    }

switch (entry) {                                        /* decode IR<4:0> */

/* EIG module 1 */

    case 000:                                           /* SAX, SBX 10x740 (OP_A) */
        op[0] = (op[0] + XR) & VAMASK;                  /* indexed addr */
        WriteW (op[0], ABREG[absel]);                   /* store */
        break;

    case 001:                                           /* CAX, CBX 10x741 (OP_N) */
        XR = ABREG[absel];                              /* copy to XR */
        break;

    case 002:                                           /* LAX, LBX 10x742 (OP_A) */
        op[0] = (op[0] + XR) & VAMASK;                  /* indexed addr */
        ABREG[absel] = ReadW (op[0]);                   /* load */
        break;

    case 003:                                           /* STX 105743 (OP_A) */
        WriteW (op[0], XR);                             /* store XR */
        break;

    case 004:                                           /* CXA, CXB 10x744 (OP_N) */
        ABREG[absel] = XR;                              /* copy from XR */
        break;

    case 005:                                           /* LDX 105745 (OP_K)*/
        XR = op[0];                                     /* load XR */
        break;

    case 006:                                           /* ADX 105746 (OP_K) */
        t = XR + op[0];                                 /* add to XR */
        if (t > DMASK) E = 1;                           /* set E, O */
        if (((~XR ^ op[0]) & (XR ^ t)) & SIGN) O = 1;
        XR = t & DMASK;
        break;

    case 007:                                           /* XAX, XBX 10x747 (OP_N) */
        t = XR;                                         /* exchange XR */
        XR = ABREG[absel];
        ABREG[absel] = t;
        break;

    case 010:                                           /* SAY, SBY 10x750 (OP_A) */
        op[0] = (op[0] + YR) & VAMASK;                  /* indexed addr */
        WriteW (op[0], ABREG[absel]);                   /* store */
        break;

    case 011:                                           /* CAY, CBY 10x751 (OP_N) */
        YR = ABREG[absel];                              /* copy to YR */
        break;

    case 012:                                           /* LAY, LBY 10x752 (OP_A) */
        op[0] = (op[0] + YR) & VAMASK;                  /* indexed addr */
        ABREG[absel] = ReadW (op[0]);                   /* load */
        break;

    case 013:                                           /* STY 105753 (OP_A) */
        WriteW (op[0], YR);                             /* store YR */
        break;

    case 014:                                           /* CYA, CYB 10x754 (OP_N) */
        ABREG[absel] = YR;                              /* copy from YR */
        break;

    case 015:                                           /* LDY 105755 (OP_K) */
        YR = op[0];                                     /* load YR */
        break;

    case 016:                                           /* ADY 105756 (OP_K) */
        t = YR + op[0];                                 /* add to YR */
        if (t > DMASK) E = 1;                           /* set E, O */
        if (((~YR ^ op[0]) & (YR ^ t)) & SIGN) O = 1;
        YR = t & DMASK;
        break;

    case 017:                                           /* XAY, XBY 10x757 (OP_N) */
        t = YR;                                         /* exchange YR */
        YR = ABREG[absel];
        ABREG[absel] = t;
        break;

/* EIG module 2 */

    case 020:                                           /* ISX 105760 (OP_N) */
        XR = (XR + 1) & DMASK;                          /* incr XR */
        if (XR == 0) PC = (PC + 1) & VAMASK;            /* skip if zero */
        break;

    case 021:                                           /* DSX 105761 (OP_N) */
        XR = (XR - 1) & DMASK;                          /* decr XR */
        if (XR == 0) PC = (PC + 1) & VAMASK;            /* skip if zero */
        break;

    case 022:                                           /* JLY 105762 (OP_A) */
        mp_dms_jmp (op[0]);                             /* validate jump addr */
        PCQ_ENTRY;
        YR = PC;                                        /* ret addr to YR */
        PC = op[0];                                     /* jump */
        break;

    case 023:                                           /* LBT 105763 (OP_N) */
        AR = ReadB (BR);                                /* load byte */
        BR = (BR + 1) & DMASK;                          /* incr ptr */
        break;

    case 024:                                           /* SBT 105764 (OP_N) */
        WriteB (BR, AR);                                /* store byte */
        BR = (BR + 1) & DMASK;                          /* incr ptr */
        break;

    case 025:                                           /* MBT 105765 (OP_KV) */
        wc = ReadW (op[1]);                             /* get continuation count */
        if (wc == 0) wc = op[0];                        /* none? get initiation count */
        if ((wc & SIGN) && (UNIT_CPU_TYPE == UNIT_TYPE_2100))
            break;                                      /* < 0 is NOP for 2100 IOP */
        while (wc != 0) {                               /* while count */
            WriteW (op[1], wc);                         /* for MP abort */
            t = ReadB (AR);                             /* move byte */
            WriteB (BR, t);
            AR = (AR + 1) & DMASK;                      /* incr src */
            BR = (BR + 1) & DMASK;                      /* incr dst */
            wc = (wc - 1) & DMASK;                      /* decr cnt */
            if (intrq && wc) {                          /* intr, more to do? */
                PC = err_PC;                            /* back up PC */
                break;
                }
            }
        WriteW (op[1], wc);                             /* clean up inline */
        break;

    case 026:                                           /* CBT 105766 (OP_KV) */
        wc = ReadW (op[1]);                             /* get continuation count */
        if (wc == 0) wc = op[0];                        /* none? get initiation count */
        while (wc != 0) {                               /* while count */
            WriteW (op[1], wc);                         /* for MP abort */
            v1 = ReadB (AR);                            /* get src1 */
            v2 = ReadB (BR);                            /* get src2 */
            if (v1 != v2) {                             /* compare */
                PC = (PC + 1 + (v1 > v2)) & VAMASK;
                BR = (BR + wc) & DMASK;                 /* update BR */
                wc = 0;                                 /* clr interim */
                break;
                }
            AR = (AR + 1) & DMASK;                      /* incr src1 */
            BR = (BR + 1) & DMASK;                      /* incr src2 */
            wc = (wc - 1) & DMASK;                      /* decr cnt */
            if (intrq && wc) {                          /* intr, more to do? */
                PC = err_PC;                            /* back up PC */
                break;
                }
            }
        WriteW (op[1], wc);                             /* clean up inline */
        break;

    case 027:                                           /* SFB 105767 (OP_N) */
        v1 = AR & 0377;                                 /* test byte */
        v2 = (AR >> 8) & 0377;                          /* term byte */
        for (;;) {                                      /* scan */
            t = ReadB (BR);                             /* read byte */
            if (t == v1) break;                         /* test match? */
            BR = (BR + 1) & DMASK;
            if (t == v2) {                              /* term match? */
                PC = (PC + 1) & VAMASK;
                break;
				}
            if (intrq) {                                /* int pending? */
                PC = err_PC;                            /* back up PC */
                break;
                }
            }
        break;

    case 030:                                           /* ISY 105770 (OP_N) */
        YR = (YR + 1) & DMASK;                          /* incr YR */
        if (YR == 0) PC = (PC + 1) & VAMASK;            /* skip if zero */
        break;

    case 031:                                           /* DSY 105771 (OP_N) */
        YR = (YR - 1) & DMASK;                          /* decr YR */
        if (YR == 0) PC = (PC + 1) & VAMASK;            /* skip if zero */
        break;

    case 032:                                           /* JPY 105772 (OP_C) */
        op[0] = (op[0] + YR) & VAMASK;                  /* index, no indir */
        mp_dms_jmp (op[0]);                             /* validate jump addr */
        PCQ_ENTRY;
        PC = op[0];                                     /* jump */
        break;

    case 033:                                           /* SBS 105773 (OP_KA) */
        WriteW (op[1], ReadW (op[1]) | op[0]);          /* set bits */
        break;

    case 034:                                           /* CBS 105774 (OP_KA) */
        WriteW (op[1], ReadW (op[1]) & ~op[0]);         /* clear bits */
        break;

    case 035:                                           /* TBS 105775 (OP_KK) */
        if ((op[1] & op[0]) != op[0])                   /* test bits */
            PC = (PC + 1) & VAMASK;
        break;

    case 036:                                           /* CMW 105776 (OP_KV) */
        wc = ReadW (op[1]);                             /* get continuation count */
        if (wc == 0) wc = op[0];                        /* none? get initiation count */
        while (wc != 0) {                               /* while count */
            WriteW (op[1], wc);                         /* for abort */
            v1 = ReadW (AR & VAMASK);                   /* first op */
            v2 = ReadW (BR & VAMASK);                   /* second op */
            sop1 = (int32) SEXT (v1);                   /* signed */
            sop2 = (int32) SEXT (v2);
            if (sop1 != sop2) {                         /* compare */
                PC = (PC + 1 + (sop1 > sop2)) & VAMASK;
                BR = (BR + wc) & DMASK;                 /* update BR */
                wc = 0;                                 /* clr interim */
                break;
                }
            AR = (AR + 1) & DMASK;                      /* incr src1 */
            BR = (BR + 1) & DMASK;                      /* incr src2 */
            wc = (wc - 1) & DMASK;                      /* decr cnt */
            if (intrq && wc) {                          /* intr, more to do? */
                PC = err_PC;                            /* back up PC */
                break;
                }
            }
        WriteW (op[1], wc);                             /* clean up inline */
        break;

    case 037:                                           /* MVW 105777 (OP_KV) */
        wc = ReadW (op[1]);                             /* get continuation count */
        if (wc == 0) wc = op[0];                        /* none? get initiation count */
        if ((wc & SIGN) && (UNIT_CPU_TYPE == UNIT_TYPE_2100))
            break;                                      /* < 0 is NOP for 2100 IOP */
        while (wc != 0) {                               /* while count */
            WriteW (op[1], wc);                         /* for abort */
            t = ReadW (AR & VAMASK);                    /* move word */
            WriteW (BR & VAMASK, t);
            AR = (AR + 1) & DMASK;                      /* incr src */
            BR = (BR + 1) & DMASK;                      /* incr dst */
            wc = (wc - 1) & DMASK;                      /* decr cnt */
            if (intrq && wc) {                          /* intr, more to do? */
                PC = err_PC;                            /* back up PC */
                break;
                }
            }
        WriteW (op[1], wc);                             /* clean up inline */
        break;

    default:                                            /* all others NOP */
        break;
        }

return reason;
}

/* Get instruction operands

   Operands for a given instruction are specifed by an "operand pattern"
   consisting of flags indicating the types and storage methods.  The pattern
   directs how each operand is to be retrieved and whether the operand value or
   address is returned in the operand array.

   Eight operand encodings are defined:

      Code   Operand Description              Example    Return
     ------  -----------------------------  -----------  ------------
     OP_NUL  No operand present                 [inst]   None

     OP_CON  Inline constant                    [inst]   Value of C
                                             C  DEC 0

     OP_VAR  Inline variable                    [inst]   Address of V
                                             V  BSS 1

     OP_ADR  Address                            [inst]   Address of A
                                                DEF A
                                                ...
                                             A  EQU *
        
     OP_ADK  Address of a 1-word constant       [instr]  Value of K
                                                DEF K
                                                ...
                                             K  DEC 0

     OP_ADF  Address of a 2-word constant       [inst]   Value of F
                                                DEF F
                                                ...
                                             F  DEC 0.0

     OP_ADX  Address of a 3-word constant       [inst]   Value of X
                                                DEF X
                                                ...
                                             X  DEX 0.0

     OP_ADT  Address of a 4-word constant       [inst]   Value of T
                                                DEF T
                                                ...
                                             T  DEY 0.0

   Address operands, i.e., those having a DEF to the operand, will be resolved
   to direct addresses.  If an interrupt is pending and more than three levels
   of indirection are used, the routine returns without completing operand
   retrieval (the instruction will be retried after interrupt servicing).
   Addresses are always resolved in the current DMS map.

   An operand pattern consists of one or more operand encodings, corresponding
   to the operands required by a given instruction.  Values are returned in
   sequence to the operand array.  Addresses and one-word values are returned in
   the lower half of the 32-bit array element.  Two-word values are packed into
   one array element, with the first word in the upper 16 bits.  Three- and
   four-word values are packed into two consecutive elements, with the last word
   of a three-word value in the upper 16 bits of of the second element.

   IMPLEMENTATION NOTE: OP_ADT is not currently supported.
*/

static t_stat get_ops (OP_PAT pattern, OPS op, uint32 irq)
{
t_stat reason = SCPE_OK;
OP_PAT flags;
uint32 i, MA;
XPN xop;

for (i = 0; i < OP_N_F; i++) {
    flags = pattern & OP_M_FLAGS;                       /* get operand pattern */

    if (flags >= OP_ADR)                                /* address operand? */
        if (reason = resolve (ReadW (PC), &MA, irq))    /* resolve indirects */
            return reason;

    switch (flags) {
        case OP_NUL:                                    /* null operand */
            return reason;                              /* no more, so quit */

        case OP_CON:                                    /* constant operand */
            *op++ = ReadW (PC);                         /* get value */
            break;

        case OP_VAR:                                    /* variable operand */
            *op++ = PC;                                 /* get pointer to variable */
            break;

        case OP_ADR:                                    /* address operand */
            *op++ = MA;                                 /* get address */
            break;

        case OP_ADK:                                    /* address of 1-word constant */
            *op++ = ReadW (MA);                         /* get value */
            break;

        case OP_ADF:                                    /* address of 2-word constant */
            *op++ = ReadF (MA);                         /* get value */
            break;

        case OP_ADX:                                    /* address of 3-word constant */
            xop = ReadX (MA);
            *op++ = xop.high;                           /* get first two words of value */
            *op++ = xop.low;                            /* get third word of value */
            break;

        case OP_ADT:                                    /* address of 4-word constant */

        default:
            return SCPE_IERR;                           /* not implemented */
        }

    PC = (PC + 1) & VAMASK;
    pattern = pattern >> OP_N_FLAGS;                    /* move next pattern into place */
    }
return reason;
}
